Skip to content

Commit

Permalink
Merge .alpha_got with .got
Browse files Browse the repository at this point in the history
It's not only Alpha but MIPS relocations also need dynamic symbols
with addends, so we want to use this code not only for Alpha.
  • Loading branch information
rui314 committed Jul 14, 2023
1 parent 83524e3 commit 72671df
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 118 deletions.
70 changes: 2 additions & 68 deletions elf/arch-alpha.cc
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
break;
case R_ALPHA_LITERAL:
if (A)
*(ul16 *)loc = ctx.extra.got->get_addr(sym, A) - GP;
*(ul16 *)loc = ctx.got->get_gota_addr(ctx, &sym, A) - GP;
else
*(ul16 *)loc = GOT + G - GP;
break;
Expand Down Expand Up @@ -222,7 +222,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
break;
case R_ALPHA_LITERAL:
if (rel.r_addend)
ctx.extra.got->add_symbol(sym, rel.r_addend);
ctx.got->add_gota_symbol(ctx, &sym, rel.r_addend);
else
sym.flags |= NEEDS_GOT;
break;
Expand Down Expand Up @@ -261,70 +261,4 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
}
}

// An R_ALPHA_LITERAL relocation may request the linker to create a GOT
// entry for an external symbol with a non-zero addend. This is an unusual
// request which is not found in any other targets.
//
// Referring an external symbol with a non-zero addend is a bad practice
// because we need to create as many dynamic relocations as the number of
// distinctive addends for the same symbol.
//
// We don't want to mess up the implementation of the common GOT section
// for Alpha. So we create another GOT-like section, .alpha_got. Any GOT
// entry for an R_ALPHA_LITERAL reloc with a non-zero addend is created
// not in .got but in .alpha_got.
//
// Since .alpha_got entries are accessed relative to GP, .alpha_got
// needs to be close enough to .got. It's actually placed next to .got.
void AlphaGotSection::add_symbol(Symbol<E> &sym, i64 addend) {
assert(addend);
std::scoped_lock lock(mu);
entries.push_back({&sym, addend});
}

bool operator<(const AlphaGotSection::Entry &a, const AlphaGotSection::Entry &b) {
return std::tuple(a.sym->file->priority, a.sym->sym_idx, a.addend) <
std::tuple(b.sym->file->priority, b.sym->sym_idx, b.addend);
};

u64 AlphaGotSection::get_addr(Symbol<E> &sym, i64 addend) {
auto it = std::lower_bound(entries.begin(), entries.end(), Entry{&sym, addend});
assert(it != entries.end());
return this->shdr.sh_addr + (it - entries.begin()) * sizeof(Word<E>);
}

i64 AlphaGotSection::get_reldyn_size(Context<E> &ctx) const {
i64 n = 0;
for (const Entry &e : entries)
if (e.sym->is_imported || (ctx.arg.pic && !e.sym->is_absolute()))
n++;
return n;
}

void AlphaGotSection::finalize() {
sort(entries);
remove_duplicates(entries);
shdr.sh_size = entries.size() * sizeof(Word<E>);
}

void AlphaGotSection::copy_buf(Context<E> &ctx) {
ElfRel<E> *dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
reldyn_offset);

for (i64 i = 0; i < entries.size(); i++) {
Entry &e = entries[i];
u64 P = this->shdr.sh_addr + sizeof(Word<E>) * i;
ul64 *buf = (ul64 *)(ctx.buf + this->shdr.sh_offset + sizeof(Word<E>) * i);

if (e.sym->is_imported) {
*buf = ctx.arg.apply_dynamic_relocs ? e.addend : 0;
*dynrel++ = ElfRel<E>(P, E::R_ABS, e.sym->get_dynsym_idx(ctx), e.addend);
} else {
*buf = e.sym->get_addr(ctx) + e.addend;
if (ctx.arg.pic && !e.sym->is_absolute())
*dynrel++ = ElfRel<E>(P, E::R_RELATIVE, 0, *buf);
}
}
}

} // namespace mold::elf
57 changes: 18 additions & 39 deletions elf/mold.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,14 @@ class OutputSection : public Chunk<E> {
std::unique_ptr<RelocSection<E>> reloc_sec;
};

template <typename E>
struct SymbolAddend {
bool operator==(const SymbolAddend &) const = default;
bool operator<(const SymbolAddend &) const;
Symbol<E> *sym;
i64 addend;
};

template <typename E>
class GotSection : public Chunk<E> {
public:
Expand All @@ -482,19 +490,17 @@ class GotSection : public Chunk<E> {
this->shdr.sh_type = SHT_PROGBITS;
this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE;
this->shdr.sh_addralign = sizeof(Word<E>);

// We always create a .got so that _GLOBAL_OFFSET_TABLE_ has
// something to point to. s390x psABI defines GOT[1] as a
// reserved slot, so we allocate one more on s390x.
this->shdr.sh_size = (is_s390x<E> ? 2 : 1) * sizeof(Word<E>);
this->shdr.sh_size = NUM_RESERVED * sizeof(Word<E>);
}

void add_got_symbol(Context<E> &ctx, Symbol<E> *sym);
void add_gota_symbol(Context<E> &ctx, Symbol<E> *sym, i64 addend);
void add_gottp_symbol(Context<E> &ctx, Symbol<E> *sym);
void add_tlsgd_symbol(Context<E> &ctx, Symbol<E> *sym);
void add_tlsdesc_symbol(Context<E> &ctx, Symbol<E> *sym);
void add_tlsld(Context<E> &ctx);

u64 get_gota_addr(Context<E> &ctx, Symbol<E> *sym, i64 addend) const;
u64 get_tlsld_addr(Context<E> &ctx) const;
bool has_tlsld(Context<E> &ctx) const { return tlsld_idx != -1; }
i64 get_reldyn_size(Context<E> &ctx) const override;
Expand All @@ -504,13 +510,20 @@ class GotSection : public Chunk<E> {
void populate_symtab(Context<E> &ctx) override;

std::vector<Symbol<E> *> got_syms;
std::vector<SymbolAddend<E>> gota_syms;
std::vector<Symbol<E> *> gottp_syms;
std::vector<Symbol<E> *> tlsgd_syms;
std::vector<Symbol<E> *> tlsdesc_syms;
u32 tlsld_idx = -1;
std::mutex mu;

void construct_relr(Context<E> &ctx);
std::vector<u64> relr;

// We always create a .got so that _GLOBAL_OFFSET_TABLE_ has
// something to point to. s390x psABI defines GOT[1] as a
// reserved slot, so we allocate one more on s390x.
static constexpr i64 NUM_RESERVED = is_s390x<E> ? 2 : 1;
};

template <typename E>
Expand Down Expand Up @@ -1499,36 +1512,6 @@ class SparcTlsGetAddrSection : public Chunk<SPARC64> {
void copy_buf(Context<SPARC64> &ctx) override;
};

//
// arch-alpha.cc
//

class AlphaGotSection : public Chunk<ALPHA> {
public:
AlphaGotSection() {
this->name = ".alpha_got";
this->shdr.sh_type = SHT_PROGBITS;
this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE;
this->shdr.sh_addralign = 8;
}

void add_symbol(Symbol<ALPHA> &sym, i64 addend);
void finalize();
u64 get_addr(Symbol<ALPHA> &sym, i64 addend);
i64 get_reldyn_size(Context<ALPHA> &ctx) const override;
void copy_buf(Context<ALPHA> &ctx) override;

struct Entry {
bool operator==(const Entry &) const = default;
Symbol<ALPHA> *sym;
i64 addend;
};

private:
std::vector<Entry> entries;
std::mutex mu;
};

//
// main.cc
//
Expand Down Expand Up @@ -1603,10 +1586,6 @@ template <> struct ContextExtras<SPARC64> {
Symbol<SPARC64> *tls_get_addr_sym = nullptr;
};

template <> struct ContextExtras<ALPHA> {
AlphaGotSection *got = nullptr;
};

// Context represents a context object for each invocation of the linker.
// It contains command line flags, pointers to singleton objects
// (such as linker-synthesized output sections), unique_ptrs for
Expand Down
52 changes: 50 additions & 2 deletions elf/output-chunks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,7 @@ bool is_relro(Context<E> &ctx, Chunk<E> *chunk) {
chunk == ctx.got || chunk == ctx.dynamic ||
chunk == ctx.relro_padding ||
(ctx.arg.z_now && ctx.gotplt && chunk == ctx.gotplt) ||
chunk->name == ".alpha_got" || chunk->name == ".toc" ||
chunk->name.ends_with(".rel.ro");
chunk->name == ".toc" || chunk->name.ends_with(".rel.ro");
return false;
}

Expand Down Expand Up @@ -1062,13 +1061,26 @@ void OutputSection<E>::populate_symtab(Context<E> &ctx) {
}
}

template <typename E>
bool SymbolAddend<E>::operator<(const SymbolAddend<E> &other) const {
return std::tuple(sym->file->priority, sym->sym_idx, addend) <
std::tuple(other.sym->file->priority, other.sym->sym_idx, other.addend);
}

template <typename E>
void GotSection<E>::add_got_symbol(Context<E> &ctx, Symbol<E> *sym) {
sym->set_got_idx(ctx, this->shdr.sh_size / sizeof(Word<E>));
this->shdr.sh_size += sizeof(Word<E>);
got_syms.push_back(sym);
}

template <typename E>
void GotSection<E>::add_gota_symbol(Context<E> &ctx, Symbol<E> *sym, i64 addend) {
assert(addend != 0);
std::scoped_lock lock(mu);
gota_syms.push_back({sym, addend});
}

template <typename E>
void GotSection<E>::add_gottp_symbol(Context<E> &ctx, Symbol<E> *sym) {
sym->set_gottp_idx(ctx, this->shdr.sh_size / sizeof(Word<E>));
Expand Down Expand Up @@ -1102,6 +1114,16 @@ void GotSection<E>::add_tlsld(Context<E> &ctx) {
this->shdr.sh_size += sizeof(Word<E>) * 2;
}

template <typename E>
u64 GotSection<E>::get_gota_addr(Context<E> &ctx, Symbol<E> *sym,
i64 addend) const {
auto it = std::lower_bound(gota_syms.begin(), gota_syms.end(),
SymbolAddend<E>{sym, addend});
assert(it != gota_syms.end());
i64 idx = it - gota_syms.begin() + NUM_RESERVED;
return this->shdr.sh_addr + idx * sizeof(Word<E>);
}

template <typename E>
u64 GotSection<E>::get_tlsld_addr(Context<E> &ctx) const {
assert(tlsld_idx != -1);
Expand Down Expand Up @@ -1139,6 +1161,32 @@ template <typename E>
static std::vector<GotEntry<E>> get_got_entries(Context<E> &ctx) {
std::vector<GotEntry<E>> entries;

// Create GOT entries for symbols with addends
for (i64 i = 0; i < ctx.got->gota_syms.size(); i++) {
SymbolAddend<E> &ent = ctx.got->gota_syms[i];
Symbol<E> *sym = ent.sym;
u64 addend = ent.addend;
i64 idx = GotSection<E>::NUM_RESERVED + i;

// If a symbol is imported, let the dynamic linker to resolve it.
if (sym->is_imported) {
entries.push_back({idx, addend, E::R_GLOB_DAT, sym});
continue;
}

// We do not allow IFUNC with addend because referring to a middle of
// a function doesn't make sense.
if constexpr (supports_ifunc<E>)
assert(!sym->is_ifunc());

// If we know an address at link-time, fill that GOT entry now.
// It may need a base relocation, though.
if (ctx.arg.pic && sym->is_relative())
entries.push_back({idx, sym->get_addr(ctx, NO_PLT) + addend, E::R_RELATIVE});
else
entries.push_back({idx, sym->get_addr(ctx, NO_PLT) + addend});
}

// Create GOT entries for ordinary symbols
for (Symbol<E> *sym : ctx.got->got_syms) {
i64 idx = sym->get_got_idx(ctx);
Expand Down
15 changes: 6 additions & 9 deletions elf/passes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,6 @@ void create_synthetic_sections(Context<E> &ctx) {
ctx.extra.tls_get_addr_sym = get_symbol(ctx, "__tls_get_addr");
}

if constexpr (is_alpha<E>)
ctx.extra.got = push(new AlphaGotSection);

// If .dynamic exists, .dynsym and .dynstr must exist as well
// since .dynamic refers them.
if (ctx.dynamic) {
Expand Down Expand Up @@ -1343,6 +1340,12 @@ void scan_relocations(Context<E> &ctx) {
vec[i].push_back(sym);
});

// Handle GOT-generating relocations with addends
sort(ctx.got->gota_syms);
remove_duplicates(ctx.got->gota_syms);
ctx.got->shdr.sh_size += ctx.got->gota_syms.size() * sizeof(Word<E>);

// Handle GOT-generating relocations without addends
std::vector<Symbol<E> *> syms = flatten(vec);
ctx.symbol_aux.reserve(syms.size());

Expand Down Expand Up @@ -1399,9 +1402,6 @@ void scan_relocations(Context<E> &ctx) {
if (ctx.needs_tlsld)
ctx.got->add_tlsld(ctx);

if constexpr (is_alpha<E>)
ctx.extra.got->finalize();

if (ctx.has_textrel && ctx.arg.warn_textrel)
Warn(ctx) << "creating a DT_TEXTREL in an output file";
}
Expand Down Expand Up @@ -1744,7 +1744,6 @@ void clear_padding(Context<E> &ctx) {
// <writable RELRO data>
// .got
// .toc
// .alpha_got
// <writable RELRO bss>
// .relro_padding
// <writable non-RELRO data>
Expand Down Expand Up @@ -1828,8 +1827,6 @@ void sort_output_sections_regular(Context<E> &ctx) {
return 1;
if (chunk->name == ".toc")
return 2;
if (chunk->name == ".alpha_got")
return 3;
if (chunk == ctx.relro_padding)
return INT_MAX;
return 0;
Expand Down

0 comments on commit 72671df

Please sign in to comment.