Skip to content

Commit 5393efb

Browse files
Kenovchuravy
authored andcommitted
Add support for unwinding during prologue/epilogue
1 parent 814d51a commit 5393efb

File tree

1 file changed

+156
-0
lines changed

1 file changed

+156
-0
lines changed

libunwind/src/CompactUnwinder.hpp

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,50 @@ int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame(
310310
uint32_t savedRegistersLocations =
311311
EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
312312

313+
// If we have not stored EBP yet
314+
if (functionStart == registers.getIP()) {
315+
uint64_t rsp = registers.getSP();
316+
// old esp is ebp less return address
317+
registers.setSP(rsp+8);
318+
// pop return address into eip
319+
registers.setIP(addressSpace.get64(rsp));
320+
321+
return UNW_STEP_SUCCESS;
322+
} else if (functionStart + 1 == registers.getIP()) {
323+
uint64_t rsp = registers.getSP();
324+
// old esp is ebp less return address
325+
registers.setSP(rsp + 16);
326+
// pop return address into eip
327+
registers.setIP(addressSpace.get64(rsp + 8));
328+
329+
return UNW_STEP_SUCCESS;
330+
}
331+
332+
// If we're about to return, we've already popped the base pointer
333+
uint8_t b = addressSpace.get8(registers.getIP());
334+
335+
// This is a hack to detect VZEROUPPER but in between popq rbp and ret
336+
// It's not pretty but it works
337+
if (b == 0xC5) {
338+
if ((b = addressSpace.get8(registers.getIP() + 1)) == 0xF8 &&
339+
(b = addressSpace.get8(registers.getIP() + 2)) == 0x77)
340+
b = addressSpace.get8(registers.getIP() + 3);
341+
else
342+
goto skip_ret;
343+
}
344+
345+
if (b == 0xC3 || b == 0xCB || b == 0xC2 || b == 0xCA) {
346+
uint64_t rbp = registers.getSP();
347+
// old esp is ebp less return address
348+
registers.setSP(rbp + 16);
349+
// pop return address into eip
350+
registers.setIP(addressSpace.get64(rbp + 8));
351+
352+
return UNW_STEP_SUCCESS;
353+
}
354+
355+
skip_ret:
356+
313357
uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset;
314358
for (int i = 0; i < 5; ++i) {
315359
switch (savedRegistersLocations & 0x7) {
@@ -430,6 +474,118 @@ int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless(
430474
}
431475
}
432476
}
477+
478+
// Note that the order of these registers is so that
479+
// registersSaved[0] is the one that will be pushed onto the stack last.
480+
// Thus, if we want to walk this from the top, we need to go in reverse.
481+
assert(regCount <= 6);
482+
483+
// check whether we are still in the prologue
484+
uint64_t curAddr = functionStart;
485+
if (regCount > 0) {
486+
for (int8_t i = (int8_t)(regCount) - 1; i >= 0; --i) {
487+
if (registers.getIP() == curAddr) {
488+
// None of the registers have been modified yet, so we don't need to reload them
489+
framelessUnwind(addressSpace, registers.getSP() + 8 * (regCount - (uint64_t)(i + 1)), registers);
490+
return UNW_STEP_SUCCESS;
491+
} else {
492+
assert(curAddr < registers.getIP());
493+
}
494+
495+
496+
// pushq %rbp and pushq %rbx is 1 byte. Everything else 2
497+
if ((UNWIND_X86_64_REG_RBP == registersSaved[i]) ||
498+
(UNWIND_X86_64_REG_RBX == registersSaved[i]))
499+
curAddr += 1;
500+
else
501+
curAddr += 2;
502+
}
503+
}
504+
if (registers.getIP() == curAddr) {
505+
// None of the registers have been modified yet, so we don't need to reload them
506+
framelessUnwind(addressSpace, registers.getSP() + 8*regCount, registers);
507+
return UNW_STEP_SUCCESS;
508+
} else {
509+
assert(curAddr < registers.getIP());
510+
}
511+
512+
513+
// And now for the epilogue
514+
{
515+
uint8_t i = 0;
516+
uint64_t p = registers.getIP();
517+
uint8_t b = 0;
518+
519+
while (true) {
520+
b = addressSpace.get8(p++);
521+
// This is a hack to detect VZEROUPPER but in between the popq's and ret
522+
// It's not pretty but it works
523+
if (b == 0xC5) {
524+
if ((b = addressSpace.get8(p++)) == 0xF8 && (b = addressSpace.get8(p++)) == 0x77)
525+
b = addressSpace.get8(p++);
526+
else
527+
break;
528+
}
529+
// popq %rbx popq %rbp
530+
if (b == 0x5B || b == 0x5D) {
531+
i++;
532+
} else if (b == 0x41) {
533+
b = addressSpace.get8(p++);
534+
if (b == 0x5C || b == 0x5D || b == 0x5E || b == 0x5F)
535+
i++;
536+
else
537+
break;
538+
} else if (b == 0xC3 || b == 0xCB || b == 0xC2 || b == 0xCA) {
539+
// i pop's haven't happened yet
540+
uint64_t savedRegisters = registers.getSP() + 8 * i;
541+
if (regCount > 0) {
542+
for (int8_t j = (int8_t)(regCount) - 1; j >= (int8_t)(regCount) - i; --j) {
543+
uint64_t addr = savedRegisters - 8 * (regCount - (uint64_t)(j));
544+
switch (registersSaved[j]) {
545+
case UNWIND_X86_64_REG_RBX:
546+
registers.setRBX(addressSpace.get64(addr));
547+
break;
548+
case UNWIND_X86_64_REG_R12:
549+
registers.setR12(addressSpace.get64(addr));
550+
break;
551+
case UNWIND_X86_64_REG_R13:
552+
registers.setR13(addressSpace.get64(addr));
553+
break;
554+
case UNWIND_X86_64_REG_R14:
555+
registers.setR14(addressSpace.get64(addr));
556+
break;
557+
case UNWIND_X86_64_REG_R15:
558+
registers.setR15(addressSpace.get64(addr));
559+
break;
560+
case UNWIND_X86_64_REG_RBP:
561+
registers.setRBP(addressSpace.get64(addr));
562+
break;
563+
default:
564+
_LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
565+
"function starting at 0x%llX",
566+
encoding, functionStart);
567+
_LIBUNWIND_ABORT("invalid compact unwind encoding");
568+
}
569+
}
570+
}
571+
framelessUnwind(addressSpace, savedRegisters, registers);
572+
return UNW_STEP_SUCCESS;
573+
} else {
574+
break;
575+
}
576+
}
577+
}
578+
579+
/*
580+
0x10fe2733a: 5b popq %rbx
581+
0x10fe2733b: 41 5c popq %r12
582+
0x10fe2733d: 41 5d popq %r13
583+
0x10fe2733f: 41 5e popq %r14
584+
0x10fe27341: 41 5f popq %r15
585+
0x10fe27343: 5d popq %rbp
586+
*/
587+
588+
433589
uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount;
434590
for (uint32_t i = 0; i < regCount; ++i) {
435591
switch (registersSaved[i]) {

0 commit comments

Comments
 (0)