From f7c163c98664de4143e2cbadb9e8c59fa32fa830 Mon Sep 17 00:00:00 2001 From: Lang Hames Date: Wed, 13 May 2015 00:44:47 +0000 Subject: [PATCH] [LLD] Properly relocate the LSDA field of MachO eh-frames. Previously the LSDA field was not being relocated during linking, leading to failures for some EH tests. llvm-svn: 237222 --- .../MachO/MachONormalizedFileToAtoms.cpp | 193 ++++++++++++++++----- lld/test/mach-o/parse-eh-frame-relocs-x86_64.yaml | 107 ++++++++++++ 2 files changed, 260 insertions(+), 40 deletions(-) create mode 100644 lld/test/mach-o/parse-eh-frame-relocs-x86_64.yaml diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp index e1dab38..d92d7a7a 100644 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp +++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp @@ -29,6 +29,7 @@ #include "lld/Core/LLVM.h" #include "llvm/Support/Format.h" #include "llvm/Support/MachO.h" +#include "llvm/Support/LEB128.h" using namespace llvm::MachO; using namespace lld::mach_o::normalized; @@ -644,11 +645,147 @@ static int64_t readSPtr(bool is64, bool isBig, const uint8_t *addr) { return res; } +/// --- Augmentation String Processing --- + +struct AugmentationDataInfo { + bool _present = false; + bool _mayHaveLSDA = false; +}; + +static std::error_code processAugmentationString(const uint8_t *augStr, + AugmentationDataInfo &adi, + unsigned *len = nullptr) { + + if (augStr[0] == '\0') { + if (len) + *len = 1; + return std::error_code(); + } + + if (augStr[0] != 'z') + return make_dynamic_error_code("expected 'z' at start of augmentation " + "string"); + + adi._present = true; + uint64_t idx = 1; + + while (augStr[idx] != '\0') { + if (augStr[idx] == 'L') { + adi._mayHaveLSDA = true; + ++idx; + } else + ++idx; + } + + if (len) + *len = idx + 1; + return std::error_code(); +} + +static std::error_code processCIE(const NormalizedFile &normalizedFile, + MachODefinedAtom *atom, + AugmentationDataInfo &adi) { + const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); + + const uint8_t *frameData = atom->rawContent().data(); + uint32_t size = read32(frameData, isBig); + uint64_t cieIDField = size == 0xffffffffU + ? sizeof(uint32_t) + sizeof(uint64_t) + : sizeof(uint32_t); + uint64_t versionField = cieIDField + sizeof(uint32_t); + uint64_t augmentationStringField = versionField + sizeof(uint8_t); + + if (auto err = processAugmentationString(frameData + augmentationStringField, + adi)) + return err; + + return std::error_code(); +} + +static std::error_code processFDE(const NormalizedFile &normalizedFile, + MachOFile &file, + mach_o::ArchHandler &handler, + const Section *ehFrameSection, + MachODefinedAtom *atom, + uint64_t offset, + const AugmentationDataInfo &adi) { + + const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); + const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch); + + // Compiler wasn't lazy and actually told us what it meant. + if (atom->begin() != atom->end()) + return std::error_code(); + + const uint8_t *frameData = atom->rawContent().data(); + uint32_t size = read32(frameData, isBig); + uint64_t cieFieldInFDE = size == 0xffffffffU + ? sizeof(uint32_t) + sizeof(uint64_t) + : sizeof(uint32_t); + + // Linker needs to fixup a reference from the FDE to its parent CIE (a + // 32-bit byte offset backwards in the __eh_frame section). + uint32_t cieDelta = read32(frameData + cieFieldInFDE, isBig); + uint64_t cieAddress = ehFrameSection->address + offset + cieFieldInFDE; + cieAddress -= cieDelta; + + Reference::Addend addend; + const Atom *cie = + findAtomCoveringAddress(normalizedFile, file, cieAddress, &addend); + atom->addReference(cieFieldInFDE, handler.unwindRefToCIEKind(), cie, + addend, handler.kindArch()); + + // Linker needs to fixup reference from the FDE to the function it's + // describing. FIXME: there are actually different ways to do this, and the + // particular method used is specified in the CIE's augmentation fields + // (hopefully) + uint64_t rangeFieldInFDE = cieFieldInFDE + sizeof(uint32_t); + + int64_t functionFromFDE = readSPtr(is64, isBig, + frameData + rangeFieldInFDE); + uint64_t rangeStart = ehFrameSection->address + offset + rangeFieldInFDE; + rangeStart += functionFromFDE; + + const Atom *func = + findAtomCoveringAddress(normalizedFile, file, rangeStart, &addend); + atom->addReference(rangeFieldInFDE, handler.unwindRefToFunctionKind(), + func, addend, handler.kindArch()); + + // Handle the augmentation data if there is any. + if (adi._present) { + // First process the augmentation data length field. + uint64_t augmentationDataLengthFieldInFDE = + rangeFieldInFDE + 2 * (is64 ? sizeof(uint64_t) : sizeof(uint32_t)); + unsigned lengthFieldSize = 0; + uint64_t augmentationDataLength = + llvm::decodeULEB128(frameData + augmentationDataLengthFieldInFDE, + &lengthFieldSize); + + if (adi._mayHaveLSDA && augmentationDataLength > 0) { + + // Look at the augmentation data field. + uint64_t augmentationDataFieldInFDE = + augmentationDataLengthFieldInFDE + lengthFieldSize; + + int64_t lsdaFromFDE = readSPtr(is64, isBig, + frameData + augmentationDataFieldInFDE); + uint64_t lsdaStart = + ehFrameSection->address + offset + augmentationDataFieldInFDE + + lsdaFromFDE; + const Atom *lsda = + findAtomCoveringAddress(normalizedFile, file, lsdaStart, &addend); + atom->addReference(augmentationDataFieldInFDE, + handler.unwindRefToFunctionKind(), + lsda, addend, handler.kindArch()); + } + } + + return std::error_code(); +} + std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile, MachOFile &file, mach_o::ArchHandler &handler) { - const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); - const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch); const Section *ehFrameSection = nullptr; for (auto §ion : normalizedFile.sections) @@ -662,51 +799,27 @@ std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile, if (!ehFrameSection) return std::error_code(); + std::error_code ehFrameErr; + AugmentationDataInfo adi; + file.eachAtomInSection(*ehFrameSection, [&](MachODefinedAtom *atom, uint64_t offset) -> void { assert(atom->contentType() == DefinedAtom::typeCFI); - if (ArchHandler::isDwarfCIE(isBig, atom)) - return; - - // Compiler wasn't lazy and actually told us what it meant. - if (atom->begin() != atom->end()) + // Bail out if we've encountered an error. + if (ehFrameErr) return; - const uint8_t *frameData = atom->rawContent().data(); - uint32_t size = read32(frameData, isBig); - uint64_t cieFieldInFDE = size == 0xffffffffU - ? sizeof(uint32_t) + sizeof(uint64_t) - : sizeof(uint32_t); - - // Linker needs to fixup a reference from the FDE to its parent CIE (a - // 32-bit byte offset backwards in the __eh_frame section). - uint32_t cieDelta = read32(frameData + cieFieldInFDE, isBig); - uint64_t cieAddress = ehFrameSection->address + offset + cieFieldInFDE; - cieAddress -= cieDelta; - - Reference::Addend addend; - const Atom *cie = - findAtomCoveringAddress(normalizedFile, file, cieAddress, &addend); - atom->addReference(cieFieldInFDE, handler.unwindRefToCIEKind(), cie, - addend, handler.kindArch()); - - // Linker needs to fixup reference from the FDE to the function it's - // describing. FIXME: there are actually different ways to do this, and the - // particular method used is specified in the CIE's augmentation fields - // (hopefully) - uint64_t rangeFieldInFDE = cieFieldInFDE + sizeof(uint32_t); - - int64_t functionFromFDE = readSPtr(is64, isBig, frameData + rangeFieldInFDE); - uint64_t rangeStart = ehFrameSection->address + offset + rangeFieldInFDE; - rangeStart += functionFromFDE; - - const Atom *func = - findAtomCoveringAddress(normalizedFile, file, rangeStart, &addend); - atom->addReference(rangeFieldInFDE, handler.unwindRefToFunctionKind(), func, - addend, handler.kindArch()); + const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); + if (ArchHandler::isDwarfCIE(isBig, atom)) { + adi = AugmentationDataInfo(); + ehFrameErr = processCIE(normalizedFile, atom, adi); + } else + ehFrameErr = processFDE(normalizedFile, file, handler, ehFrameSection, + atom, offset, adi); }); - return std::error_code(); + + return ehFrameErr; } diff --git a/lld/test/mach-o/parse-eh-frame-relocs-x86_64.yaml b/lld/test/mach-o/parse-eh-frame-relocs-x86_64.yaml new file mode 100644 index 0000000..c8281b3 --- /dev/null +++ b/lld/test/mach-o/parse-eh-frame-relocs-x86_64.yaml @@ -0,0 +1,107 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of x86_64 __eh_frame (dwarf unwind) relocations. +# +#_catchMyException: +# pushq %rbp +# movq %rsp, %rbp +# callq _foo +# popq %rbp +# retq +# movq %rax, %rdi +# callq ___cxa_begin_catch +# popq %rbp +# jmp ___cxa_end_catch + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [0x55, 0x48, 0x89, 0xe5, 0xe8, 0x00, 0x00, 0x00, + 0x00, 0x5d, 0xc3, 0x48, 0x89, 0xc7, 0xe8, 0x00, + 0x00, 0x00, 0x00, 0x5d, 0xe9, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000015 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 2 + - offset: 0x0000000f + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x00000005 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 0 + - segment: __TEXT + section: __gcc_except_tab + type: S_REGULAR + attributes: [ ] + address: 0x000000000000001c + content: [ 0x00, 0x00, 0x00, 0x00 ] + - segment: __TEXT + section: __eh_frame + type: S_COALESCED + attributes: [ ] + address: 0x0000000000000020 + content: [ 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x7a, 0x50, 0x4c, 0x52, 0x00, 0x01, 0x78, + 0x10, 0x07, 0x9b, 0x04, 0x00, 0x00, 0x00, 0x10, + 0x10, 0x0c, 0x07, 0x08, 0x90, 0x01, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0xB8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x41, 0x0e, 0x10, 0x86, 0x02, 0x43, 0x0d, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + address: 0x0000000000000068 + content: [ 0x00, 0x00, 0x00, 0x00 ] +local-symbols: + - name: _catchMyException + type: N_SECT + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _foo + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: ___cxa_begin_catch + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: ___cxa_end_catch + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + +# CHECK: - type: unwind-cfi +# CHECK-NOT: - type: +# CHECK: references: +# CHECK-NEXT: - kind: negDelta32 +# CHECK-NEXT: offset: 4 +# CHECK-NEXT: target: L000 +# CHECK-NEXT: - kind: unwindFDEToFunction +# CHECK-NEXT: offset: 8 +# CHECK-NEXT: target: _catchMyException +# CHECK-NEXT: - kind: unwindFDEToFunction +# CHECK-NEXT: offset: 25 +# CHECK-NEXT: target: L001 -- 2.7.4