summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Forstall <brucefo@microsoft.com>2016-04-05 13:09:07 -0700
committerBruce Forstall <brucefo@microsoft.com>2016-04-05 13:09:07 -0700
commitb54f7b545907b7b56a338a54d14b01dab937cfc1 (patch)
treed71ce2c6ca72f7fa1477a4a26ceb6bd13834db88
parentcaa979f987c90be2075183a9a819ae110957a2ee (diff)
parenta3f23bd7de3fd60b2a5bf2f56ba3c38a72c50435 (diff)
downloadcoreclr-b54f7b545907b7b56a338a54d14b01dab937cfc1.tar.gz
coreclr-b54f7b545907b7b56a338a54d14b01dab937cfc1.tar.bz2
coreclr-b54f7b545907b7b56a338a54d14b01dab937cfc1.zip
Merge pull request #4019 from BruceForstall/Fix1977
Fix #1977: always create RBP chains on Unix
-rw-r--r--src/inc/clrnt.h25
-rw-r--r--src/jit/codegenxarch.cpp12
-rw-r--r--src/jit/emitxarch.cpp4
-rw-r--r--src/jit/unwindamd64.cpp61
-rw-r--r--src/unwinder/amd64/unwinder_amd64.cpp64
5 files changed, 153 insertions, 13 deletions
diff --git a/src/inc/clrnt.h b/src/inc/clrnt.h
index fd7e56b61b..bd25ab39ff 100644
--- a/src/inc/clrnt.h
+++ b/src/inc/clrnt.h
@@ -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[] = {
@@ -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
};
//
diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp
index 7239d705bf..c1d97d3670 100644
--- a/src/jit/codegenxarch.cpp
+++ b/src/jit/codegenxarch.cpp
@@ -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.
@@ -7691,6 +7701,8 @@ int CodeGenInterface::genSPtoFPdelta()
delta = genTotalFrameSize();
}
+#endif // !PLATFORM_UNIX
+
return delta;
}
diff --git a/src/jit/emitxarch.cpp b/src/jit/emitxarch.cpp
index 7e17ab7a4c..2b0a3141ec 100644
--- a/src/jit/emitxarch.cpp
+++ b/src/jit/emitxarch.cpp
@@ -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);
diff --git a/src/jit/unwindamd64.cpp b/src/jit/unwindamd64.cpp
index b5658ab716..6a5b199a07 100644
--- a/src/jit/unwindamd64.cpp
+++ b/src/jit/unwindamd64.cpp
@@ -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
@@ -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)
{
@@ -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);
diff --git a/src/unwinder/amd64/unwinder_amd64.cpp b/src/unwinder/amd64/unwinder_amd64.cpp
index b3dd1ab1ac..c04db21dfd 100644
--- a/src/unwinder/amd64/unwinder_amd64.cpp
+++ b/src/unwinder/amd64/unwinder_amd64.cpp
@@ -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) {
@@ -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
@@ -1055,6 +1082,7 @@ Arguments:
ULONG EpilogueSize;
PEXCEPTION_ROUTINE FoundHandler;
ULONG FrameRegister;
+ ULONG FrameOffset;
ULONG Index;
BOOL InEpilogue;
PULONG64 IntegerAddress;
@@ -1115,23 +1143,55 @@ Arguments:
} 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;