X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/d425e3882ca60fabae080ddb890789ef2e73a66b..f80fe69f3f29962e8aa43a99f8ed9201548f3d78:/src/ld/parsers/libunwind/DwarfInstructions.hpp diff --git a/src/ld/parsers/libunwind/DwarfInstructions.hpp b/src/ld/parsers/libunwind/DwarfInstructions.hpp index b04582d..c67a237 100644 --- a/src/ld/parsers/libunwind/DwarfInstructions.hpp +++ b/src/ld/parsers/libunwind/DwarfInstructions.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2013 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -95,7 +95,8 @@ public: typedef typename A::sint_t sint_t; static const char* parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, - CFI_Atom_Info* infos, uint32_t infosCount, void* ref, WarnFunc warn); + const pint_t cuStarts[], uint32_t cuCount, bool keepDwarfWhichHasCU, bool forceDwarfConversion, + CFI_Atom_Info* infos, uint32_t& infosCount, void* ref, WarnFunc warn); static compact_unwind_encoding_t createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart, @@ -152,6 +153,18 @@ private: static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, const Registers_ppc&, const typename CFI_Parser::PrologInfo& prolog, char warningBuffer[1024]); + + // arm64 specific variants + static bool isReturnAddressRegister(int regNum, const Registers_arm64&); + static int lastRestoreReg(const Registers_arm64&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_arm64&); + static bool checkRegisterPair(uint32_t reg, const typename CFI_Parser::PrologInfo& prolog, + int& offset, char warningBuffer[1024]); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_arm64&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_arm64&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); + }; @@ -159,7 +172,8 @@ private: template const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, - CFI_Atom_Info* infos, uint32_t infosCount, void* ref, WarnFunc warn) + const pint_t cuStarts[], uint32_t cuCount, bool keepDwarfWhichHasCU, bool forceDwarfConversion, + CFI_Atom_Info* infos, uint32_t& infosCount, void* ref, WarnFunc warn) { typename CFI_Parser::CIE_Info cieInfo; CFI_Atom_Info* entry = infos; @@ -221,7 +235,6 @@ const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionS pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding); pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F); //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); - // test if pc is within the function this FDE covers entry->u.fdeInfo.function.targetAddress = pcStart; entry->u.fdeInfo.function.offsetInCFI = offsetOfFunctionAddress; entry->u.fdeInfo.function.encodingOfTargetAddress = cieInfo.pointerEncoding; @@ -243,34 +256,60 @@ const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionS } p = endOfAug; } - // compute compact unwind encoding - typename CFI_Parser::FDE_Info fdeInfo; - fdeInfo.fdeStart = currentCFI; - fdeInfo.fdeLength = nextCFI - currentCFI; - fdeInfo.fdeInstructions = p; - fdeInfo.pcStart = pcStart; - fdeInfo.pcEnd = pcStart + pcRange; - fdeInfo.lsda = entry->u.fdeInfo.lsda.targetAddress; - typename CFI_Parser::PrologInfo prolog; - R dummy; // for proper selection of architecture specific functions - if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { - char warningBuffer[1024]; - entry->u.fdeInfo.compactUnwindInfo = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); - if ( fdeInfo.lsda != CFI_INVALID_ADDRESS ) - entry->u.fdeInfo.compactUnwindInfo |= UNWIND_HAS_LSDA; - if ( warningBuffer[0] != '\0' ) - warn(ref, fdeInfo.pcStart, warningBuffer); + // See if already is a compact unwind for this address. + bool alreadyHaveCU = false; + for (uint32_t i=0; i < cuCount; ++i) { + if (cuStarts[i] == entry->u.fdeInfo.function.targetAddress) { + alreadyHaveCU = true; + break; + } + } + //fprintf(stderr, "FDE for func at 0x%08X, alreadyHaveCU=%d\n", (uint32_t)entry->u.fdeInfo.function.targetAddress, alreadyHaveCU); + if ( alreadyHaveCU && !forceDwarfConversion ) { + if ( keepDwarfWhichHasCU ) + ++entry; } else { - warn(ref, CFI_INVALID_ADDRESS, "dwarf unwind instructions could not be parsed"); - entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + if ( (cuCount != 0) && !forceDwarfConversion ) { + // Have some compact unwind, so this is a new .o file, therefore anything without + // compact unwind must be something not expressable in compact unwind. + R dummy; + entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + } + else { + // compute compact unwind encoding by parsing dwarf + typename CFI_Parser::FDE_Info fdeInfo; + fdeInfo.fdeStart = currentCFI; + fdeInfo.fdeLength = nextCFI - currentCFI; + fdeInfo.fdeInstructions = p; + fdeInfo.pcStart = pcStart; + fdeInfo.pcEnd = pcStart + pcRange; + fdeInfo.lsda = entry->u.fdeInfo.lsda.targetAddress; + typename CFI_Parser::PrologInfo prolog; + R dummy; // for proper selection of architecture specific functions + if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { + char warningBuffer[1024]; + entry->u.fdeInfo.compactUnwindInfo = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); + if ( fdeInfo.lsda != CFI_INVALID_ADDRESS ) + entry->u.fdeInfo.compactUnwindInfo |= UNWIND_HAS_LSDA; + if ( warningBuffer[0] != '\0' ) + warn(ref, fdeInfo.pcStart, warningBuffer); + } + else { + warn(ref, CFI_INVALID_ADDRESS, "dwarf unwind instructions could not be parsed"); + entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + } + } + ++entry; } - ++entry; } p = nextCFI; } - if ( entry != end ) - return "wrong entry count for parseCFIs"; + if ( entry != end ) { + //fprintf(stderr, "DwarfInstructions::parseCFIs() infosCount was %d on input, now %ld\n", infosCount, entry - infos); + infosCount = (entry - infos); + } + return NULL; // success } @@ -1037,7 +1076,7 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo for (int i=0; i < 64; ++i) { if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterInCFA ) { - sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + sprintf(warningBuffer, "register %d saved somewhere other than in frame", i); return UNWIND_X86_64_MODE_DWARF; } switch (i) { @@ -1184,15 +1223,19 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo strcpy(warningBuffer, "stack size is large but stack subq instruction not found"); return UNWIND_X86_64_MODE_DWARF; } - pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + #if __EXCEPTIONS try { + #endif uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/8; + #if __EXCEPTIONS } catch (...) { strcpy(warningBuffer, "stack size is large but stack subq instruction not found"); return UNWIND_X86_64_MODE_DWARF; } + #endif stackValue = functionContentAdjustStackIns - funcAddr; immedStackSize = false; if ( stackAdjust > 7 ) { @@ -1410,7 +1453,7 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo for (int i=0; i < 64; ++i) { if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterInCFA ) { - sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + sprintf(warningBuffer, "register %d saved somewhere other than in frame", i); return UNWIND_X86_MODE_DWARF; } switch (i) { @@ -1554,12 +1597,22 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo if ( stackValue > stackMaxImmedValue ) { // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; - uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); - stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4; + #if __EXCEPTIONS + try { + #endif + uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); + stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4; + #if __EXCEPTIONS + } + catch (...) { + strcpy(warningBuffer, "stack size is large but stack subl instruction not found"); + return UNWIND_X86_MODE_DWARF; + } + #endif stackValue = functionContentAdjustStackIns - funcAddr; immedStackSize = false; if ( stackAdjust > 7 ) { - strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size"); + strcpy(warningBuffer, "stack subl instruction is too different from dwarf stack size"); return UNWIND_X86_MODE_DWARF; } encoding = UNWIND_X86_MODE_STACK_IND; @@ -1715,6 +1768,195 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo +// +// arm64 specific functions +// + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_arm64&) +{ + return UNWIND_ARM64_MODE_DWARF; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_arm64&) +{ + return (regNum == UNW_ARM64_LR); +} + +template +int DwarfInstructions::lastRestoreReg(const Registers_arm64&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)UNW_ARM64_D31 ); + return UNW_ARM64_D31; +} + + +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_arm64& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else + ABORT("getCFA(): unsupported location for arm64 cfa"); +} + +template +bool DwarfInstructions::checkRegisterPair(uint32_t reg, const typename CFI_Parser::PrologInfo& prolog, + int& offset, char warningBuffer[1024]) +{ + if ( (prolog.savedRegisters[reg].location != CFI_Parser::kRegisterUnused) + || (prolog.savedRegisters[reg+1].location != CFI_Parser::kRegisterUnused) ) { + if ( prolog.savedRegisters[reg].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other than in frame", reg); + return false; + } + if ( prolog.savedRegisters[reg+1].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other than in frame", reg+1); + return false; + } + if ( prolog.savedRegisters[reg].value != prolog.savedRegisters[reg+1].value + 8 ) { + sprintf(warningBuffer, "registers %d and %d not saved contiguously in frame", reg, reg+1); + return false; + } + if ( prolog.savedRegisters[reg].value != offset ) { + sprintf(warningBuffer, "registers %d not saved contiguously in frame", reg); + return false; + } + offset -= 16; + return true; + } + return false; +} + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_arm64& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + + if ( prolog.registerSavedTwiceInCIE == UNW_ARM64_LR ) { + warningBuffer[0] = '\0'; // silently disable conversion to compact unwind by linker + return UNWIND_ARM64_MODE_DWARF; + } + // don't create compact unwind info for unsupported dwarf kinds + if ( prolog.registerSavedMoreThanOnce ) { + strcpy(warningBuffer, "register saved more than once (might be shrink wrap)"); + return UNWIND_ARM64_MODE_DWARF; + } + if ( prolog.spExtraArgSize != 0 ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size"); + return UNWIND_ARM64_MODE_DWARF; + } + if ( prolog.sameValueUsed ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_same_value"); + return UNWIND_ARM64_MODE_DWARF; + } + + compact_unwind_encoding_t encoding = 0; + int offset = 0; + + // figure out which kind of frame this function uses + bool standardFPframe = ( + (prolog.cfaRegister == UNW_ARM64_FP) + && (prolog.cfaRegisterOffset == 16) + && (prolog.savedRegisters[UNW_ARM64_FP].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_ARM64_FP].value == -16) + && (prolog.savedRegisters[UNW_ARM64_LR].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_ARM64_LR].value == -8) ); + + bool standardFrameless = ( prolog.cfaRegister == UNW_ARM64_SP ); + + if ( standardFrameless ) { + // verify enough space for registers saved + int count = 0; + for (int i=0; i < 96; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) + ++count; + } + if ( count * 8 > prolog.cfaRegisterOffset ) { + strcpy(warningBuffer, "saved registers do not fit in stack size"); + return UNWIND_ARM64_MODE_DWARF; + } + if ( (prolog.cfaRegisterOffset % 16) != 0 ) { + strcpy(warningBuffer, "stack size is not 16-byte multiple"); + return UNWIND_ARM64_MODE_DWARF; + } + const int32_t maxStack = (UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK >> __builtin_ctz(UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK)); + if ( (prolog.cfaRegisterOffset / 16) > maxStack ) { + strcpy(warningBuffer, "stack size is too large for frameless function"); + return UNWIND_ARM64_MODE_DWARF; + } + encoding = UNWIND_ARM64_MODE_FRAMELESS | ((prolog.cfaRegisterOffset/16) << __builtin_ctz(UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK)); + offset = -16; + } + else if ( standardFPframe ) { + encoding = UNWIND_ARM64_MODE_FRAME; + offset = -24; + } + else { + // no compact encoding for this + strcpy(warningBuffer, "does not use standard frame"); + return UNWIND_ARM64_MODE_DWARF; + } + + // make sure no volatile registers are saved + for (int i=UNW_ARM64_X0; i < UNW_ARM64_X19; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + sprintf(warningBuffer, "non standard register %d saved in frame", i); + return UNWIND_ARM64_MODE_DWARF; + } + } + for (int i=UNW_ARM64_SP+1; i < UNW_ARM64_D8; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + sprintf(warningBuffer, "non standard register %d saved in frame", i); + return UNWIND_ARM64_MODE_DWARF; + } + } + for (int i=UNW_ARM64_D16; i < UNW_ARM64_D31+1; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + sprintf(warningBuffer, "non standard register %d saved in frame", i); + return UNWIND_ARM64_MODE_DWARF; + } + } + + // compute encoding + bool X19_X20_saved = checkRegisterPair(UNW_ARM64_X19, prolog, offset, warningBuffer); + bool X21_X22_saved = checkRegisterPair(UNW_ARM64_X21, prolog, offset, warningBuffer); + bool X23_X24_saved = checkRegisterPair(UNW_ARM64_X23, prolog, offset, warningBuffer); + bool X25_X26_saved = checkRegisterPair(UNW_ARM64_X25, prolog, offset, warningBuffer); + bool X27_X28_saved = checkRegisterPair(UNW_ARM64_X27, prolog, offset, warningBuffer); + bool D8_D9_saved = checkRegisterPair(UNW_ARM64_D8, prolog, offset, warningBuffer); + bool D10_D11_saved = checkRegisterPair(UNW_ARM64_D10, prolog, offset, warningBuffer); + bool D12_D13_saved = checkRegisterPair(UNW_ARM64_D12, prolog, offset, warningBuffer); + bool D14_D15_saved = checkRegisterPair(UNW_ARM64_D14, prolog, offset, warningBuffer); + if ( warningBuffer[0] != '\0' ) + return UNWIND_ARM64_MODE_DWARF; + + if ( X19_X20_saved ) + encoding |= UNWIND_ARM64_FRAME_X19_X20_PAIR; + if ( X21_X22_saved ) + encoding |= UNWIND_ARM64_FRAME_X21_X22_PAIR; + if ( X23_X24_saved ) + encoding |= UNWIND_ARM64_FRAME_X23_X24_PAIR; + if ( X25_X26_saved ) + encoding |= UNWIND_ARM64_FRAME_X25_X26_PAIR; + if ( X27_X28_saved ) + encoding |= UNWIND_ARM64_FRAME_X27_X28_PAIR; + if ( D8_D9_saved ) + encoding |= UNWIND_ARM64_FRAME_D8_D9_PAIR; + if ( D10_D11_saved ) + encoding |= UNWIND_ARM64_FRAME_D10_D11_PAIR; + if ( D12_D13_saved ) + encoding |= UNWIND_ARM64_FRAME_D12_D13_PAIR; + if ( D14_D15_saved ) + encoding |= UNWIND_ARM64_FRAME_D14_D15_PAIR; + + return encoding; +} } // namespace libunwind