#pragma once #include "elf.h" #include "../mold.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define XXH_INLINE_ALL 1 #include namespace mold::elf { static constexpr i32 SHA256_SIZE = 32; template class InputFile; template class InputSection; template class MergedSection; template class ObjectFile; template class Chunk; template class OutputSection; template class SharedFile; template class Symbol; template struct CieRecord; template struct Context; template struct FdeRecord; template class RChunk; template class ROutputEhdr; template class ROutputShdr; template class RStrtabSection; template class RSymtabSection; template std::ostream &operator<<(std::ostream &out, const Symbol &sym); class HashCmp { public: static size_t hash(const std::string_view &k) { return XXH3_64bits(k.data(), k.size()); } static bool equal(const std::string_view &k1, const std::string_view &k2) { return k1 == k2; } }; // // Mergeable section fragments // template struct SectionFragment { SectionFragment(MergedSection *sec) : output_section(*sec) {} SectionFragment(const SectionFragment &other) : output_section(other.output_section), offset(other.offset), p2align(other.p2align.load()), is_alive(other.is_alive.load()) {} u64 get_addr(Context &ctx) const; MergedSection &output_section; u32 offset = -1; std::atomic_uint8_t p2align = 0; std::atomic_bool is_alive = false; }; template struct SectionFragmentRef { SectionFragment *frag = nullptr; i32 idx = 0; i32 addend = 0; }; // Additional class members for dynamic symbols. Because most symbols // don't need them and we allocate tens of millions of symbol objects // for large programs, we separate them from `Symbol` class to save // memory. struct SymbolAux { i32 got_idx = -1; i32 gotplt_idx = -1; i32 gottp_idx = -1; i32 tlsgd_idx = -1; i32 tlsdesc_idx = -1; i32 plt_idx = -1; i32 pltgot_idx = -1; i32 dynsym_idx = -1; }; inline u64 hash_string(std::string_view str) { return XXH3_64bits(str.data(), str.size()); } // // input-sections.cc // // .eh_frame section contains CIE and FDE records to teach the runtime // how to handle exceptions. Usually, a .eh_frame contains one CIE // followed by as many FDEs as the number of functions defined by the // file. CIE contains common information for FDEs (it is actually // short for Common Information Entry). FDE contains the start address // of a function and its length as well as how to handle exceptions // for that function. // // Unlike other sections, the linker has to parse .eh_frame for optimal // output for the following reasons: // // - Compilers tend to emit the same CIE as long as the programming // language is the same, so CIEs in input object files are almost // always identical. We want to merge them to make a resulting // .eh_frame smaller. // // - If we eliminate a function (e.g. when we see two object files // containing the duplicate definition of an inlined function), we // want to also eliminate a corresponding FDE so that a resulting // .eh_frame doesn't contain a dead FDE entry. // // - If we need to compare two function definitions for equality for // ICF, we need to compare not only the function body but also its // exception handlers. // // Note that we assume that the first relocation entry for an FDE // always points to the function that the FDE is associated to. template struct CieRecord { CieRecord(Context &ctx, ObjectFile &file, InputSection &isec, u32 input_offset, std::span> rels, u32 rel_idx) : file(file), input_section(isec), input_offset(input_offset), rel_idx(rel_idx), rels(rels), contents(file.get_string(ctx, isec.shdr())) {} i64 size() const { return *(u32 *)(contents.data() + input_offset) + 4; } std::string_view get_contents() const { return contents.substr(input_offset, size()); } std::span> get_rels() const { i64 end = rel_idx; while (end < rels.size() && rels[end].r_offset < input_offset + size()) end++; return rels.subspan(rel_idx, end - rel_idx); } bool equals(const CieRecord &other) const; ObjectFile &file; InputSection &input_section; u32 input_offset = -1; u32 output_offset = -1; u32 rel_idx = -1; u32 icf_idx = -1; bool is_leader = false; std::span> rels; std::string_view contents; }; template struct FdeRecord { FdeRecord(u32 input_offset, u32 rel_idx) : input_offset(input_offset), rel_idx(rel_idx) {} FdeRecord(const FdeRecord &other) : input_offset(other.input_offset), output_offset(other.output_offset), rel_idx(other.rel_idx), cie_idx(other.cie_idx), is_alive(other.is_alive.load()) {} FdeRecord &operator=(const FdeRecord &other) { input_offset = other.input_offset; output_offset = other.output_offset; rel_idx = other.rel_idx; cie_idx = other.cie_idx; is_alive = other.is_alive.load(); return *this; } i64 size(ObjectFile &file) const; std::string_view get_contents(ObjectFile &file) const; std::span> get_rels(ObjectFile &file) const; u32 input_offset = -1; u32 output_offset = -1; u32 rel_idx = -1; u16 cie_idx = -1; std::atomic_bool is_alive = true; }; template class RangeExtensionThunk {}; template <> class RangeExtensionThunk { public: RangeExtensionThunk(OutputSection &osec) : output_section(osec) {} i64 size() const { return symbols.size() * ENTRY_SIZE; } u64 get_addr(i64 idx) const; void copy_buf(Context &ctx); static constexpr i64 ENTRY_SIZE = 12; OutputSection &output_section; i32 thunk_idx = -1; i64 offset = -1; std::mutex mu; std::vector *> symbols; std::vector symbol_map; std::unique_ptr used; }; struct RangeExtensionRef { i32 thunk_idx = -1; i32 sym_idx = -1; }; template struct InputSectionExtra { InputSectionExtra() = default; InputSectionExtra(const InputSectionExtra &other) : is_visited(other.is_visited.load()), leader(other.leader), icf_idx(other.icf_idx), icf_eligible(other.icf_eligible), icf_leaf(other.icf_leaf) {} // For garbage collection std::atomic_bool is_visited = false; // For ICF InputSection *leader = nullptr; u32 icf_idx = -1; bool icf_eligible = false; bool icf_leaf = false; }; // InputSection represents a section in an input object file. template class InputSection { public: InputSection(Context &ctx, ObjectFile &file, std::string_view name, i64 shndx); bool is_compressed(); void uncompress(Context &ctx, u8 *buf); void scan_relocations(Context &ctx); void write_to(Context &ctx, u8 *buf); void apply_reloc_alloc(Context &ctx, u8 *base); void apply_reloc_nonalloc(Context &ctx, u8 *base); void kill(); std::string_view name() const; InputSectionExtra &extra() const; i64 get_priority() const; u64 get_addr() const; i64 get_addend(const ElfRel &rel) const; const ElfShdr &shdr() const; std::span> get_rels(Context &ctx) const; std::span> get_fdes() const; // For ARM64 range extension thunks std::vector &get_range_extn() const; // For RISC-V section resizing std::vector &get_r_deltas() const; std::vector *> &get_sorted_symbols() const; ObjectFile &file; OutputSection *output_section = nullptr; std::string_view contents; std::unique_ptr[]> rel_fragments; i32 fde_begin = -1; i32 fde_end = -1; u32 offset = -1; u32 shndx = -1; u32 relsec_idx = -1; u32 reldyn_offset = 0; u32 sh_size = -1; // For COMDAT de-duplication and garbage collection std::atomic_bool is_alive = true; bool is_ehframe = false; u8 p2align = 0; private: typedef enum : u8 { NONE, ERROR, COPYREL, PLT, DYNREL, BASEREL } Action; void dispatch(Context &ctx, Action table[3][4], i64 i, const ElfRel &rel, Symbol &sym); void copy_contents(Context &ctx, u8 *buf); void copy_contents_riscv(Context &ctx, u8 *buf); std::pair *, i64> get_fragment(Context &ctx, const ElfRel &rel); bool is_relr_reloc(Context &ctx, const ElfRel &rel); }; template void report_undef(Context &ctx, InputFile &file, Symbol &sym); // // output-chunks.cc // template bool is_relro(Context &ctx, Chunk *chunk); template bool separate_page(Context &ctx, Chunk *a, Chunk *b); // Chunk represents a contiguous region in an output file. template class Chunk { public: virtual ~Chunk() = default; virtual bool is_header() const { return false; } virtual bool is_output_section() const { return false; } virtual void copy_buf(Context &ctx) {} virtual void write_to(Context &ctx, u8 *buf); virtual void update_shdr(Context &ctx) {} std::string_view name; i64 shndx = 0; ElfShdr shdr = {}; protected: Chunk() { shdr.sh_addralign = 1; } }; // ELF header template class OutputEhdr : public Chunk { public: OutputEhdr() { this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_size = sizeof(ElfEhdr); this->shdr.sh_addralign = E::word_size; } bool is_header() const override { return true; } void copy_buf(Context &ctx) override; }; // Section header template class OutputShdr : public Chunk { public: OutputShdr() { this->shdr.sh_addralign = E::word_size; } bool is_header() const override { return true; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; // Program header template class OutputPhdr : public Chunk { public: OutputPhdr() { this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_addralign = E::word_size; } bool is_header() const override { return true; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; template class InterpSection : public Chunk { public: InterpSection() { this->name = ".interp"; this->shdr.sh_type = SHT_PROGBITS; this->shdr.sh_flags = SHF_ALLOC; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; // Sections template class OutputSection : public Chunk { public: static OutputSection * get_instance(Context &ctx, std::string_view name, u64 type, u64 flags); bool is_output_section() const override { return true; } void copy_buf(Context &ctx) override; void write_to(Context &ctx, u8 *buf) override; std::vector *> members; u32 idx; void construct_relr(Context &ctx); std::vector relr; std::vector>> thunks; private: OutputSection(std::string_view name, u32 type, u64 flags, u32 idx) : idx(idx) { this->name = name; this->shdr.sh_type = type; this->shdr.sh_flags = flags; } }; template class GotSection : public Chunk { public: GotSection() { this->name = ".got"; this->shdr.sh_type = SHT_PROGBITS; this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE; this->shdr.sh_addralign = E::word_size; } void add_got_symbol(Context &ctx, Symbol *sym); 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_tlsld_addr(Context &ctx) const; i64 get_reldyn_size(Context &ctx) const; void copy_buf(Context &ctx) override; std::vector *> got_syms; std::vector *> gottp_syms; std::vector *> tlsgd_syms; std::vector *> tlsdesc_syms; u32 tlsld_idx = -1; void construct_relr(Context &ctx); std::vector relr; }; template class GotPltSection : public Chunk { public: GotPltSection() { this->name = ".got.plt"; this->shdr.sh_type = SHT_PROGBITS; this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE; this->shdr.sh_addralign = E::word_size; } void copy_buf(Context &ctx) override; }; template class PltSection : public Chunk { public: PltSection() { this->name = ".plt"; this->shdr.sh_type = SHT_PROGBITS; this->shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; this->shdr.sh_addralign = 16; } void add_symbol(Context &ctx, Symbol *sym); void copy_buf(Context &ctx) override; std::vector *> symbols; }; template class PltGotSection : public Chunk { public: PltGotSection() { this->name = ".plt.got"; this->shdr.sh_type = SHT_PROGBITS; this->shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; this->shdr.sh_addralign = E::pltgot_size; } void add_symbol(Context &ctx, Symbol *sym); void copy_buf(Context &ctx) override; std::vector *> symbols; }; template class RelPltSection : public Chunk { public: RelPltSection() { this->name = E::is_rel ? ".rel.plt" : ".rela.plt"; this->shdr.sh_type = E::is_rel ? SHT_REL : SHT_RELA; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_entsize = sizeof(ElfRel); this->shdr.sh_addralign = E::word_size; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; template class RelDynSection : public Chunk { public: RelDynSection() { this->name = E::is_rel ? ".rel.dyn" : ".rela.dyn"; this->shdr.sh_type = E::is_rel ? SHT_REL : SHT_RELA; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_entsize = sizeof(ElfRel); this->shdr.sh_addralign = E::word_size; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; void sort(Context &ctx); i64 relcount = 0; }; template class RelrDynSection : public Chunk { public: RelrDynSection() { this->name = ".relr.dyn"; this->shdr.sh_type = SHT_RELR; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_entsize = E::word_size; this->shdr.sh_addralign = E::word_size; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; template class StrtabSection : public Chunk { public: StrtabSection() { this->name = ".strtab"; this->shdr.sh_type = SHT_STRTAB; this->shdr.sh_size = 1; } void update_shdr(Context &ctx) override; }; template class ShstrtabSection : public Chunk { public: ShstrtabSection() { this->name = ".shstrtab"; this->shdr.sh_type = SHT_STRTAB; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; template class DynstrSection : public Chunk { public: DynstrSection() { this->name = ".dynstr"; this->shdr.sh_type = SHT_STRTAB; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_size = 1; } i64 add_string(std::string_view str); i64 find_string(std::string_view str); void copy_buf(Context &ctx) override; i64 dynsym_offset = -1; private: std::unordered_map strings; }; template class DynamicSection : public Chunk { public: DynamicSection() { this->name = ".dynamic"; this->shdr.sh_type = SHT_DYNAMIC; this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE; this->shdr.sh_addralign = E::word_size; this->shdr.sh_entsize = sizeof(ElfDyn); } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; template class SymtabSection : public Chunk { public: SymtabSection() { this->name = ".symtab"; this->shdr.sh_type = SHT_SYMTAB; this->shdr.sh_entsize = sizeof(ElfSym); this->shdr.sh_addralign = E::word_size; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; template class DynsymSection : public Chunk { public: DynsymSection() { this->name = ".dynsym"; this->shdr.sh_type = SHT_DYNSYM; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_entsize = sizeof(ElfSym); this->shdr.sh_addralign = E::word_size; } void add_symbol(Context &ctx, Symbol *sym); void finalize(Context &ctx); void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; std::vector *> symbols{1}; }; template class HashSection : public Chunk { public: HashSection() { this->name = ".hash"; this->shdr.sh_type = SHT_HASH; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_entsize = 4; this->shdr.sh_addralign = 4; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; template class GnuHashSection : public Chunk { public: GnuHashSection() { this->name = ".gnu.hash"; this->shdr.sh_type = SHT_GNU_HASH; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_addralign = E::word_size; } std::span *> get_exported_symbols(Context &ctx); void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; static constexpr i64 LOAD_FACTOR = 8; static constexpr i64 HEADER_SIZE = 16; static constexpr i64 BLOOM_SHIFT = 26; static constexpr i64 ELFCLASS_BITS = E::word_size * 8; u32 num_buckets = -1; u32 num_bloom = 1; }; template class MergedSection : public Chunk { public: static MergedSection * get_instance(Context &ctx, std::string_view name, u64 type, u64 flags); SectionFragment *insert(std::string_view data, u64 hash, i64 p2align); void assign_offsets(Context &ctx); void copy_buf(Context &ctx) override; void write_to(Context &ctx, u8 *buf) override; void print_stats(Context &ctx); HyperLogLog estimator; private: MergedSection(std::string_view name, u64 flags, u32 type); ConcurrentMap> map; std::vector shard_offsets; std::once_flag once_flag; }; template class EhFrameSection : public Chunk { public: EhFrameSection() { this->name = ".eh_frame"; this->shdr.sh_type = SHT_PROGBITS; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_addralign = E::word_size; } void construct(Context &ctx); void apply_reloc(Context &ctx, ElfRel &rel, u64 offset, u64 val); void copy_buf(Context &ctx) override; }; template class EhFrameHdrSection : public Chunk { public: EhFrameHdrSection() { this->name = ".eh_frame_hdr"; this->shdr.sh_type = SHT_PROGBITS; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_addralign = 4; this->shdr.sh_size = HEADER_SIZE; } static constexpr i64 HEADER_SIZE = 12; void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; u32 num_fdes = 0; }; template class CopyrelSection : public Chunk { public: CopyrelSection(bool is_relro) : is_relro(is_relro) { this->name = is_relro ? ".copyrel.rel.ro" : ".copyrel"; this->shdr.sh_type = SHT_NOBITS; this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE; this->shdr.sh_addralign = 64; } void add_symbol(Context &ctx, Symbol *sym); void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; bool is_relro; std::vector *> symbols; }; template class VersymSection : public Chunk { public: VersymSection() { this->name = ".gnu.version"; this->shdr.sh_type = SHT_GNU_VERSYM; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_entsize = 2; this->shdr.sh_addralign = 2; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; std::vector contents; }; template class VerneedSection : public Chunk { public: VerneedSection() { this->name = ".gnu.version_r"; this->shdr.sh_type = SHT_GNU_VERNEED; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_addralign = E::word_size; } void construct(Context &ctx); void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; std::vector contents; }; template class VerdefSection : public Chunk { public: VerdefSection() { this->name = ".gnu.version_d"; this->shdr.sh_type = SHT_GNU_VERDEF; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_addralign = 8; } void construct(Context &ctx); void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; std::vector contents; }; template class BuildIdSection : public Chunk { public: BuildIdSection() { this->name = ".note.gnu.build-id"; this->shdr.sh_type = SHT_NOTE; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_addralign = 4; this->shdr.sh_size = 1; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; void write_buildid(Context &ctx); static constexpr i64 HEADER_SIZE = 16; }; template class NotePropertySection : public Chunk { public: NotePropertySection() { this->name = ".note.gnu.property"; this->shdr.sh_type = SHT_NOTE; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_addralign = E::word_size; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; u32 features = 0; }; template class GabiCompressedSection : public Chunk { public: GabiCompressedSection(Context &ctx, Chunk &chunk); void copy_buf(Context &ctx) override; private: ElfChdr chdr = {}; std::unique_ptr contents; }; template class GnuCompressedSection : public Chunk { public: GnuCompressedSection(Context &ctx, Chunk &chunk); void copy_buf(Context &ctx) override; private: static constexpr i64 HEADER_SIZE = 12; i64 original_size = 0; std::unique_ptr contents; }; template class RelocSection : public Chunk { public: RelocSection(Context &ctx, OutputSection &osec); void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; private: using RelaTy = typename std::conditional::type; OutputSection &output_section; std::vector offsets; }; bool is_c_identifier(std::string_view name); template std::vector> create_phdr(Context &ctx); // // input-files.cc // // A comdat section typically represents an inline function, // which are de-duplicated by the linker. // // For each inline function, there's one comdat section, which // contains section indices of the function code and its data such as // string literals, if any. // // Comdat sections are identified by its signature. If two comdat // sections have the same signature, the linker picks up one and // discards the other by eliminating all sections that the other // comdat section refers to. struct ComdatGroup { ComdatGroup() = default; ComdatGroup(const ComdatGroup &other) : owner(other.owner.load()) {} // The file priority of the owner file of this comdat section. std::atomic_uint32_t owner = -1; }; template struct MergeableSection { MergedSection *parent; u8 p2align = 0; std::vector strings; std::vector hashes; std::vector frag_offsets; std::vector *> fragments; }; // InputFile is the base class of ObjectFile and SharedFile. template class InputFile { public: InputFile(Context &ctx, MappedFile> *mf); InputFile() : filename("") {} virtual ~InputFile() = default; template std::span get_data(Context &ctx, const ElfShdr &shdr); template std::span get_data(Context &ctx, i64 idx); std::string_view get_string(Context &ctx, const ElfShdr &shdr); std::string_view get_string(Context &ctx, i64 idx); ElfEhdr &get_ehdr() { return *(ElfEhdr *)mf->data; } ElfPhdr *get_phdr() { return (ElfPhdr *)(mf->data + get_ehdr().e_phoff); } ElfShdr *find_section(i64 type); virtual void resolve_symbols(Context &ctx) = 0; virtual void clear_symbols(Context &ctx); virtual void mark_live_objects(Context &ctx, std::function *)> feeder) = 0; std::span *> get_global_syms(); MappedFile> *mf = nullptr; std::span> elf_sections; std::span> elf_syms; std::vector *> symbols; i64 first_global = 0; std::string filename; bool is_dso = false; u32 priority; std::atomic_bool is_alive = false; std::string_view shstrtab; // To create an output .symtab u64 local_symtab_idx = 0; u64 global_symtab_idx = 0; u64 num_local_symtab = 0; u64 num_global_symtab = 0; u64 strtab_offset = 0; u64 strtab_size = 0; // For --emit-relocs std::vector output_sym_indices; protected: std::unique_ptr[]> local_syms; }; // ObjectFile represents an input .o file. template class ObjectFile : public InputFile { public: ObjectFile() = default; static ObjectFile *create(Context &ctx, MappedFile> *mf, std::string archive_name, bool is_in_lib); void parse(Context &ctx); void register_section_pieces(Context &ctx); void resolve_symbols(Context &ctx) override; void mark_live_objects(Context &ctx, std::function *)> feeder) override; void convert_undefined_weak_symbols(Context &ctx); void resolve_comdat_groups(); void eliminate_duplicate_comdat_groups(); void claim_unresolved_symbols(Context &ctx); void scan_relocations(Context &ctx); void convert_common_symbols(Context &ctx); void compute_symtab(Context &ctx); void write_symtab(Context &ctx); i64 get_shndx(const ElfSym &esym); InputSection *get_section(const ElfSym &esym); std::string archive_name; std::vector>> sections; std::vector> extras; std::vector>> mergeable_sections; bool is_in_lib = false; std::vector> elf_sections2; std::vector> cies; std::vector> fdes; std::vector symvers; std::vector> sym_fragments; std::vector>> comdat_groups; bool exclude_libs = false; u32 features = 0; bool is_lto_obj = false; u64 num_dynrel = 0; u64 reldyn_offset = 0; u64 fde_idx = 0; u64 fde_offset = 0; u64 fde_size = 0; // For ARM64 range extension thunks std::vector> range_extn; // For RISC-V section resizing std::vector> r_deltas; std::vector>> sorted_rels; std::vector *>> sorted_symbols; private: ObjectFile(Context &ctx, MappedFile> *mf, std::string archive_name, bool is_in_lib); void initialize_sections(Context &ctx); void initialize_symbols(Context &ctx); void sort_relocations(Context &ctx); void initialize_mergeable_sections(Context &ctx); void initialize_ehframe_sections(Context &ctx); u32 read_note_gnu_property(Context &ctx, const ElfShdr &shdr); void read_ehframe(Context &ctx, InputSection &isec); void override_symbol(Context &ctx, Symbol &sym, const ElfSym &esym, i64 symidx); void merge_visibility(Context &ctx, Symbol &sym, u8 visibility); std::pair *> uncompress_contents(Context &ctx, const ElfShdr &shdr, std::string_view name); bool has_common_symbol; std::string_view symbol_strtab; const ElfShdr *symtab_sec; std::span symtab_shndx_sec; }; // SharedFile represents an input .so file. template class SharedFile : public InputFile { public: static SharedFile *create(Context &ctx, MappedFile> *mf); void parse(Context &ctx); void resolve_symbols(Context &ctx) override; std::vector *> find_aliases(Symbol *sym); bool is_readonly(Context &ctx, Symbol *sym); void mark_live_objects(Context &ctx, std::function *)> feeder) override; void compute_symtab(Context &ctx); void write_symtab(Context &ctx); std::string soname; std::vector version_strings; std::vector> elf_syms2; private: SharedFile(Context &ctx, MappedFile> *mf); std::string get_soname(Context &ctx); void maybe_override_symbol(Symbol &sym, const ElfSym &esym); std::vector read_verdef(Context &ctx); std::vector versyms; std::string_view symbol_strtab; const ElfShdr *symtab_sec; }; // // linker-script.cc // template void parse_linker_script(Context &ctx, MappedFile> *mf); template i64 get_script_output_type(Context &ctx, MappedFile> *mf); template void parse_version_script(Context &ctx, std::string path); template void parse_dynamic_list(Context &ctx, std::string path); // // glob.cc // class GlobPattern { typedef enum { STRING, STAR, QUESTION, BRACKET } Kind; struct Element { Element(Kind k) : kind(k) {} Kind kind; std::string str; std::vector bitmap; }; public: static std::optional compile(std::string_view pat); bool match(std::string_view str); private: GlobPattern(std::vector &&vec) : elements(vec) {} static bool do_match(std::string_view str, std::span elements); std::vector elements; }; // // version-matcher.cc // class VersionMatcher { public: bool add(std::string_view pat, u16 ver); bool empty() const { return !root && globs.empty(); } std::optional find(std::string_view str); private: struct TrieNode { u32 value = -1; TrieNode *suffix_link = nullptr; std::unique_ptr children[256]; }; void compile(); void fix_suffix_links(TrieNode &node); void fix_values(); std::vector strings; std::unique_ptr root; std::vector> globs; std::vector versions; std::once_flag once_flag; bool compiled = false; }; // // lto.cc // template ObjectFile *read_lto_object(Context &ctx, MappedFile> *mb); template std::vector *> do_lto(Context &ctx); template void lto_cleanup(Context &ctx); // // gc-sections.cc // template void gc_sections(Context &ctx); // // icf.cc // template void icf_sections(Context &ctx); // // relocatable.cc // template void combine_objects(Context &ctx, std::span file_args); // // mapfile.cc // template void print_map(Context &ctx); // // subprocess.cc // std::function fork_child(); template void try_resume_daemon(Context &ctx); template void daemonize(Context &ctx, std::function *wait_for_client, std::function *on_complete); template [[noreturn]] void process_run_subcommand(Context &ctx, int argc, char **argv); // // commandline.cc // bool read_flag(std::span &args, std::string name); template bool read_arg(Context &ctx, std::span &args, std::string_view &arg, std::string name); template void parse_nonpositional_args(Context &ctx, std::vector &remaining); // // passes.cc // template void apply_exclude_libs(Context &); template void create_synthetic_sections(Context &); template void set_file_priority(Context &); template void resolve_symbols(Context &); template void register_section_pieces(Context &); template void eliminate_comdats(Context &); template void convert_common_symbols(Context &); template void compute_merged_section_sizes(Context &); template void bin_sections(Context &); template ObjectFile *create_internal_file(Context &); template void check_cet_errors(Context &); template void print_dependencies(Context &); template void print_dependencies(Context &); template void print_dependencies_full(Context &); template void write_repro_file(Context &); template void check_duplicate_symbols(Context &); template void sort_init_fini(Context &); template void shuffle_sections(Context &); template std::vector *> collect_output_sections(Context &); template void compute_section_sizes(Context &); template void claim_unresolved_symbols(Context &); template void scan_rels(Context &); template void construct_relr(Context &); template void create_output_symtab(Context &); template void create_reloc_sections(Context &); template void apply_version_script(Context &); template void parse_symbol_version(Context &); template void compute_import_export(Context &); template void clear_padding(Context &); template i64 get_section_rank(Context &, Chunk *chunk); template i64 set_osec_offsets(Context &); template void fix_synthetic_symbols(Context &); template i64 compress_debug_sections(Context &); template void write_dependency_file(Context &); // // arch-arm64.cc // i64 create_range_extension_thunks(Context &ctx); void write_thunks(Context &ctx); // // arch-riscv64.cc // i64 riscv_resize_sections(Context &ctx); // // output-file.cc // template class OutputFile { public: static std::unique_ptr> open(Context &ctx, std::string path, i64 filesize, i64 perm); virtual void close(Context &ctx) = 0; virtual ~OutputFile() = default; u8 *buf = nullptr; std::string path; i64 filesize; bool is_mmapped; bool is_unmapped = false; protected: OutputFile(std::string path, i64 filesize, bool is_mmapped) : path(path), filesize(filesize), is_mmapped(is_mmapped) {} }; // // main.cc // struct BuildId { template i64 size(Context &ctx) const; enum { NONE, HEX, HASH, UUID } kind = NONE; std::vector value; i64 hash_size = 0; }; typedef enum { COMPRESS_NONE, COMPRESS_GABI, COMPRESS_GNU } CompressKind; typedef enum { UNRESOLVED_ERROR, UNRESOLVED_WARN, UNRESOLVED_IGNORE, } UnresolvedKind; typedef enum { SEPARATE_LOADABLE_SEGMENTS, SEPARATE_CODE, NOSEPARATE_CODE, } SeparateCodeKind; typedef enum { CET_REPORT_NONE, CET_REPORT_WARNING, CET_REPORT_ERROR, } CetReportKind; typedef enum { SHUFFLE_SECTIONS_NONE, SHUFFLE_SECTIONS_SHUFFLE, SHUFFLE_SECTIONS_REVERSE, } ShuffleSectionsKind; struct VersionPattern { std::string_view pattern; u16 ver_idx = -1; bool is_cpp = false; }; template class FileCache { public: void store(MappedFile> *mf, T *obj) { Key k(mf->name, mf->size, mf->mtime); cache[k].push_back(obj); } std::vector get(MappedFile> *mf) { Key k(mf->name, mf->size, mf->mtime); std::vector objs = cache[k]; cache[k].clear(); return objs; } T *get_one(MappedFile> *mf) { std::vector objs = get(mf); return objs.empty() ? nullptr : objs[0]; } private: typedef std::tuple Key; std::map> cache; }; // 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 // resource management, and other miscellaneous objects. template struct Context { Context() = default; Context(const Context &) = delete; void checkpoint() { if (has_error) { cleanup(); _exit(1); } } // Command-line arguments struct { BuildId build_id; CetReportKind z_cet_report = CET_REPORT_NONE; CompressKind compress_debug_sections = COMPRESS_NONE; SeparateCodeKind z_separate_code = SEPARATE_LOADABLE_SEGMENTS; ShuffleSectionsKind shuffle_sections = SHUFFLE_SECTIONS_NONE; UnresolvedKind unresolved_symbols = UNRESOLVED_ERROR; bool Bsymbolic = false; bool Bsymbolic_functions = false; bool allow_multiple_definition = false; bool color_diagnostics = false; bool default_symver = false; bool demangle = true; bool discard_all = false; bool discard_locals = false; bool eh_frame_hdr = true; bool emit_relocs = false; bool export_dynamic = false; bool fatal_warnings = false; bool fork = true; bool gc_sections = false; bool hash_style_gnu = false; bool hash_style_sysv = true; bool icf = false; bool is_static = false; bool lto_pass2 = false; bool noinhibit_exec = false; bool omagic = false; bool pack_dyn_relocs_relr = false; bool perf = false; bool pic = false; bool pie = false; bool preload = false; bool print_gc_sections = false; bool print_icf_sections = false; bool print_map = false; bool quick_exit = true; bool relax = true; bool relocatable = false; bool repro = false; bool shared = false; bool stats = false; bool strip_all = false; bool strip_debug = false; bool trace = false; bool warn_common = false; bool warn_once = false; bool warn_textrel = false; bool z_copyreloc = true; bool z_defs = false; bool z_delete = true; bool z_dlopen = true; bool z_dump = true; bool z_execstack = false; bool z_ibt = false; bool z_ibtplt = false; bool z_initfirst = false; bool z_interpose = false; bool z_keep_text_section_prefix = false; bool z_nodefaultlib = false; bool z_now = false; bool z_origin = false; bool z_relro = true; bool z_shstk = false; bool z_text = false; i64 emulation = -1; i64 filler = -1; i64 print_dependencies = 0; i64 spare_dynamic_tags = 5; i64 thread_count = 0; std::optional unique; std::optional shuffle_sections_seed; std::string Map; std::string chroot; std::string dependency_file; std::string directory; std::string dynamic_linker; std::string entry = "_start"; std::string fini = "_fini"; std::string init = "_init"; std::string output; std::string plugin; std::string rpaths; std::string soname; std::string sysroot; std::unique_ptr> retain_symbols_file; std::unordered_set ignore_ir_file; std::unordered_set wrap; std::vector *, std::variant *, u64>>> defsyms; std::vector library_paths; std::vector plugin_opt; std::vector version_definitions; std::vector auxiliary; std::vector exclude_libs; std::vector filter; std::vector require_defined; std::vector trace_symbol; std::vector undefined; u64 image_base = 0x200000; } arg; std::vector version_patterns; u16 default_version = VER_NDX_GLOBAL; i64 page_size = -1; i64 plt_hdr_size = -1; i64 plt_size = -1; // Reader context bool as_needed = false; bool whole_archive = false; bool is_static; bool in_lib = false; i64 file_priority = 10000; std::unordered_set visited; tbb::task_group tg; bool has_error = false; bool has_lto_object = false; // Symbol table tbb::concurrent_hash_map, HashCmp> symbol_map; tbb::concurrent_hash_map comdat_groups; tbb::concurrent_vector>> merged_sections; tbb::concurrent_vector>> output_chunks; std::vector>> output_sections; FileCache> obj_cache; FileCache> dso_cache; tbb::concurrent_vector> timer_records; tbb::concurrent_vector> on_exit; tbb::concurrent_vector>> obj_pool; tbb::concurrent_vector>> dso_pool; tbb::concurrent_vector> string_pool; tbb::concurrent_vector>>> mf_pool; tbb::concurrent_vector>> rel_pool; // Symbol auxiliary data std::vector symbol_aux; // Fully-expanded command line args std::vector cmdline_args; // Input files std::vector *> objs; std::vector *> dsos; ObjectFile *internal_obj = nullptr; // Output buffer std::unique_ptr> output_file; u8 *buf = nullptr; std::vector *> chunks; std::atomic_bool needs_tlsld = false; std::atomic_bool has_gottp_rel = false; std::atomic_bool has_textrel = false; // For --warn-once tbb::concurrent_hash_map warned; // Output chunks std::unique_ptr> ehdr; std::unique_ptr> shdr; std::unique_ptr> phdr; std::unique_ptr> interp; std::unique_ptr> got; std::unique_ptr> gotplt; std::unique_ptr> relplt; std::unique_ptr> reldyn; std::unique_ptr> relrdyn; std::unique_ptr> dynamic; std::unique_ptr> strtab; std::unique_ptr> dynstr; std::unique_ptr> hash; std::unique_ptr> gnu_hash; std::unique_ptr> shstrtab; std::unique_ptr> plt; std::unique_ptr> pltgot; std::unique_ptr> symtab; std::unique_ptr> dynsym; std::unique_ptr> eh_frame; std::unique_ptr> eh_frame_hdr; std::unique_ptr> copyrel; std::unique_ptr> copyrel_relro; std::unique_ptr> versym; std::unique_ptr> verneed; std::unique_ptr> verdef; std::unique_ptr> buildid; std::unique_ptr> note_property; // For --relocatable std::vector *> r_chunks; ROutputEhdr *r_ehdr = nullptr; ROutputShdr *r_shdr = nullptr; RStrtabSection *r_shstrtab = nullptr; RStrtabSection *r_strtab = nullptr; RSymtabSection *r_symtab = nullptr; u64 tls_begin = -1; u64 tls_end = -1; bool relax_tlsdesc = false; // Linker-synthesized symbols Symbol *_DYNAMIC = nullptr; Symbol *_GLOBAL_OFFSET_TABLE_ = nullptr; Symbol *__GNU_EH_FRAME_HDR = nullptr; Symbol *__bss_start = nullptr; Symbol *__ehdr_start = nullptr; Symbol *__executable_start = nullptr; Symbol *__fini_array_end = nullptr; Symbol *__fini_array_start = nullptr; Symbol *__global_pointer = nullptr; Symbol *__init_array_end = nullptr; Symbol *__init_array_start = nullptr; Symbol *__preinit_array_end = nullptr; Symbol *__preinit_array_start = nullptr; Symbol *__rel_iplt_end = nullptr; Symbol *__rel_iplt_start = nullptr; Symbol *_edata = nullptr; Symbol *_end = nullptr; Symbol *_etext = nullptr; Symbol *edata = nullptr; Symbol *end = nullptr; Symbol *etext = nullptr; }; template MappedFile> *open_library(Context &ctx, std::string path); template MappedFile> *find_library(Context &ctx, std::string path); template void read_file(Context &ctx, MappedFile> *mf); int main(int argc, char **argv); template std::ostream &operator<<(std::ostream &out, const InputFile &file); // // Symbol // enum { NEEDS_GOT = 1 << 0, NEEDS_PLT = 1 << 1, NEEDS_GOTTP = 1 << 2, NEEDS_TLSGD = 1 << 3, NEEDS_COPYREL = 1 << 4, NEEDS_TLSDESC = 1 << 5, NEEDS_THUNK = 1 << 6, }; // A struct to hold taret-dependent symbol members; template struct SymbolExtras {}; template <> struct SymbolExtras { // For range extension thunks i32 thunk_idx = -1; i32 thunk_sym_idx = -1; }; // Symbol class represents a defined symbol. // // A symbol has not only one but several different addresses if it // has PLT or GOT entries. This class provides various functions to // compute different addresses. template class Symbol { public: Symbol() = default; Symbol(std::string_view name) : nameptr(name.data()), namelen(name.size()) {} Symbol(const Symbol &other) : Symbol(other.name()) {} u64 get_addr(Context &ctx, bool allow_plt = true) const; u64 get_got_addr(Context &ctx) const; u64 get_gotplt_addr(Context &ctx) const; u64 get_gottp_addr(Context &ctx) const; u64 get_tlsgd_addr(Context &ctx) const; u64 get_tlsdesc_addr(Context &ctx) const; u64 get_plt_addr(Context &ctx) const; void set_got_idx(Context &ctx, i32 idx); void set_gotplt_idx(Context &ctx, i32 idx); void set_gottp_idx(Context &ctx, i32 idx); void set_tlsgd_idx(Context &ctx, i32 idx); void set_tlsdesc_idx(Context &ctx, i32 idx); void set_plt_idx(Context &ctx, i32 idx); void set_pltgot_idx(Context &ctx, i32 idx); void set_dynsym_idx(Context &ctx, i32 idx); i32 get_got_idx(Context &ctx) const; i32 get_gotplt_idx(Context &ctx) const; i32 get_gottp_idx(Context &ctx) const; i32 get_tlsgd_idx(Context &ctx) const; i32 get_tlsdesc_idx(Context &ctx) const; i32 get_plt_idx(Context &ctx) const; i32 get_pltgot_idx(Context &ctx) const; i32 get_dynsym_idx(Context &ctx) const; bool has_plt(Context &ctx) const; bool has_got(Context &ctx) const; bool is_absolute() const; bool is_relative() const; InputSection *get_input_section() const; u32 get_type() const; std::string_view get_version() const; const ElfSym &esym() const; SectionFragment *get_frag() const; std::string_view name() const; // A symbol is owned by a file. If two or more files define the // same symbol, the one with the strongest definition owns the symbol. // If `file` is null, the symbol is equivalent to nonexistent. InputFile *file = nullptr; u64 value = 0; const char *nameptr = nullptr; u64 namelen : 20 = 0; // Index into the symbol table of the owner file. i64 sym_idx : 20 = -1; // shndx > 0 : symbol is in file's shndx'th section // shndx == 0 : absolute symbol // shndx < 0 : symbol is in the -shndx'th output section i64 shndx : 20 = 0; i32 aux_idx = -1; u16 ver_idx = 0; // `flags` has NEEDS_ flags. std::atomic_uint8_t flags = 0; tbb::spin_mutex mu; std::atomic_uint8_t visibility = STV_DEFAULT; u8 is_weak : 1 = false; u8 write_to_symtab : 1 = false; u8 traced : 1 = false; u8 wrap : 1 = false; u8 has_copyrel : 1 = false; u8 copyrel_readonly : 1 = false; // If a symbol can be interposed at runtime, `is_imported` is true. // If a symbol is a dynamic symbol and can be used by other ELF // module at runtime, `is_exported` is true. // // Note that both can be true at the same time. Such symbol // represents a function or data exported from this ELF module // which can be interposed by other definition at runtime. // That is the usual exported symbols when creating a DSO. // In other words, a dynamic symbol is exported by a DSO and // imported by itself. // // If is_imported is true and is_exported is false, it is a dynamic // symbol imported from other DSO. // // If is_imported is false and is_exported is true, there are two // possible cases. If we are creating an executable, we know that // exported symbols cannot be interposed by any DSO (because the // dynamic loader searches a dynamic symbol from an executable // before examining any DSOs), so any exported symbol is export-only. // If we are creating a DSO, export-only symbols represent a // protected symbol (i.e. a symbol whose visibility is STV_PROTECTED). u8 is_imported : 1 = false; u8 is_exported : 1 = false; // For LTO. True if the symbol is referenced by a regular object (as // opposed to IR object). u8 referenced_by_regular_obj : 1 = false; // Target-dependent extra members. SymbolExtras extra; }; // If we haven't seen the same `key` before, create a new instance // of Symbol and returns it. Otherwise, returns the previously- // instantiated object. `key` is usually the same as `name`. template Symbol *get_symbol(Context &ctx, std::string_view key, std::string_view name) { typename decltype(ctx.symbol_map)::const_accessor acc; ctx.symbol_map.insert(acc, {key, Symbol(name)}); return const_cast *>(&acc->second); } template Symbol *get_symbol(Context &ctx, std::string_view name) { return get_symbol(ctx, name, name); } template std::ostream &operator<<(std::ostream &out, const Symbol &sym) { if (opt_demangle) out << demangle(sym.name()); else out << sym.name(); return out; } // // Inline objects and functions // template inline i64 FdeRecord::size(ObjectFile &file) const { return *(u32 *)(file.cies[cie_idx].contents.data() + input_offset) + 4; } template inline std::string_view FdeRecord::get_contents(ObjectFile &file) const { return file.cies[cie_idx].contents.substr(input_offset, size(file)); } template inline std::span> FdeRecord::get_rels(ObjectFile &file) const { std::span> rels = file.cies[cie_idx].rels; i64 end = rel_idx; while (end < rels.size() && rels[end].r_offset < input_offset + size(file)) end++; return rels.subspan(rel_idx, end - rel_idx); } template inline std::ostream & operator<<(std::ostream &out, const InputSection &isec) { out << isec.file << ":(" << isec.name() << ")"; return out; } template inline u64 SectionFragment::get_addr(Context &ctx) const { return output_section.shdr.sh_addr + offset; } template inline void InputSection::kill() { if (is_alive.exchange(false)) for (FdeRecord &fde : get_fdes()) fde.is_alive = false; } template inline u64 InputSection::get_addr() const { return output_section->shdr.sh_addr + offset; } template inline std::string_view InputSection::name() const { if (file.elf_sections.size() <= shndx) return ".common"; return file.shstrtab.data() + file.elf_sections[shndx].sh_name; } template inline i64 InputSection::get_priority() const { return ((i64)file.priority << 32) | shndx; } template inline InputSectionExtra &InputSection::extra() const { return file.extras[shndx]; } template inline i64 InputSection::get_addend(const ElfRel &rel) const { return rel.r_addend; } template <> inline i64 InputSection::get_addend(const ElfRel &rel) const { u8 *loc = (u8 *)contents.data() + rel.r_offset; switch (rel.r_type) { case R_386_NONE: return 0; case R_386_8: case R_386_PC8: return *loc; case R_386_16: case R_386_PC16: return *(u16 *)loc; case R_386_32: case R_386_PC32: case R_386_GOT32: case R_386_GOT32X: case R_386_PLT32: case R_386_GOTOFF: case R_386_GOTPC: case R_386_TLS_LDM: case R_386_TLS_GOTIE: case R_386_TLS_LE: case R_386_TLS_IE: case R_386_TLS_GD: case R_386_TLS_LDO_32: case R_386_SIZE32: case R_386_TLS_GOTDESC: return *(u32 *)loc; } unreachable(); } template inline const ElfShdr &InputSection::shdr() const { if (shndx < file.elf_sections.size()) return file.elf_sections[shndx]; return file.elf_sections2[shndx - file.elf_sections.size()]; } template inline std::span> InputSection::get_rels(Context &ctx) const { if (relsec_idx == -1) return {}; if (!file.sorted_rels.empty() && !file.sorted_rels[shndx].empty()) return file.sorted_rels[shndx]; return file.template get_data>(ctx, file.elf_sections[relsec_idx]); } template inline std::span> InputSection::get_fdes() const { if (fde_begin == -1) return {}; std::span> span(file.fdes); return span.subspan(fde_begin, fde_end - fde_begin); } template inline std::vector &InputSection::get_range_extn() const { return file.range_extn[shndx]; } template inline std::vector &InputSection::get_r_deltas() const { return file.r_deltas[shndx]; } template inline std::vector *> &InputSection::get_sorted_symbols() const { return file.sorted_symbols[shndx]; } template std::pair *, i64> InputSection::get_fragment(Context &ctx, const ElfRel &rel) { assert(!(shdr().sh_flags & SHF_ALLOC)); const ElfSym &esym = file.elf_syms[rel.r_sym]; if (esym.st_type != STT_SECTION) return {nullptr, 0}; std::unique_ptr> &m = file.mergeable_sections[file.get_shndx(esym)]; if (!m) return {nullptr, 0}; i64 offset = esym.st_value + get_addend(rel); std::span offsets = m->frag_offsets; auto it = std::upper_bound(offsets.begin(), offsets.end(), offset); if (it == offsets.begin()) Fatal(ctx) << *this << ": bad relocation at " << rel.r_sym; i64 idx = it - 1 - offsets.begin(); return {m->fragments[idx], offset - offsets[idx]}; } template inline bool InputSection::is_relr_reloc(Context &ctx, const ElfRel &rel) { return ctx.arg.pack_dyn_relocs_relr && !(shdr().sh_flags & SHF_EXECINSTR) && (shdr().sh_addralign % E::word_size) == 0 && (rel.r_offset % E::word_size) == 0; } inline u64 RangeExtensionThunk::get_addr(i64 idx) const { return output_section.shdr.sh_addr + offset + symbol_map[idx] * ENTRY_SIZE; } template template inline std::span InputFile::get_data(Context &ctx, const ElfShdr &shdr) { std::string_view view = this->get_string(ctx, shdr); if (view.size() % sizeof(T)) Fatal(ctx) << *this << ": corrupted section"; return {(T *)view.data(), view.size() / sizeof(T)}; } template template inline std::span InputFile::get_data(Context &ctx, i64 idx) { if (elf_sections.size() <= idx) Fatal(ctx) << *this << ": invalid section index"; return this->template get_data(elf_sections[idx]); } template inline std::string_view InputFile::get_string(Context &ctx, const ElfShdr &shdr) { u8 *begin = mf->data + shdr.sh_offset; u8 *end = begin + shdr.sh_size; if (mf->data + mf->size < end) Fatal(ctx) << *this << ": section header is out of range: " << shdr.sh_offset; return {(char *)begin, (size_t)(end - begin)}; } template inline std::string_view InputFile::get_string(Context &ctx, i64 idx) { assert(idx < elf_sections.size()); if (elf_sections.size() <= idx) Fatal(ctx) << *this << ": invalid section index: " << idx; return this->get_string(ctx, elf_sections[idx]); } template inline std::span *> InputFile::get_global_syms() { return std::span *>(this->symbols).subspan(this->first_global); } template inline i64 ObjectFile::get_shndx(const ElfSym &esym) { assert(&this->elf_syms[0] <= &esym); assert(&esym <= &this->elf_syms[this->elf_syms.size() - 1]); if (esym.st_shndx == SHN_XINDEX) return symtab_shndx_sec[&esym - &this->elf_syms[0]]; return esym.st_shndx; } template inline InputSection *ObjectFile::get_section(const ElfSym &esym) { return sections[get_shndx(esym)].get(); } // This operator defines a total order over symbols. This is used to // make the output deterministic. template inline bool operator<(const Symbol &a, const Symbol &b) { return std::tuple{a.file->priority, a.sym_idx} < std::tuple{b.file->priority, b.sym_idx}; } template inline u64 Symbol::get_addr(Context &ctx, bool allow_plt) const { if (file && !file->is_dso) { SectionFragmentRef &ref = ((ObjectFile *)file)->sym_fragments[sym_idx]; if (ref.frag) { if (!ref.frag->is_alive) { // This condition is met if a non-alloc section refers an // alloc section and if the referenced piece of data is // garbage-collected. Typically, this condition is met if a // debug info section referring a string constant in .rodata. return 0; } return ref.frag->get_addr(ctx) + ref.addend; } } if (has_copyrel) { return copyrel_readonly ? ctx.copyrel_relro->shdr.sh_addr + value : ctx.copyrel->shdr.sh_addr + value; } if (allow_plt && has_plt(ctx)) if (is_imported || esym().st_type == STT_GNU_IFUNC) return get_plt_addr(ctx); if (InputSection *isec = get_input_section()) { if (isec->is_ehframe) { // .eh_frame contents are parsed and reconstructed by the linker, // so pointing to a specific location in a source .eh_frame // section doesn't make much sense. However, CRT files contain // symbols pointing to the very beginning and ending of the section. if (name() == "__EH_FRAME_BEGIN__" || name() == "__EH_FRAME_LIST__" || esym().st_type == STT_SECTION) return ctx.eh_frame->shdr.sh_addr; if (name() == "__FRAME_END__" || name() == "__EH_FRAME_LIST_END__") return ctx.eh_frame->shdr.sh_addr + ctx.eh_frame->shdr.sh_size; // ARM object files contain "$d" local symbol at the beginning // of data sections. Their values are not significant for .eh_frame, // so we just treat them as offset 0. if (name() == "$d" || name().starts_with("$d.")) return ctx.eh_frame->shdr.sh_addr; Fatal(ctx) << "symbol referring .eh_frame is not supported: " << *this << " " << *file; } if (!isec->is_alive) { // The control can reach here if there's a relocation that refers // a local symbol belonging to a comdat group section. This is a // violation of the spec, as all relocations should use only global // symbols of comdat members. However, .eh_frame tends to have such // relocations. return 0; } return isec->get_addr() + value; } return value; } template inline u64 Symbol::get_got_addr(Context &ctx) const { return ctx.got->shdr.sh_addr + get_got_idx(ctx) * E::word_size; } template inline u64 Symbol::get_gotplt_addr(Context &ctx) const { assert(get_gotplt_idx(ctx) != -1); return ctx.gotplt->shdr.sh_addr + get_gotplt_idx(ctx) * E::word_size; } template inline u64 Symbol::get_gottp_addr(Context &ctx) const { assert(get_gottp_idx(ctx) != -1); return ctx.got->shdr.sh_addr + get_gottp_idx(ctx) * E::word_size; } template inline u64 Symbol::get_tlsgd_addr(Context &ctx) const { assert(get_tlsgd_idx(ctx) != -1); return ctx.got->shdr.sh_addr + get_tlsgd_idx(ctx) * E::word_size; } template inline u64 Symbol::get_tlsdesc_addr(Context &ctx) const { assert(get_tlsdesc_idx(ctx) != -1); return ctx.got->shdr.sh_addr + get_tlsdesc_idx(ctx) * E::word_size; } template inline u64 Symbol::get_plt_addr(Context &ctx) const { if (i32 idx = get_plt_idx(ctx); idx != -1) return ctx.plt->shdr.sh_addr + ctx.plt_hdr_size + idx * ctx.plt_size; return ctx.pltgot->shdr.sh_addr + get_pltgot_idx(ctx) * E::pltgot_size; } template inline void Symbol::set_got_idx(Context &ctx, i32 idx) { assert(aux_idx != -1); assert(ctx.symbol_aux[aux_idx].got_idx < 0); ctx.symbol_aux[aux_idx].got_idx = idx; } template inline void Symbol::set_gotplt_idx(Context &ctx, i32 idx) { assert(aux_idx != -1); assert(ctx.symbol_aux[aux_idx].gotplt_idx < 0); ctx.symbol_aux[aux_idx].gotplt_idx = idx; } template inline void Symbol::set_gottp_idx(Context &ctx, i32 idx) { assert(aux_idx != -1); assert(ctx.symbol_aux[aux_idx].gottp_idx < 0); ctx.symbol_aux[aux_idx].gottp_idx = idx; } template inline void Symbol::set_tlsgd_idx(Context &ctx, i32 idx) { assert(aux_idx != -1); assert(ctx.symbol_aux[aux_idx].tlsgd_idx < 0); ctx.symbol_aux[aux_idx].tlsgd_idx = idx; } template inline void Symbol::set_tlsdesc_idx(Context &ctx, i32 idx) { assert(aux_idx != -1); assert(ctx.symbol_aux[aux_idx].tlsdesc_idx < 0); ctx.symbol_aux[aux_idx].tlsdesc_idx = idx; } template inline void Symbol::set_plt_idx(Context &ctx, i32 idx) { assert(aux_idx != -1); assert(ctx.symbol_aux[aux_idx].plt_idx < 0); ctx.symbol_aux[aux_idx].plt_idx = idx; } template inline void Symbol::set_pltgot_idx(Context &ctx, i32 idx) { assert(aux_idx != -1); assert(ctx.symbol_aux[aux_idx].pltgot_idx < 0); ctx.symbol_aux[aux_idx].pltgot_idx = idx; } template inline void Symbol::set_dynsym_idx(Context &ctx, i32 idx) { assert(aux_idx != -1); assert(ctx.symbol_aux[aux_idx].dynsym_idx < 0); ctx.symbol_aux[aux_idx].dynsym_idx = idx; } template inline i32 Symbol::get_got_idx(Context &ctx) const { return (aux_idx == -1) ? -1 : ctx.symbol_aux[aux_idx].got_idx; } template inline i32 Symbol::get_gotplt_idx(Context &ctx) const { return (aux_idx == -1) ? -1 : ctx.symbol_aux[aux_idx].gotplt_idx; } template inline i32 Symbol::get_gottp_idx(Context &ctx) const { return (aux_idx == -1) ? -1 : ctx.symbol_aux[aux_idx].gottp_idx; } template inline i32 Symbol::get_tlsgd_idx(Context &ctx) const { return (aux_idx == -1) ? -1 : ctx.symbol_aux[aux_idx].tlsgd_idx; } template inline i32 Symbol::get_tlsdesc_idx(Context &ctx) const { return (aux_idx == -1) ? -1 : ctx.symbol_aux[aux_idx].tlsdesc_idx; } template inline i32 Symbol::get_plt_idx(Context &ctx) const { return (aux_idx == -1) ? -1 : ctx.symbol_aux[aux_idx].plt_idx; } template inline i32 Symbol::get_pltgot_idx(Context &ctx) const { return (aux_idx == -1) ? -1 : ctx.symbol_aux[aux_idx].pltgot_idx; } template inline i32 Symbol::get_dynsym_idx(Context &ctx) const { return (aux_idx == -1) ? -1 : ctx.symbol_aux[aux_idx].dynsym_idx; } template inline bool Symbol::has_plt(Context &ctx) const { return get_plt_idx(ctx) != -1 || get_pltgot_idx(ctx) != -1; } template inline bool Symbol::has_got(Context &ctx) const { return get_got_idx(ctx) != -1; } template inline bool Symbol::is_absolute() const { if (file->is_dso) return esym().is_abs(); return !is_imported && !get_frag() && shndx == 0; } template inline bool Symbol::is_relative() const { return !is_absolute(); } template inline InputSection *Symbol::get_input_section() const { if (shndx > 0) { assert(!file->is_dso); return ((ObjectFile *)file)->sections[shndx].get(); } return nullptr; } template inline u32 Symbol::get_type() const { if (esym().st_type == STT_GNU_IFUNC && file->is_dso) return STT_FUNC; return esym().st_type; } template inline std::string_view Symbol::get_version() const { if (file->is_dso) return ((SharedFile *)file)->version_strings[ver_idx]; return ""; } template inline const ElfSym &Symbol::esym() const { return file->elf_syms[sym_idx]; } template inline SectionFragment *Symbol::get_frag() const { if (!file || file->is_dso) return nullptr; return ((ObjectFile *)file)->sym_fragments[sym_idx].frag; } template inline std::string_view Symbol::name() const { return {nameptr, (size_t)namelen}; } } // namespace mold::elf