Skip to content

Commit adef51a

Browse files
committed
[lld] Disable section merging only for non-trivial relocations
1 parent e4041b7 commit adef51a

File tree

6 files changed

+182
-111
lines changed

6 files changed

+182
-111
lines changed

lld/ELF/ICF.cpp

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,11 @@
7777
#include "InputFiles.h"
7878
#include "LinkerScript.h"
7979
#include "OutputSections.h"
80+
#include "Relocations.h"
8081
#include "SymbolTable.h"
8182
#include "Symbols.h"
8283
#include "SyntheticSections.h"
84+
#include "Target.h"
8385
#include "llvm/ADT/EquivalenceClasses.h"
8486
#include "llvm/BinaryFormat/ELF.h"
8587
#include "llvm/Object/ELF.h"
@@ -105,18 +107,23 @@ template <class ELFT> class ICF {
105107
void segregate(size_t begin, size_t end, uint32_t eqClassBase, bool constant);
106108

107109
template <class RelTy>
108-
bool constantEq(const InputSection *a, Relocs<RelTy> relsA,
109-
const InputSection *b, Relocs<RelTy> relsB);
110+
bool constantEq(InputSection *a, Relocs<RelTy> relsA, InputSection *b,
111+
Relocs<RelTy> relsB);
110112

111113
template <class RelTy>
112114
bool variableEq(const InputSection *a, Relocs<RelTy> relsA,
113115
const InputSection *b, Relocs<RelTy> relsB);
114116

115-
bool equalsConstant(const InputSection *a, const InputSection *b);
117+
bool equalsConstant(InputSection *a, InputSection *b);
116118
bool equalsVariable(const InputSection *a, const InputSection *b);
117119

118120
size_t findBoundary(size_t begin, size_t end);
119121

122+
// A relocation with side-effects is considered non-trivial. Eg: relocation
123+
// creates GOT entry or TLS slot.
124+
template <class RelTy>
125+
bool isTrivialRelocation(InputSection *a, Symbol &s, RelTy reloc);
126+
120127
void forEachClassRange(size_t begin, size_t end,
121128
llvm::function_ref<void(size_t, size_t)> fn);
122129

@@ -235,11 +242,30 @@ void ICF<ELFT>::segregate(size_t begin, size_t end, uint32_t eqClassBase,
235242
}
236243
}
237244

245+
template <class ELFT>
246+
template <class RelTy>
247+
bool ICF<ELFT>::isTrivialRelocation(InputSection *a, Symbol &s, RelTy reloc) {
248+
OffsetGetter getter(*a);
249+
uint64_t offset = getter.get(ctx, reloc.r_offset);
250+
RelExpr expr = ctx.target->getRelExpr(reloc.getType(ctx.arg.isMips64EL), s,
251+
a->content().data() + offset);
252+
253+
if (needsGot(expr) || needsTls(s, expr))
254+
return false;
255+
return true;
256+
}
257+
258+
// Two symbols referenced by relocations can be merged together safely
259+
// when their addends are same.
260+
static bool canMergeSymbols(uint64_t addA, uint64_t addB) {
261+
return addA == addB;
262+
}
263+
238264
// Compare two lists of relocations.
239265
template <class ELFT>
240266
template <class RelTy>
241-
bool ICF<ELFT>::constantEq(const InputSection *secA, Relocs<RelTy> ra,
242-
const InputSection *secB, Relocs<RelTy> rb) {
267+
bool ICF<ELFT>::constantEq(InputSection *secA, Relocs<RelTy> ra,
268+
InputSection *secB, Relocs<RelTy> rb) {
243269
if (ra.size() != rb.size())
244270
return false;
245271
auto rai = ra.begin(), rae = ra.end(), rbi = rb.begin();
@@ -287,13 +313,14 @@ bool ICF<ELFT>::constantEq(const InputSection *secA, Relocs<RelTy> ra,
287313
// Relocations referring to InputSections are constant-equal if their
288314
// section offsets are equal.
289315
if (isa<InputSection>(da->section)) {
290-
// Our symbol folding logic later merges symbols in two folded sections
291-
// We should not merge sections in the first place if their symbols
292-
// cannot be merged together.
293-
bool canMergeSymbols = addA == addB;
294-
if (da->value + addA == db->value + addB && canMergeSymbols)
316+
if (da->value + addA == db->value + addB) {
317+
// For non-trivial relocations, if we cannot merge symbols together,
318+
// we must not merge them.
319+
if (!isTrivialRelocation(secA, sa, *rai) &&
320+
!canMergeSymbols(addA, addB))
321+
return false;
295322
continue;
296-
return false;
323+
}
297324
}
298325

299326
// Relocations referring to MergeInputSections are constant-equal if their
@@ -319,7 +346,7 @@ bool ICF<ELFT>::constantEq(const InputSection *secA, Relocs<RelTy> ra,
319346
// Compare "non-moving" part of two InputSections, namely everything
320347
// except relocation targets.
321348
template <class ELFT>
322-
bool ICF<ELFT>::equalsConstant(const InputSection *a, const InputSection *b) {
349+
bool ICF<ELFT>::equalsConstant(InputSection *a, InputSection *b) {
323350
if (a->flags != b->flags || a->getSize() != b->getSize() ||
324351
a->content() != b->content())
325352
return false;
@@ -338,26 +365,27 @@ bool ICF<ELFT>::equalsConstant(const InputSection *a, const InputSection *b) {
338365
: constantEq(a, ra.relas, b, rb.relas);
339366
}
340367

341-
template <class RelTy>
342-
static SmallVector<Symbol *> getReloc(const InputSection *sec,
343-
Relocs<RelTy> relocs) {
344-
SmallVector<Symbol *> syms;
368+
template <class ELFT, class RelTy>
369+
static SmallVector<std::pair<Symbol *, uint64_t>>
370+
getReloc(const InputSection *sec, Relocs<RelTy> relocs) {
371+
SmallVector<std::pair<Symbol *, uint64_t>> syms;
345372
for (auto ri = relocs.begin(), re = relocs.end(); ri != re; ++ri) {
346373
Symbol &sym = sec->file->getRelocTargetSym(*ri);
347-
syms.push_back(&sym);
374+
syms.emplace_back(&sym, getAddend<ELFT>(*ri));
348375
}
349376
return syms;
350377
}
351378

352379
template <class ELFT>
353-
static SmallVector<Symbol *> getRelocTargetSyms(const InputSection *sec) {
380+
static SmallVector<std::pair<Symbol *, uint64_t>>
381+
getRelocTargetSyms(const InputSection *sec) {
354382
const RelsOrRelas<ELFT> rel = sec->template relsOrRelas<ELFT>();
355383
if (rel.areRelocsCrel())
356-
return getReloc(sec, rel.crels);
384+
return getReloc<ELFT>(sec, rel.crels);
357385
if (rel.areRelocsRel())
358-
return getReloc(sec, rel.rels);
386+
return getReloc<ELFT>(sec, rel.rels);
359387

360-
return getReloc(sec, rel.relas);
388+
return getReloc<ELFT>(sec, rel.relas);
361389
}
362390

363391
// Compare two lists of relocations. Returns true if all pairs of
@@ -572,19 +600,21 @@ template <class ELFT> void ICF<ELFT>::run() {
572600
if (end - begin == 1)
573601
return;
574602
print() << "selected section " << sections[begin];
575-
SmallVector<Symbol *> syms = getRelocTargetSyms<ELFT>(sections[begin]);
603+
SmallVector<std::pair<Symbol *, uint64_t>> syms =
604+
getRelocTargetSyms<ELFT>(sections[begin]);
576605
for (size_t i = begin + 1; i < end; ++i) {
577606
print() << " removing identical section " << sections[i];
578607
sections[begin]->replace(sections[i]);
579-
SmallVector<Symbol *> replacedSyms =
608+
SmallVector<std::pair<Symbol *, uint64_t>> replacedSyms =
580609
getRelocTargetSyms<ELFT>(sections[i]);
581610
assert(syms.size() == replacedSyms.size() &&
582611
"Should have same number of syms!");
583612
for (size_t i = 0; i < syms.size(); i++) {
584-
if (syms[i] == replacedSyms[i] || !syms[i]->isGlobal() ||
585-
!replacedSyms[i]->isGlobal())
613+
if (syms[i].first == replacedSyms[i].first ||
614+
!syms[i].first->isGlobal() || !replacedSyms[i].first->isGlobal() ||
615+
!canMergeSymbols(syms[i].second, replacedSyms[i].second))
586616
continue;
587-
symbolEquivalence.unionSets(syms[i], replacedSyms[i]);
617+
symbolEquivalence.unionSets(syms[i].first, replacedSyms[i].first);
588618
}
589619

590620
// At this point we know sections merged are fully identical and hence

lld/ELF/InputSection.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,49 @@ class SyntheticSection : public InputSection {
514514
}
515515
};
516516

517+
class OffsetGetter {
518+
public:
519+
OffsetGetter() = default;
520+
explicit OffsetGetter(InputSectionBase &sec) {
521+
if (auto *eh = dyn_cast<EhInputSection>(&sec)) {
522+
cies = eh->cies;
523+
fdes = eh->fdes;
524+
i = cies.begin();
525+
j = fdes.begin();
526+
}
527+
}
528+
529+
// Translates offsets in input sections to offsets in output sections.
530+
// Given offset must increase monotonically. We assume that Piece is
531+
// sorted by inputOff.
532+
uint64_t get(Ctx &ctx, uint64_t off) {
533+
if (cies.empty())
534+
return off;
535+
536+
while (j != fdes.end() && j->inputOff <= off)
537+
++j;
538+
auto it = j;
539+
if (j == fdes.begin() || j[-1].inputOff + j[-1].size <= off) {
540+
while (i != cies.end() && i->inputOff <= off)
541+
++i;
542+
if (i == cies.begin() || i[-1].inputOff + i[-1].size <= off) {
543+
Err(ctx) << ".eh_frame: relocation is not in any piece";
544+
return 0;
545+
}
546+
it = i;
547+
}
548+
549+
// Offset -1 means that the piece is dead (i.e. garbage collected).
550+
if (it[-1].outputOff == -1)
551+
return -1;
552+
return it[-1].outputOff + (off - it[-1].inputOff);
553+
}
554+
555+
private:
556+
ArrayRef<EhSectionPiece> cies, fdes;
557+
ArrayRef<EhSectionPiece>::iterator i, j;
558+
};
559+
517560
inline bool isStaticRelSecType(uint32_t type) {
518561
return type == llvm::ELF::SHT_RELA || type == llvm::ELF::SHT_CREL ||
519562
type == llvm::ELF::SHT_REL;

lld/ELF/Relocations.cpp

Lines changed: 1 addition & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -127,29 +127,6 @@ void elf::reportRangeError(Ctx &ctx, uint8_t *loc, int64_t v, int n,
127127
}
128128
}
129129

130-
// Build a bitmask with one bit set for each 64 subset of RelExpr.
131-
static constexpr uint64_t buildMask() { return 0; }
132-
133-
template <typename... Tails>
134-
static constexpr uint64_t buildMask(int head, Tails... tails) {
135-
return (0 <= head && head < 64 ? uint64_t(1) << head : 0) |
136-
buildMask(tails...);
137-
}
138-
139-
// Return true if `Expr` is one of `Exprs`.
140-
// There are more than 64 but less than 128 RelExprs, so we divide the set of
141-
// exprs into [0, 64) and [64, 128) and represent each range as a constant
142-
// 64-bit mask. Then we decide which mask to test depending on the value of
143-
// expr and use a simple shift and bitwise-and to test for membership.
144-
template <RelExpr... Exprs> static bool oneof(RelExpr expr) {
145-
assert(0 <= expr && (int)expr < 128 &&
146-
"RelExpr is too large for 128-bit mask!");
147-
148-
if (expr >= 64)
149-
return (uint64_t(1) << (expr - 64)) & buildMask((Exprs - 64)...);
150-
return (uint64_t(1) << expr) & buildMask(Exprs...);
151-
}
152-
153130
static RelType getMipsPairType(RelType type, bool isLocal) {
154131
switch (type) {
155132
case R_MIPS_HI16:
@@ -196,15 +173,6 @@ static bool needsPlt(RelExpr expr) {
196173
RE_PPC64_CALL_PLT>(expr);
197174
}
198175

199-
bool lld::elf::needsGot(RelExpr expr) {
200-
return oneof<R_GOT, RE_AARCH64_AUTH_GOT, RE_AARCH64_AUTH_GOT_PC, R_GOT_OFF,
201-
RE_MIPS_GOT_LOCAL_PAGE, RE_MIPS_GOT_OFF, RE_MIPS_GOT_OFF32,
202-
RE_AARCH64_GOT_PAGE_PC, RE_AARCH64_AUTH_GOT_PAGE_PC,
203-
RE_AARCH64_AUTH_GOT_PAGE_PC, R_GOT_PC, R_GOTPLT,
204-
RE_AARCH64_GOT_PAGE, RE_LOONGARCH_GOT, RE_LOONGARCH_GOT_PAGE_PC>(
205-
expr);
206-
}
207-
208176
// True if this expression is of the form Sym - X, where X is a position in the
209177
// file (PC, or GOT for example).
210178
static bool isRelExpr(RelExpr expr) {
@@ -403,49 +371,6 @@ template <class ELFT> static void addCopyRelSymbol(Ctx &ctx, SharedSymbol &ss) {
403371
//
404372
// For sections other than .eh_frame, this class doesn't do anything.
405373
namespace {
406-
class OffsetGetter {
407-
public:
408-
OffsetGetter() = default;
409-
explicit OffsetGetter(InputSectionBase &sec) {
410-
if (auto *eh = dyn_cast<EhInputSection>(&sec)) {
411-
cies = eh->cies;
412-
fdes = eh->fdes;
413-
i = cies.begin();
414-
j = fdes.begin();
415-
}
416-
}
417-
418-
// Translates offsets in input sections to offsets in output sections.
419-
// Given offset must increase monotonically. We assume that Piece is
420-
// sorted by inputOff.
421-
uint64_t get(Ctx &ctx, uint64_t off) {
422-
if (cies.empty())
423-
return off;
424-
425-
while (j != fdes.end() && j->inputOff <= off)
426-
++j;
427-
auto it = j;
428-
if (j == fdes.begin() || j[-1].inputOff + j[-1].size <= off) {
429-
while (i != cies.end() && i->inputOff <= off)
430-
++i;
431-
if (i == cies.begin() || i[-1].inputOff + i[-1].size <= off) {
432-
Err(ctx) << ".eh_frame: relocation is not in any piece";
433-
return 0;
434-
}
435-
it = i;
436-
}
437-
438-
// Offset -1 means that the piece is dead (i.e. garbage collected).
439-
if (it[-1].outputOff == -1)
440-
return -1;
441-
return it[-1].outputOff + (off - it[-1].inputOff);
442-
}
443-
444-
private:
445-
ArrayRef<EhSectionPiece> cies, fdes;
446-
ArrayRef<EhSectionPiece>::iterator i, j;
447-
};
448-
449374
// This class encapsulates states needed to scan relocations for one
450375
// InputSectionBase.
451376
class RelocationScanner {
@@ -1593,7 +1518,7 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
15931518
//
15941519
// Some RISCV TLSDESC relocations reference a local NOTYPE symbol,
15951520
// but we need to process them in handleTlsRelocation.
1596-
if (sym.isTls() || oneof<R_TLSDESC_PC, R_TLSDESC_CALL>(expr)) {
1521+
if (needsTls(sym, expr)) {
15971522
if (unsigned processed =
15981523
handleTlsRelocation(expr, type, offset, sym, addend)) {
15991524
i += processed - 1;

lld/ELF/Relocations.h

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLD_ELF_RELOCATIONS_H
1010
#define LLD_ELF_RELOCATIONS_H
1111

12+
#include "Symbols.h"
1213
#include "lld/Common/LLVM.h"
1314
#include "llvm/ADT/DenseMap.h"
1415
#include "llvm/ADT/STLExtras.h"
@@ -18,7 +19,6 @@
1819
namespace lld::elf {
1920
struct Ctx;
2021
class Defined;
21-
class Symbol;
2222
class InputSection;
2323
class InputSectionBase;
2424
class OutputSection;
@@ -355,6 +355,29 @@ inline Relocs<RelTy> sortRels(Relocs<RelTy> rels,
355355
return rels;
356356
}
357357

358+
// Build a bitmask with one bit set for each 64 subset of RelExpr.
359+
constexpr uint64_t buildMask() { return 0; }
360+
361+
template <typename... Tails>
362+
constexpr uint64_t buildMask(int head, Tails... tails) {
363+
return (0 <= head && head < 64 ? uint64_t(1) << head : 0) |
364+
buildMask(tails...);
365+
}
366+
367+
// Return true if `Expr` is one of `Exprs`.
368+
// There are more than 64 but less than 128 RelExprs, so we divide the set of
369+
// exprs into [0, 64) and [64, 128) and represent each range as a constant
370+
// 64-bit mask. Then we decide which mask to test depending on the value of
371+
// expr and use a simple shift and bitwise-and to test for membership.
372+
template <RelExpr... Exprs> static bool oneof(RelExpr expr) {
373+
assert(0 <= expr && (int)expr < 128 &&
374+
"RelExpr is too large for 128-bit mask!");
375+
376+
if (expr >= 64)
377+
return (uint64_t(1) << (expr - 64)) & buildMask((Exprs - 64)...);
378+
return (uint64_t(1) << expr) & buildMask(Exprs...);
379+
}
380+
358381
template <bool is64>
359382
inline Relocs<llvm::object::Elf_Crel_Impl<is64>>
360383
sortRels(Relocs<llvm::object::Elf_Crel_Impl<is64>> rels,
@@ -367,7 +390,19 @@ RelocationBaseSection &getIRelativeSection(Ctx &ctx);
367390
// Returns true if Expr refers a GOT entry. Note that this function returns
368391
// false for TLS variables even though they need GOT, because TLS variables uses
369392
// GOT differently than the regular variables.
370-
bool needsGot(RelExpr expr);
393+
inline bool needsGot(RelExpr expr) {
394+
return oneof<R_GOT, RE_AARCH64_AUTH_GOT, RE_AARCH64_AUTH_GOT_PC, R_GOT_OFF,
395+
RE_MIPS_GOT_LOCAL_PAGE, RE_MIPS_GOT_OFF, RE_MIPS_GOT_OFF32,
396+
RE_AARCH64_GOT_PAGE_PC, RE_AARCH64_AUTH_GOT_PAGE_PC,
397+
RE_AARCH64_AUTH_GOT_PAGE_PC, R_GOT_PC, R_GOTPLT,
398+
RE_AARCH64_GOT_PAGE, RE_LOONGARCH_GOT, RE_LOONGARCH_GOT_PAGE_PC>(
399+
expr);
400+
}
401+
402+
inline bool needsTls(Symbol &s, RelExpr expr) {
403+
return s.isTls() || oneof<R_TLSDESC_PC, R_TLSDESC_CALL>(expr);
404+
}
405+
371406
} // namespace lld::elf
372407

373408
#endif

lld/ELF/Target.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "Target.h"
2727
#include "InputFiles.h"
2828
#include "OutputSections.h"
29+
#include "Relocations.h"
2930
#include "SymbolTable.h"
3031
#include "Symbols.h"
3132
#include "SyntheticSections.h"

0 commit comments

Comments
 (0)