diff --git a/lld/MachO/ConcatOutputSection.h b/lld/MachO/ConcatOutputSection.h index 9af661d0ab1e0..8131c48d31113 100644 --- a/lld/MachO/ConcatOutputSection.h +++ b/lld/MachO/ConcatOutputSection.h @@ -25,8 +25,9 @@ class Defined; // in the final binary. class ConcatOutputSection : public OutputSection { public: - explicit ConcatOutputSection(StringRef name) - : OutputSection(ConcatKind, name) {} + explicit ConcatOutputSection(StringRef name, + OutputSection::Kind kind = ConcatKind) + : OutputSection(kind, name) {} const ConcatInputSection *firstSection() const { return inputs.front(); } const ConcatInputSection *lastSection() const { return inputs.back(); } @@ -46,7 +47,7 @@ class ConcatOutputSection : public OutputSection { void writeTo(uint8_t *buf) const override; static bool classof(const OutputSection *sec) { - return sec->kind() == ConcatKind; + return sec->kind() == ConcatKind || sec->kind() == TextKind; } static ConcatOutputSection *getOrCreateForInput(const InputSection *); @@ -66,12 +67,18 @@ class ConcatOutputSection : public OutputSection { // support thunk insertion. class TextOutputSection : public ConcatOutputSection { public: - explicit TextOutputSection(StringRef name) : ConcatOutputSection(name) {} + explicit TextOutputSection(StringRef name) + : ConcatOutputSection(name, TextKind) {} void finalizeContents() override {} void finalize() override; bool needsThunks() const; + ArrayRef getThunks() const { return thunks; } void writeTo(uint8_t *buf) const override; + static bool classof(const OutputSection *sec) { + return sec->kind() == TextKind; + } + private: uint64_t estimateStubsInRangeVA(size_t callIdx) const; diff --git a/lld/MachO/MapFile.cpp b/lld/MachO/MapFile.cpp index 9c0621622ae2f..12417df8cecb8 100644 --- a/lld/MachO/MapFile.cpp +++ b/lld/MachO/MapFile.cpp @@ -161,6 +161,20 @@ static uint64_t getSymSizeForMap(Defined *sym) { return sym->size; } +// Merges two vectors of input sections in order of their outSecOff values. +// This approach creates a new (temporary) vector which is not ideal but the +// ideal approach leads to a lot of code duplication. +static std::vector +mergeOrderedInputs(ArrayRef inputs1, + ArrayRef inputs2) { + std::vector vec(inputs1.size() + inputs2.size()); + std::merge(inputs1.begin(), inputs1.end(), inputs2.begin(), inputs2.end(), + vec.begin(), [](ConcatInputSection *a, ConcatInputSection *b) { + return a->outSecOff < b->outSecOff; + }); + return vec; +} + void macho::writeMapFile() { if (config->mapFile.empty()) return; @@ -220,7 +234,11 @@ void macho::writeMapFile() { os << "# Address\tSize \tFile Name\n"; for (const OutputSegment *seg : outputSegments) { for (const OutputSection *osec : seg->getSections()) { - if (auto *concatOsec = dyn_cast(osec)) { + if (auto *textOsec = dyn_cast(osec)) { + auto inputsAndThunks = + mergeOrderedInputs(textOsec->inputs, textOsec->getThunks()); + printIsecArrSyms(inputsAndThunks); + } else if (auto *concatOsec = dyn_cast(osec)) { printIsecArrSyms(concatOsec->inputs); } else if (osec == in.cStringSection || osec == in.objcMethnameSection) { const auto &liveCStrings = info.liveCStringsForSection.lookup(osec); diff --git a/lld/MachO/OutputSection.h b/lld/MachO/OutputSection.h index 5297a03c2cfa7..9afd3a9eeb192 100644 --- a/lld/MachO/OutputSection.h +++ b/lld/MachO/OutputSection.h @@ -37,6 +37,7 @@ class OutputSection { enum Kind { ConcatKind, SyntheticKind, + TextKind, }; OutputSection(Kind kind, StringRef name) : name(name), sectionKind(kind) {} diff --git a/lld/test/MachO/arm64-thunks.s b/lld/test/MachO/arm64-thunks.s index d887359bbc23e..858a27dfe36af 100644 --- a/lld/test/MachO/arm64-thunks.s +++ b/lld/test/MachO/arm64-thunks.s @@ -8,14 +8,46 @@ ## (4) early calls to a dylib stub use a thunk, and later calls the stub ## directly ## (5) Thunks are created for all sections in the text segment with branches. +## (6) Thunks are in the linker map file. ## Notes: ## 0x4000000 = 64 Mi = half the magnitude of the forward-branch range # RUN: rm -rf %t; mkdir %t # RUN: llvm-mc -filetype=obj -triple=arm64-apple-darwin %s -o %t/input.o -# RUN: %lld -arch arm64 -dead_strip -lSystem -U _extern_sym -o %t/thunk %t/input.o +# RUN: %lld -arch arm64 -dead_strip -lSystem -U _extern_sym -map %t/thunk.map -o %t/thunk %t/input.o # RUN: llvm-objdump --no-print-imm-hex -d --no-show-raw-insn %t/thunk | FileCheck %s +## Check that the thunks appear in the map file and that everything is sorted by address +# Because of the `.space` instructions, there will end up being a lot of dead symbols in the +# linker map (linker map will be ~2.7GB). So to avoid the test trying to (slowly) match regex +# across all the ~2.7GB of the linker map - generate a version of the linker map without dead symbols. +# RUN: awk '/# Dead Stripped Symbols:/ {exit} {print}' %t/thunk.map > %t/thunk_no_dead_syms.map + +# RUN: FileCheck %s --input-file %t/thunk_no_dead_syms.map --check-prefix=MAP + +# MAP: 0x{{[[:xdigit:]]+}} {{.*}} _b +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _c +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _d.thunk.0 +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _e.thunk.0 +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _f.thunk.0 +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _g.thunk.0 +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _h.thunk.0 +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} ___nan.thunk.0 +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _d +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _e +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _f +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _g +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _a.thunk.0 +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _b.thunk.0 +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _h +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _main +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _c.thunk.0 +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _d.thunk.1 +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _e.thunk.1 +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _f.thunk.1 +# MAP-NEXT: 0x{{[[:xdigit:]]+}} {{.*}} _z + + # CHECK: Disassembly of section __TEXT,__text: # CHECK: [[#%.13x, A_PAGE:]][[#%.3x, A_OFFSET:]] <_a>: