Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Merge pull request #4019 from BruceForstall/Fix1977
Browse files Browse the repository at this point in the history
Fix #1977: always create RBP chains on Unix
  • Loading branch information
BruceForstall committed Apr 5, 2016
2 parents caa979f + a3f23bd commit b54f7b5
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 13 deletions.
25 changes: 23 additions & 2 deletions src/inc/clrnt.h
Original file line number Diff line number Diff line change
Expand Up @@ -816,7 +816,24 @@ typedef enum _UNWIND_OP_CODES {
UWOP_SPARE_CODE,
UWOP_SAVE_XMM128,
UWOP_SAVE_XMM128_FAR,
UWOP_PUSH_MACHFRAME
UWOP_PUSH_MACHFRAME,

#ifdef PLATFORM_UNIX
// UWOP_SET_FPREG_LARGE is a CLR Unix-only extension to the Windows AMD64 unwind codes.
// It is not part of the standard Windows AMD64 unwind codes specification.
// UWOP_SET_FPREG allows for a maximum of a 240 byte offset between RSP and the
// frame pointer, when the frame pointer is established. UWOP_SET_FPREG_LARGE
// has a 32-bit range scaled by 16. When UWOP_SET_FPREG_LARGE is used,
// UNWIND_INFO.FrameRegister must be set to the frame pointer register, and
// UNWIND_INFO.FrameOffset must be set to 15 (its maximum value). UWOP_SET_FPREG_LARGE
// is followed by two UNWIND_CODEs that are combined to form a 32-bit offset (the same
// as UWOP_SAVE_NONVOL_FAR). This offset is then scaled by 16. The result must be less
// than 2^32 (that is, the top 4 bits of the unscaled 32-bit number must be zero). This
// result is used as the frame pointer register offset from RSP at the time the frame pointer
// is established. Either UWOP_SET_FPREG or UWOP_SET_FPREG_LARGE can be used, but not both.

UWOP_SET_FPREG_LARGE,
#endif // PLATFORM_UNIX
} UNWIND_OP_CODES, *PUNWIND_OP_CODES;

static const UCHAR UnwindOpExtraSlotTable[] = {
Expand All @@ -830,7 +847,11 @@ static const UCHAR UnwindOpExtraSlotTable[] = {
2, // UWOP_SPARE_CODE // previously 64-bit UWOP_SAVE_XMM_FAR
1, // UWOP_SAVE_XMM128
2, // UWOP_SAVE_XMM128_FAR
0 // UWOP_PUSH_MACHFRAME
0, // UWOP_PUSH_MACHFRAME

#ifdef PLATFORM_UNIX
2, // UWOP_SET_FPREG_LARGE
#endif // PLATFORM_UNIX
};

//
Expand Down
12 changes: 12 additions & 0 deletions src/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7667,6 +7667,16 @@ int CodeGenInterface::genSPtoFPdelta()
{
int delta;

#ifdef PLATFORM_UNIX

// We require frame chaining on Unix to support native tool unwinding (such as
// unwinding by the native debugger). We have a CLR-only extension to the
// unwind codes (UWOP_SET_FPREG_LARGE) to support SP->FP offsets larger than 240.
// If Unix ever supports EnC, the RSP == RBP assumption will have to be reevaluated.
delta = genTotalFrameSize();

#else // !PLATFORM_UNIX

// As per Amd64 ABI, RBP offset from initial RSP can be between 0 and 240 if
// RBP needs to be reported in unwind codes. This case would arise for methods
// with localloc.
Expand All @@ -7691,6 +7701,8 @@ int CodeGenInterface::genSPtoFPdelta()
delta = genTotalFrameSize();
}

#endif // !PLATFORM_UNIX

return delta;
}

Expand Down
4 changes: 3 additions & 1 deletion src/jit/emitxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1676,12 +1676,14 @@ UNATIVE_OFFSET emitter::emitInsSizeSV(size_t code, int var, int dsp)

if (EBPbased)
{
#ifdef _TARGET_AMD64_
#if defined(_TARGET_AMD64_) && !defined(PLATFORM_UNIX)
// If localloc is not used, then ebp chaining is done and hence
// offset of locals will be at negative offsets, Otherwise offsets
// will be positive. In future, when RBP gets positioned in the
// middle of the frame so as to optimize instruction encoding size,
// the below asserts needs to be modified appropriately.
// However, for Unix platforms, we always do frame pointer chaining,
// so offsets from the frame pointer will always be negative.
if (emitComp->compLocallocUsed || emitComp->opts.compDbgEnC)
{
noway_assert((int)offs >= 0);
Expand Down
61 changes: 53 additions & 8 deletions src/jit/unwindamd64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,17 +380,41 @@ void Compiler::unwindSetFrameRegWindows(regNumber reg, unsigned offset)

assert(func->unwindHeader.Version == 1); // Can't call this before unwindBegProlog
assert(func->unwindHeader.CountOfUnwindCodes == 0); // Can't call this after unwindReserve
assert(func->unwindCodeSlot > sizeof(UNWIND_CODE));
UNWIND_CODE * code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)];
unsigned int cbProlog = unwindGetCurrentOffset(func);
noway_assert((BYTE)cbProlog == cbProlog);
code->CodeOffset = (BYTE)cbProlog;
code->UnwindOp = UWOP_SET_FPREG;
code->OpInfo = 0;

func->unwindHeader.FrameRegister = (BYTE)reg;
assert(offset <= 240);
assert(offset % 16 == 0);
func->unwindHeader.FrameOffset = offset / 16;

#ifdef PLATFORM_UNIX
if (offset > 240)
{
// On Unix only, we have a CLR-only extension to the AMD64 unwind codes: UWOP_SET_FPREG_LARGE.
// It has a 32-bit offset (scaled). You must set UNWIND_INFO.FrameOffset to 15. The 32-bit
// offset follows in 2 UNWIND_CODE fields.

assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(ULONG)));
ULONG* codedSize = (ULONG*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(ULONG)];
assert(offset % 16 == 0);
*codedSize = offset / 16;

UNWIND_CODE* code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)];
code->CodeOffset = (BYTE)cbProlog;
code->OpInfo = 0;
code->UnwindOp = UWOP_SET_FPREG_LARGE;
func->unwindHeader.FrameOffset = 15;
}
else
#endif // PLATFORM_UNIX
{
assert(func->unwindCodeSlot > sizeof(UNWIND_CODE));
UNWIND_CODE* code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)];
code->CodeOffset = (BYTE)cbProlog;
code->OpInfo = 0;
code->UnwindOp = UWOP_SET_FPREG;
assert(offset <= 240);
assert(offset % 16 == 0);
func->unwindHeader.FrameOffset = offset / 16;
}
}

#ifdef UNIX_AMD64_ABI
Expand Down Expand Up @@ -545,6 +569,7 @@ void DumpUnwindInfo(bool isHotCode, UNATIVE_OFFSET startOffset, UNATIVE_OFFSET e

for (unsigned i = 0; i < pHeader->CountOfUnwindCodes; i++)
{
unsigned offset;
const UNWIND_CODE * const pCode = &(pHeader->UnwindCode[i]);
switch (pCode->UnwindOp)
{
Expand Down Expand Up @@ -588,6 +613,26 @@ void DumpUnwindInfo(bool isHotCode, UNATIVE_OFFSET startOffset, UNATIVE_OFFSET e
pCode->CodeOffset, pCode->UnwindOp, pCode->OpInfo); // This should be zero
break;

#ifdef PLATFORM_UNIX

case UWOP_SET_FPREG_LARGE:
printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SET_FPREG_LARGE (%u) OpInfo: Unused (%u)\n",
pCode->CodeOffset, pCode->UnwindOp, pCode->OpInfo); // This should be zero
i++;
offset = *(ULONG*)&(pHeader->UnwindCode[i]);
i++;
printf(" Scaled Offset: %u * 16 = %u = 0x%08X\n",
offset,
offset * 16,
offset * 16);
if ((offset & 0xF0000000) != 0)
{
printf(" Illegal unscaled offset: too large\n");
}
break;

#endif // PLATFORM_UNIX

case UWOP_SAVE_NONVOL:
printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SAVE_NONVOL (%u) OpInfo: %s (%u)\n",
pCode->CodeOffset, pCode->UnwindOp, getRegName(pCode->OpInfo), pCode->OpInfo);
Expand Down
64 changes: 62 additions & 2 deletions src/unwinder/amd64/unwinder_amd64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -730,9 +730,15 @@ Return Value:
//

UnwindOp = UnwindInfo->UnwindCode[Index].UnwindOp;
#ifdef PLATFORM_UNIX
if (UnwindOp > UWOP_SET_FPREG_LARGE) {
return E_UNEXPECTED;
}
#else // !PLATFORM_UNIX
if (UnwindOp > UWOP_PUSH_MACHFRAME) {
return E_UNEXPECTED;
}
#endif // !PLATFORM_UNIX

OpInfo = UnwindInfo->UnwindCode[Index].OpInfo;
if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) {
Expand Down Expand Up @@ -799,6 +805,27 @@ Return Value:
ContextRecord->Rsp = IntegerRegister[UnwindInfo->FrameRegister];
ContextRecord->Rsp -= UnwindInfo->FrameOffset * 16;
break;

#ifdef PLATFORM_UNIX

//
// Establish the the frame pointer register using a large size displacement.
// UNWIND_INFO.FrameOffset must be 15 (the maximum value, corresponding to a scaled
// offset of 15 * 16 == 240). The next two codes contain a 32-bit offset, which
// is also scaled by 16, since the stack must remain 16-bit aligned.
//

case UWOP_SET_FPREG_LARGE:
UNWINDER_ASSERT(UnwindInfo->FrameOffset == 15);
Index += 2;
FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset;
FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16;
UNWINDER_ASSERT((FrameOffset & 0xF0000000) == 0);
ContextRecord->Rsp = IntegerRegister[UnwindInfo->FrameRegister];
ContextRecord->Rsp -= FrameOffset * 16;
break;

#endif // PLATFORM_UNIX

//
// Save nonvolatile integer register on the stack using a
Expand Down Expand Up @@ -1055,6 +1082,7 @@ Routine Description:
ULONG EpilogueSize;
PEXCEPTION_ROUTINE FoundHandler;
ULONG FrameRegister;
ULONG FrameOffset;
ULONG Index;
BOOL InEpilogue;
PULONG64 IntegerAddress;
Expand Down Expand Up @@ -1115,23 +1143,55 @@ Routine Description:
} else if ((PrologOffset >= UnwindInfo->SizeOfProlog) ||
((UnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0)) {

FrameOffset = UnwindInfo->FrameOffset;

#ifdef PLATFORM_UNIX
// If UnwindInfo->FrameOffset == 15 (the maximum value), then there might be a UWOP_SET_FPREG_LARGE.
// However, it is still legal for a UWOP_SET_FPREG to set UnwindInfo->FrameOffset == 15 (since this
// was always part of the specification), so we need to look through the UnwindCode array to determine
// if there is indeed a UWOP_SET_FPREG_LARGE. If we don't find UWOP_SET_FPREG_LARGE, then just use
// (scaled) FrameOffset of 240, as before. (We don't verify there is a UWOP_SET_FPREG code, but we could.)
if (FrameOffset == 15) {
Index = 0;
while (Index < UnwindInfo->CountOfUnwindCodes) {
UnwindOp = UnwindInfo->UnwindCode[Index];
if (UnwindOp.UnwindOp == UWOP_SET_FPREG_LARGE) {
FrameOffset = UnwindInfo->UnwindCode[Index + 1].FrameOffset;
FrameOffset += UnwindInfo->UnwindCode[Index + 2].FrameOffset << 16;
break;
}

Index += UnwindOpSlots(UnwindOp);
}
}
#endif // PLATFORM_UNIX

*EstablisherFrame = (&ContextRecord->Rax)[UnwindInfo->FrameRegister];
*EstablisherFrame -= UnwindInfo->FrameOffset * 16;
*EstablisherFrame -= FrameOffset * 16;

} else {
FrameOffset = UnwindInfo->FrameOffset;
Index = 0;
while (Index < UnwindInfo->CountOfUnwindCodes) {
UnwindOp = UnwindInfo->UnwindCode[Index];
if (UnwindOp.UnwindOp == UWOP_SET_FPREG) {
break;
}
#ifdef PLATFORM_UNIX
else if (UnwindOp.UnwindOp == UWOP_SET_FPREG_LARGE) {
UNWINDER_ASSERT(UnwindInfo->FrameOffset == 15);
FrameOffset = UnwindInfo->UnwindCode[Index + 1].FrameOffset;
FrameOffset += UnwindInfo->UnwindCode[Index + 2].FrameOffset << 16;
break;
}
#endif // PLATFORM_UNIX

Index += UnwindOpSlots(UnwindOp);
}

if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) {
*EstablisherFrame = (&ContextRecord->Rax)[UnwindInfo->FrameRegister];
*EstablisherFrame -= UnwindInfo->FrameOffset * 16;
*EstablisherFrame -= FrameOffset * 16;

} else {
*EstablisherFrame = ContextRecord->Rsp;
Expand Down

0 comments on commit b54f7b5

Please sign in to comment.