Skip to content

[lld] Merge GOT entries for symbols #131630

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
14 changes: 5 additions & 9 deletions lld/ELF/ICF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,15 +262,11 @@ bool ICF<ELFT>::constantEq(const InputSection *secA, Relocs<RelTy> ra,
auto *db = dyn_cast<Defined>(&sb);

// Placeholder symbols generated by linker scripts look the same now but
// may have different values later.
if (!da || !db || da->scriptDefined || db->scriptDefined)
return false;

// When comparing a pair of relocations, if they refer to different symbols,
// and either symbol is preemptible, the containing sections should be
// considered different. This is because even if the sections are identical
// in this DSO, they may not be after preemption.
if (da->isPreemptible || db->isPreemptible)
// may have different values later. Similarly, preemptible symbols may be
// different after preemption. When comparing a pair of relocations, if they
// refer to different symbols, the containing sections should be considered
// different.
if (!da || !db || !da->isFoldable() || !db->isFoldable())
return false;

// Relocations referring to absolute symbols are constant-equal if their
Expand Down
3 changes: 1 addition & 2 deletions lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -915,8 +915,7 @@ void elf::addGotEntry(Ctx &ctx, Symbol &sym) {
}

static void addGotAuthEntry(Ctx &ctx, Symbol &sym) {
ctx.in.got->addEntry(sym);
ctx.in.got->addAuthEntry(sym);
ctx.in.got->addEntry(sym, /*authEntry = */ true);
uint64_t off = sym.getGotOffset(ctx);

// If preemptible, emit a GLOB_DAT relocation.
Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ class Symbol {
}

uint8_t computeBinding(Ctx &) const;
// Preemptible and script defined symbols cannot ever be treated same
bool isFoldable() const { return !isPreemptible && !scriptDefined; }
bool isGlobal() const { return binding == llvm::ELF::STB_GLOBAL; }
bool isWeak() const { return binding == llvm::ELF::STB_WEAK; }

Expand Down
28 changes: 23 additions & 5 deletions lld/ELF/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,13 +665,31 @@ GotSection::GotSection(Ctx &ctx)
}

void GotSection::addConstant(const Relocation &r) { relocations.push_back(r); }
void GotSection::addEntry(const Symbol &sym) {
void GotSection::addEntry(const Symbol &sym, bool authEntry) {
assert(sym.auxIdx == ctx.symAux.size() - 1);
ctx.symAux.back().gotIdx = numEntries++;
}
auto *d = dyn_cast<Defined>(&sym);
std::optional<uint32_t> finalGotIdx;
if (d && d->isFoldable()) {
// Generate one GOT entry for all foldable symbols. This could be due to
// ICF where containing sections have now been folded into one, or aliases
// that all point to the same symbol.
auto [it, inserted] = gotEntries.insert(std::make_pair(
std::make_tuple(d->section, d->value, sym.type), numEntries));
if (!inserted) {
finalGotIdx = it->getSecond();
}
}

if (!finalGotIdx.has_value()) {
Copy link
Member

Choose a reason for hiding this comment

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

we omit braces for single-line simple statements. the code style is concise. we don't add many blank lines.

finalGotIdx = numEntries++;
}

if (authEntry) {
authEntries.push_back(
{finalGotIdx.value() * ctx.arg.wordsize, sym.isFunc()});
}

void GotSection::addAuthEntry(const Symbol &sym) {
authEntries.push_back({(numEntries - 1) * ctx.arg.wordsize, sym.isFunc()});
ctx.symAux.back().gotIdx = finalGotIdx.value();
}

bool GotSection::addTlsDescEntry(const Symbol &sym) {
Expand Down
11 changes: 9 additions & 2 deletions lld/ELF/SyntheticSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@ class GotSection final : public SyntheticSection {
void writeTo(uint8_t *buf) override;

void addConstant(const Relocation &r);
void addEntry(const Symbol &sym);
void addAuthEntry(const Symbol &sym);
void addEntry(const Symbol &sym, bool authEntry = false);
bool addTlsDescEntry(const Symbol &sym);
void addTlsDescAuthEntry();
bool addDynTlsEntry(const Symbol &sym);
Expand All @@ -138,6 +137,14 @@ class GotSection final : public SyntheticSection {
bool isSymbolFunc;
};
SmallVector<AuthEntryInfo, 0> authEntries;

// Map of GOT entries keyed by section, offset, and type. The purpose is to
Copy link
Collaborator

Choose a reason for hiding this comment

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

How do you propose targets override this to require additional properties be the same? For CHERI targets (currently downstream, but eventually to be upstreamed) we would also need to ensure the symbol size was the same, since that determines the bounds for the GOT entry.

Copy link
Collaborator

Choose a reason for hiding this comment

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

(along with additional complexity for handling "zero"-sized symbols to ensure that section start symbols get the bounds of the whole section, rather than be 0 bytes as they would otherwise be)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I imagine in that case the second element of this tuple would evolve into something more complex. Right now it's just the offset. For CHERI targets, it would include size and other things necessary -- first and third element would still be the same.

Copy link
Collaborator

Choose a reason for hiding this comment

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

But how does that work when the type of the map needs to change? GotSection isn't templated on the target (and refactoring to do so would be a real pain I imagine).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we would be okay if you add another member to the tuple named size (or just create a new struct) and populate that from Symbol::getSize() in GotSection::addEntry(). The original problem we are trying to solve (as mentioned in #129122) would still work.

// reuse GOT entries when multiple same-type, foldable symbols refer to the
// image location. In general, this is a GOT-size optimization, but it is
// also required for some cases involving multi-instruction GOT access
// patterns and ICF.
llvm::DenseMap<std::tuple<SectionBase *, uint64_t, unsigned char>, uint32_t>
gotEntries;
};

// .note.GNU-stack section.
Expand Down
68 changes: 68 additions & 0 deletions lld/test/ELF/aarch64-got-merging-icf.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// REQUIRES: aarch64

# RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t
# RUN: ld.lld %t -o %t2 --icf=all
# RUN: llvm-objdump --section-headers %t2 | FileCheck %s --check-prefix=EXE

# RUN: ld.lld -shared %t -o %t3 --icf=all
# RUN: llvm-objdump --section-headers %t3 | FileCheck %s --check-prefix=DSO

## All .rodata.* sections should merge into a single GOT entry
# EXE: {{.*}}.got 00000008{{.*}}

## When symbols are preemptible in DSO mode, GOT entries wouldn't be merged
# DSO: {{.*}}.got 00000020{{.*}}

.addrsig

callee:
ret

.section .rodata.dummy1,"a",@progbits
sym1:
.long 111
.long 122
.byte 123

.section .rodata.dummy2,"a",@progbits
sym2:
.long 111
.long 122
sym3:
.byte 123

.macro f, index

.section .text.f1_\index,"ax",@progbits
f1_\index:
adrp x0, :got:g\index
mov x1, #\index
b f2_\index

.section .text.f2_\index,"ax",@progbits
f2_\index:
ldr x0, [x0, :got_lo12:g\index]
b callee

.globl g\index
.section .rodata.g\index,"a",@progbits
g_\index:
.long 111
.long 122

g\index:
.byte 123

.section .text._start,"ax",@progbits
bl f1_\index

.endm

.section .text._start,"ax",@progbits
.globl _start
_start:

f 0
f 1
f 2
f 3