Skip to content
Closed
Show file tree
Hide file tree
Changes from 8 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
13 changes: 4 additions & 9 deletions lld/ELF/ICF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,15 +262,10 @@ 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 relocation, if they
// refer to different symbols, containing sections should be treated 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
7 changes: 5 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,10 @@ class GotSection final : public SyntheticSection {
bool isSymbolFunc;
};
SmallVector<AuthEntryInfo, 0> authEntries;

// To track GOT entries for symbols that may have been ICFed
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
Loading