diff --git a/elf/arch-alpha.cc b/elf/arch-alpha.cc index d96a80875c..0865d47b4c 100644 --- a/elf/arch-alpha.cc +++ b/elf/arch-alpha.cc @@ -110,7 +110,7 @@ void InputSection::apply_reloc_alloc(Context &ctx, u8 *base) { break; case R_ALPHA_LITERAL: if (A) - *(ul16 *)loc = ctx.got->get_gota_addr(ctx, &sym, A) - GP; + *(ul16 *)loc = ctx.extra.got->get_addr(sym, A) - GP; else *(ul16 *)loc = GOT + G - GP; break; @@ -222,7 +222,7 @@ void InputSection::scan_relocations(Context &ctx) { break; case R_ALPHA_LITERAL: if (rel.r_addend) - ctx.got->add_gota_symbol(ctx, &sym, rel.r_addend); + ctx.extra.got->add_symbol(sym, rel.r_addend); else sym.flags |= NEEDS_GOT; break; @@ -261,4 +261,70 @@ void InputSection::scan_relocations(Context &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 &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 &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); +} + +i64 AlphaGotSection::get_reldyn_size(Context &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); +} + +void AlphaGotSection::copy_buf(Context &ctx) { + ElfRel *dynrel = (ElfRel *)(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) * i; + ul64 *buf = (ul64 *)(ctx.buf + this->shdr.sh_offset + sizeof(Word) * i); + + if (e.sym->is_imported) { + *buf = ctx.arg.apply_dynamic_relocs ? e.addend : 0; + *dynrel++ = ElfRel(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(P, E::R_RELATIVE, 0, *buf); + } + } +} + } // namespace mold::elf diff --git a/elf/mold.h b/elf/mold.h index b9d1cc250e..9724b3d91a 100644 --- a/elf/mold.h +++ b/elf/mold.h @@ -474,14 +474,6 @@ class OutputSection : public Chunk { std::unique_ptr> reloc_sec; }; -template -struct SymbolAddend { - bool operator==(const SymbolAddend &) const = default; - bool operator<(const SymbolAddend &) const; - Symbol *sym; - i64 addend; -}; - template class GotSection : public Chunk { public: @@ -490,17 +482,19 @@ class GotSection : public Chunk { this->shdr.sh_type = SHT_PROGBITS; this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE; this->shdr.sh_addralign = sizeof(Word); - this->shdr.sh_size = NUM_RESERVED * sizeof(Word); + + // 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 ? 2 : 1) * sizeof(Word); } void add_got_symbol(Context &ctx, Symbol *sym); - void add_gota_symbol(Context &ctx, Symbol *sym, i64 addend); void add_gottp_symbol(Context &ctx, Symbol *sym); void add_tlsgd_symbol(Context &ctx, Symbol *sym); void add_tlsdesc_symbol(Context &ctx, Symbol *sym); void add_tlsld(Context &ctx); - u64 get_gota_addr(Context &ctx, Symbol *sym, i64 addend) const; u64 get_tlsld_addr(Context &ctx) const; bool has_tlsld(Context &ctx) const { return tlsld_idx != -1; } i64 get_reldyn_size(Context &ctx) const override; @@ -510,20 +504,13 @@ class GotSection : public Chunk { void populate_symtab(Context &ctx) override; std::vector *> got_syms; - std::vector> gota_syms; std::vector *> gottp_syms; std::vector *> tlsgd_syms; std::vector *> tlsdesc_syms; u32 tlsld_idx = -1; - std::mutex mu; void construct_relr(Context &ctx); std::vector 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 ? 2 : 1; }; template @@ -1512,6 +1499,36 @@ class SparcTlsGetAddrSection : public Chunk { void copy_buf(Context &ctx) override; }; +// +// arch-alpha.cc +// + +class AlphaGotSection : public Chunk { +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 &sym, i64 addend); + void finalize(); + u64 get_addr(Symbol &sym, i64 addend); + i64 get_reldyn_size(Context &ctx) const override; + void copy_buf(Context &ctx) override; + + struct Entry { + bool operator==(const Entry &) const = default; + Symbol *sym; + i64 addend; + }; + +private: + std::vector entries; + std::mutex mu; +}; + // // main.cc // @@ -1586,6 +1603,10 @@ template <> struct ContextExtras { Symbol *tls_get_addr_sym = nullptr; }; +template <> struct ContextExtras { + 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 diff --git a/elf/output-chunks.cc b/elf/output-chunks.cc index d6fe8c1fd4..caf4ff830a 100644 --- a/elf/output-chunks.cc +++ b/elf/output-chunks.cc @@ -200,7 +200,8 @@ bool is_relro(Context &ctx, Chunk *chunk) { chunk == ctx.got || chunk == ctx.dynamic || chunk == ctx.relro_padding || (ctx.arg.z_now && ctx.gotplt && chunk == ctx.gotplt) || - chunk->name == ".toc" || chunk->name.ends_with(".rel.ro"); + chunk->name == ".alpha_got" || chunk->name == ".toc" || + chunk->name.ends_with(".rel.ro"); return false; } @@ -1061,12 +1062,6 @@ void OutputSection::populate_symtab(Context &ctx) { } } -template -bool SymbolAddend::operator<(const SymbolAddend &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 void GotSection::add_got_symbol(Context &ctx, Symbol *sym) { sym->set_got_idx(ctx, this->shdr.sh_size / sizeof(Word)); @@ -1074,13 +1069,6 @@ void GotSection::add_got_symbol(Context &ctx, Symbol *sym) { got_syms.push_back(sym); } -template -void GotSection::add_gota_symbol(Context &ctx, Symbol *sym, i64 addend) { - assert(addend != 0); - std::scoped_lock lock(mu); - gota_syms.push_back({sym, addend}); -} - template void GotSection::add_gottp_symbol(Context &ctx, Symbol *sym) { sym->set_gottp_idx(ctx, this->shdr.sh_size / sizeof(Word)); @@ -1114,16 +1102,6 @@ void GotSection::add_tlsld(Context &ctx) { this->shdr.sh_size += sizeof(Word) * 2; } -template -u64 GotSection::get_gota_addr(Context &ctx, Symbol *sym, - i64 addend) const { - auto it = std::lower_bound(gota_syms.begin(), gota_syms.end(), - SymbolAddend{sym, addend}); - assert(it != gota_syms.end()); - i64 idx = it - gota_syms.begin() + NUM_RESERVED; - return this->shdr.sh_addr + idx * sizeof(Word); -} - template u64 GotSection::get_tlsld_addr(Context &ctx) const { assert(tlsld_idx != -1); @@ -1161,32 +1139,6 @@ template static std::vector> get_got_entries(Context &ctx) { std::vector> entries; - // Create GOT entries for symbols with addends - for (i64 i = 0; i < ctx.got->gota_syms.size(); i++) { - SymbolAddend &ent = ctx.got->gota_syms[i]; - Symbol *sym = ent.sym; - u64 addend = ent.addend; - i64 idx = GotSection::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) - 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 *sym : ctx.got->got_syms) { i64 idx = sym->get_got_idx(ctx); @@ -3156,7 +3108,6 @@ template class OutputShdr; template class OutputPhdr; template class InterpSection; template class OutputSection; -template class SymbolAddend; template class GotSection; template class GotPltSection; template class PltSection; diff --git a/elf/passes.cc b/elf/passes.cc index 34d50252ca..ffa4e813ae 100644 --- a/elf/passes.cc +++ b/elf/passes.cc @@ -123,6 +123,9 @@ void create_synthetic_sections(Context &ctx) { ctx.extra.tls_get_addr_sym = get_symbol(ctx, "__tls_get_addr"); } + if constexpr (is_alpha) + ctx.extra.got = push(new AlphaGotSection); + // If .dynamic exists, .dynsym and .dynstr must exist as well // since .dynamic refers them. if (ctx.dynamic) { @@ -1340,12 +1343,6 @@ void scan_relocations(Context &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); - - // Handle GOT-generating relocations without addends std::vector *> syms = flatten(vec); ctx.symbol_aux.reserve(syms.size()); @@ -1402,6 +1399,9 @@ void scan_relocations(Context &ctx) { if (ctx.needs_tlsld) ctx.got->add_tlsld(ctx); + if constexpr (is_alpha) + ctx.extra.got->finalize(); + if (ctx.has_textrel && ctx.arg.warn_textrel) Warn(ctx) << "creating a DT_TEXTREL in an output file"; } @@ -1744,6 +1744,7 @@ void clear_padding(Context &ctx) { // // .got // .toc +// .alpha_got // // .relro_padding // @@ -1827,6 +1828,8 @@ void sort_output_sections_regular(Context &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;