Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions lld/MachO/ConcatOutputSection.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(); }
Expand All @@ -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 *);
Expand All @@ -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<ConcatInputSection *> 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;

Expand Down
21 changes: 20 additions & 1 deletion lld/MachO/MapFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConcatInputSection *>
mergeOrderedInputs(ArrayRef<ConcatInputSection *> inputs1,
ArrayRef<ConcatInputSection *> inputs2) {
std::vector<ConcatInputSection *> 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;
Expand Down Expand Up @@ -220,7 +234,12 @@ 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<ConcatOutputSection>(osec)) {
const TextOutputSection *textOsec = dyn_cast<TextOutputSection>(osec);
if (textOsec && textOsec->getThunks().size()) {
auto inputsAndThunks =
mergeOrderedInputs(textOsec->inputs, textOsec->getThunks());
printIsecArrSyms(inputsAndThunks);
} else if (auto *concatOsec = dyn_cast<ConcatOutputSection>(osec)) {
printIsecArrSyms(concatOsec->inputs);
} else if (osec == in.cStringSection || osec == in.objcMethnameSection) {
const auto &liveCStrings = info.liveCStringsForSection.lookup(osec);
Expand Down
1 change: 1 addition & 0 deletions lld/MachO/OutputSection.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class OutputSection {
enum Kind {
ConcatKind,
SyntheticKind,
TextKind,
};

OutputSection(Kind kind, StringRef name) : name(name), sectionKind(kind) {}
Expand Down
33 changes: 32 additions & 1 deletion lld/test/MachO/arm64-thunks.s
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,45 @@
## (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
# so 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the point of this awk command. The last symbol you check is _z and then presumably there will be dead symbols after that. But the MAP check lines are done, so there is no problem?

Copy link
Contributor Author

@alx32 alx32 Dec 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that because the .space. commands, linker map ends up being 2.7GB in size - with apparently each character added via .space being interpreted as a dead symbol (this might be another unrelated issue).

So the issue is that if the test were to break, then it would take a very very long time to complete (I didn't wait to fully time it) - trying to match the regex against the 2.7GB file.

This is just an optimization to have the test complete in a practical amount of time - even in case of failure.


# RUN: FileCheck %s --input-file %t/thunk_no_dead_syms.map --check-prefix=MAP

# MAP: [[ADDR1:0x[0-9A-Fa-f]+]] {{.*}} _b
# MAP-NEXT: [[ADDR2:0x[0-9A-Fa-f]+]] {{.*}} _c
# MAP-NEXT: [[ADDR3:0x[0-9A-Fa-f]+]] {{.*}} _d.thunk.0
# MAP-NEXT: [[ADDR4:0x[0-9A-Fa-f]+]] {{.*}} _e.thunk.0
# MAP-NEXT: [[ADDR5:0x[0-9A-Fa-f]+]] {{.*}} _f.thunk.0
# MAP-NEXT: [[ADDR6:0x[0-9A-Fa-f]+]] {{.*}} _g.thunk.0
# MAP-NEXT: [[ADDR7:0x[0-9A-Fa-f]+]] {{.*}} _h.thunk.0
# MAP-NEXT: [[ADDR8:0x[0-9A-Fa-f]+]] {{.*}} ___nan.thunk.0
# MAP-NEXT: [[ADDR9:0x[0-9A-Fa-f]+]] {{.*}} _d
# MAP-NEXT: [[ADDR10:0x[0-9A-Fa-f]+]] {{.*}} _e
# MAP-NEXT: [[ADDR11:0x[0-9A-Fa-f]+]] {{.*}} _f
# MAP-NEXT: [[ADDR12:0x[0-9A-Fa-f]+]] {{.*}} _g
# MAP-NEXT: [[ADDR13:0x[0-9A-Fa-f]+]] {{.*}} _a.thunk.0
# MAP-NEXT: [[ADDR14:0x[0-9A-Fa-f]+]] {{.*}} _b.thunk.0
# MAP-NEXT: [[ADDR15:0x[0-9A-Fa-f]+]] {{.*}} _h
# MAP-NEXT: [[ADDR16:0x[0-9A-Fa-f]+]] {{.*}} _main
# MAP-NEXT: [[ADDR17:0x[0-9A-Fa-f]+]] {{.*}} _c.thunk.0
# MAP-NEXT: [[ADDR18:0x[0-9A-Fa-f]+]] {{.*}} _d.thunk.1
# MAP-NEXT: [[ADDR19:0x[0-9A-Fa-f]+]] {{.*}} _e.thunk.1
# MAP-NEXT: [[ADDR20:0x[0-9A-Fa-f]+]] {{.*}} _f.thunk.1
# MAP-NEXT: [[ADDR21:0x[0-9A-Fa-f]+]] {{.*}} _z


# CHECK: Disassembly of section __TEXT,__text:

# CHECK: [[#%.13x, A_PAGE:]][[#%.3x, A_OFFSET:]] <_a>:
Expand Down
Loading