#pragma once #include "elf.h" #include "../common/common.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 #include #ifndef _WIN32 # include #endif namespace mold::elf { 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 RelocSection; template std::ostream &operator<<(std::ostream &out, const Symbol &sym); std::string get_mold_version(); // // Mergeable section fragments // template struct SectionFragment { SectionFragment(MergedSection *sec, bool is_alive) : output_section(*sec), is_alive(is_alive) {} u64 get_addr(Context &ctx) const { return output_section.shdr.sh_addr + offset; } MergedSection &output_section; u32 offset = -1; Atomic p2align = 0; Atomic is_alive = false; }; // 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. template struct SymbolAux { i32 got_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; u32 djb_hash = 0; }; template <> struct SymbolAux : SymbolAux { i32 opd_idx = -1; }; // // thunks.cc // template class Thunk {}; template class Thunk { public: Thunk(OutputSection &osec, i64 offset) : output_section(osec), offset(offset) {} i64 size() const { return E::thunk_hdr_size + symbols.size() * E::thunk_size; } void copy_buf(Context &ctx); u64 get_addr(i64 idx) const { return output_section.shdr.sh_addr + offset + E::thunk_hdr_size + idx * E::thunk_size; } static constexpr i64 alignment = 16; OutputSection &output_section; i64 offset; std::mutex mu; std::vector *> symbols; }; struct ThunkRef { i16 thunk_idx = -1; i16 sym_idx = -1; }; // // 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 = input_offset + size(); i64 i = rel_idx; while (i < rels.size() && rels[i].r_offset < end) i++; return rels.subspan(rel_idx, i - rel_idx); } 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 bool cie_equals(const CieRecord &a, const CieRecord &b); template struct FdeRecord { FdeRecord(u32 input_offset, u32 rel_idx) : input_offset(input_offset), rel_idx(rel_idx) {} i64 size(ObjectFile &file) const { return *(U32 *)(file.cies[cie_idx].contents.data() + input_offset) + 4; } std::string_view get_contents(ObjectFile &file) const { return file.cies[cie_idx].contents.substr(input_offset, size(file)); } std::span> get_rels(ObjectFile &file) const { std::span> rels = file.cies[cie_idx].rels; i64 end = input_offset + size(file); i64 i = rel_idx; while (i < rels.size() && rels[i].r_offset < end) i++; return rels.subspan(rel_idx, i - rel_idx); } u32 input_offset = -1; u32 output_offset = -1; u32 rel_idx = -1; u16 cie_idx = -1; Atomic is_alive = true; }; // A struct to hold target-dependent input section members. template struct InputSectionExtras {}; template struct InputSectionExtras { std::vector thunk_refs; }; template struct InputSectionExtras { std::vector r_deltas; }; // InputSection represents a section in an input object file. template class InputSection { public: InputSection(Context &ctx, ObjectFile &file, i64 shndx); void uncompress(Context &ctx); void copy_contents(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; i64 get_priority() const; u64 get_addr() const; const ElfShdr &shdr() const; std::span> get_rels(Context &ctx) const; std::span> get_fdes() const; std::string_view get_func_name(Context &ctx, i64 offset) const; bool is_relr_reloc(Context &ctx, const ElfRel &rel) const; bool is_killed_by_icf() const; bool record_undef_error(Context &ctx, const ElfRel &rel); ObjectFile &file; OutputSection *output_section = nullptr; i64 sh_size = -1; std::string_view contents; [[no_unique_address]] InputSectionExtras extra; i32 fde_begin = -1; i32 fde_end = -1; i64 offset = -1; i32 shndx = -1; i32 relsec_idx = -1; i32 reldyn_offset = 0; bool uncompressed = false; // For COMDAT de-duplication and garbage collection std::atomic_bool is_alive = true; u8 p2align = 0; // For ICF Atomic address_taken = false; // For garbage collection Atomic is_visited = false; // For ICF // // `leader` is the section that this section has been merged with. // Three kind of values are possible: // - `leader == nullptr`: This section was not eligible for ICF. // - `leader == this`: This section was retained. // - `leader != this`: This section was merged with another identical section. InputSection *leader = nullptr; i32 icf_idx = -1; bool icf_eligible = false; bool icf_leaf = false; private: void scan_pcrel(Context &ctx, Symbol &sym, const ElfRel &rel); void scan_absrel(Context &ctx, Symbol &sym, const ElfRel &rel); void scan_dyn_absrel(Context &ctx, Symbol &sym, const ElfRel &rel); void scan_toc_rel(Context &ctx, Symbol &sym, const ElfRel &rel); void scan_tlsdesc(Context &ctx, Symbol &sym); void check_tlsle(Context &ctx, Symbol &sym, const ElfRel &rel); void apply_dyn_absrel(Context &ctx, Symbol &sym, const ElfRel &rel, u8 *loc, u64 S, i64 A, u64 P, ElfRel **dynrel); void apply_toc_rel(Context &ctx, Symbol &sym, const ElfRel &rel, u8 *loc, u64 S, i64 A, u64 P, ElfRel **dynrel); void copy_contents_riscv(Context &ctx, u8 *buf); std::pair *, i64> get_fragment(Context &ctx, const ElfRel &rel); u64 get_thunk_addr(i64 idx); std::optional get_tombstone(Symbol &sym, SectionFragment *frag); }; // // tls.cc // template u64 get_tls_begin(Context &); template u64 get_tp_addr(Context &); template u64 get_dtp_addr(Context &); // // output-chunks.cc // template OutputSection *find_section(Context &ctx, u32 sh_type); template OutputSection *find_section(Context &ctx, std::string_view name); template u64 get_eflags(Context &ctx) { return 0; } template i64 to_phdr_flags(Context &ctx, Chunk *chunk); template void write_plt_header(Context &ctx, u8 *buf); template void write_plt_entry(Context &ctx, u8 *buf, Symbol &sym); template void write_pltgot_entry(Context &ctx, u8 *buf, Symbol &sym); // Chunk represents a contiguous region in an output file. template class Chunk { public: virtual ~Chunk() = default; virtual bool is_header() { return false; } virtual OutputSection *to_osec() { return nullptr; } virtual i64 get_reldyn_size(Context &ctx) const { return 0; } virtual void construct_relr(Context &ctx) {} virtual void copy_buf(Context &ctx) {} virtual void write_to(Context &ctx, u8 *buf) { unreachable(); } virtual void update_shdr(Context &ctx) {} std::string_view name; ElfShdr shdr = { .sh_addralign = 1 }; i64 shndx = 0; bool is_relro = false; // For --gdb-index bool is_compressed = false; std::vector uncompressed_data; // Some synethetic sections add local symbols to the output. // For example, range extension thunks adds function_name@thunk // symbol for each thunk entry. The following members are used // for such synthesizing symbols. virtual void compute_symtab_size(Context &ctx) {}; virtual void populate_symtab(Context &ctx) {}; i64 local_symtab_idx = 0; i64 num_local_symtab = 0; i64 strtab_size = 0; i64 strtab_offset = 0; // Offset in .rel.dyn i64 reldyn_offset = 0; // For --section-order i64 sect_order = 0; // For --pack-dyn-relocs=relr std::vector relr; }; // ELF header template class OutputEhdr : public Chunk { public: OutputEhdr(u32 sh_flags) { this->name = "EHDR"; this->shdr.sh_flags = sh_flags; this->shdr.sh_size = sizeof(ElfEhdr); this->shdr.sh_addralign = sizeof(Word); } bool is_header() override { return true; } void copy_buf(Context &ctx) override; }; // Section header template class OutputShdr : public Chunk { public: OutputShdr() { this->name = "SHDR"; this->shdr.sh_size = 1; this->shdr.sh_addralign = sizeof(Word); } bool is_header() override { return true; } void copy_buf(Context &ctx) override; }; // Program header template class OutputPhdr : public Chunk { public: OutputPhdr(u32 sh_flags) { this->name = "PHDR"; this->shdr.sh_flags = sh_flags; this->shdr.sh_addralign = sizeof(Word); } bool is_header() override { return true; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; std::vector> phdrs; }; 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: OutputSection(std::string_view name, u32 type) { this->name = name; this->shdr.sh_type = type; } OutputSection *to_osec() override { return this; } void construct_relr(Context &ctx) override; void copy_buf(Context &ctx) override; void write_to(Context &ctx, u8 *buf) override; void compute_symtab_size(Context &ctx) override; void populate_symtab(Context &ctx) override; void create_range_extension_thunks(Context &ctx); std::vector *> members; std::vector>> thunks; std::unique_ptr> reloc_sec; Atomic sh_flags; }; template class GotSection : public Chunk { public: GotSection() { this->name = ".got"; this->is_relro = true; this->shdr.sh_type = SHT_PROGBITS; this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE; this->shdr.sh_addralign = sizeof(Word); // We always create a .got so that _GLOBAL_OFFSET_TABLE_ has // something to point to. s390x psABI define GOT[1] as a // reserved slot, so we allocate one more for them. this->shdr.sh_size = (is_s390x ? 2 : 1) * sizeof(Word); } 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; bool has_tlsld(Context &ctx) const { return tlsld_idx != -1; } i64 get_reldyn_size(Context &ctx) const override; void copy_buf(Context &ctx) override; void construct_relr(Context &ctx) override; void compute_symtab_size(Context &ctx) override; void populate_symtab(Context &ctx) override; std::vector *> got_syms; std::vector *> tlsgd_syms; std::vector *> tlsdesc_syms; std::vector *> gottp_syms; i64 tlsld_idx = -1; }; template class GotPltSection : public Chunk { public: GotPltSection(Context &ctx) { this->name = ".got.plt"; this->is_relro = ctx.arg.z_now; this->shdr.sh_type = is_ppc64 ? SHT_NOBITS : SHT_PROGBITS; this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE; this->shdr.sh_addralign = sizeof(Word); this->shdr.sh_size = HDR_SIZE; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; static constexpr i64 HDR_SIZE = (is_ppc64v2 ? 2 : 3) * sizeof(Word); static constexpr i64 ENTRY_SIZE = (is_ppc64v1 ? 3 : 1) * sizeof(Word); }; template class PltSection : public Chunk { public: PltSection() { this->name = ".plt"; this->shdr.sh_type = SHT_PROGBITS; if constexpr (is_sparc) { this->shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR | SHF_WRITE; this->shdr.sh_addralign = 256; } else { this->shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; this->shdr.sh_addralign = 16; } } void add_symbol(Context &ctx, Symbol *sym); void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; void compute_symtab_size(Context &ctx) override; void populate_symtab(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 = 16; } void add_symbol(Context &ctx, Symbol *sym); void copy_buf(Context &ctx) override; void compute_symtab_size(Context &ctx) override; void populate_symtab(Context &ctx) override; std::vector *> symbols; }; template class RelPltSection : public Chunk { public: RelPltSection() { this->name = E::is_rela ? ".rela.plt" : ".rel.plt"; this->shdr.sh_type = E::is_rela ? SHT_RELA : SHT_REL; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_entsize = sizeof(ElfRel); this->shdr.sh_addralign = sizeof(Word); } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; template class RelDynSection : public Chunk { public: RelDynSection() { this->name = E::is_rela ? ".rela.dyn" : ".rel.dyn"; this->shdr.sh_type = E::is_rela ? SHT_RELA : SHT_REL; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_entsize = sizeof(ElfRel); this->shdr.sh_addralign = sizeof(Word); } void update_shdr(Context &ctx) override; void sort(Context &ctx); }; 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 = sizeof(Word); this->shdr.sh_addralign = sizeof(Word); } 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; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; // Offsets in .strtab for ARM32 mapping symbols static constexpr i64 ARM = 1; static constexpr i64 THUMB = 4; static constexpr i64 DATA = 7; }; 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; } 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(Context &ctx) { this->name = ".dynamic"; this->shdr.sh_type = SHT_DYNAMIC; this->shdr.sh_addralign = sizeof(Word); this->shdr.sh_entsize = sizeof(ElfDyn); if (ctx.arg.z_rodynamic) { this->shdr.sh_flags = SHF_ALLOC; this->is_relro = false; } else { this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE; this->is_relro = true; } } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; template ElfSym to_output_esym(Context &ctx, Symbol &sym, u32 st_name, U32 *shndx); 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 = sizeof(Word); } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; template class SymtabShndxSection : public Chunk { public: SymtabShndxSection() { this->name = ".symtab_shndx"; this->shdr.sh_type = SHT_SYMTAB_SHNDX; this->shdr.sh_entsize = 4; this->shdr.sh_addralign = 4; } }; 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 = sizeof(Word); } 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; bool finalized = false; }; 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 = sizeof(Word); } 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; i64 num_buckets = -1; i64 num_bloom = 1; }; template class MergedSection : public Chunk { public: static MergedSection * get_instance(Context &ctx, std::string_view name, i64 type, i64 flags, i64 entsize, i64 addralign); SectionFragment *insert(Context &ctx, 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); ConcurrentMap> map; HyperLogLog estimator; private: MergedSection(std::string_view name, i64 flags, i64 type, i64 entsize); std::vector shard_offsets; }; 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 = sizeof(Word); } void construct(Context &ctx); void apply_eh_reloc(Context &ctx, const 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; i64 num_fdes = 0; }; template class EhFrameRelocSection : public Chunk { public: EhFrameRelocSection() { this->name = E::is_rela ? ".rela.eh_frame" : ".rel.eh_frame"; this->shdr.sh_type = E::is_rela ? SHT_RELA : SHT_REL; this->shdr.sh_flags = SHF_INFO_LINK; this->shdr.sh_addralign = sizeof(Word); this->shdr.sh_entsize = sizeof(ElfRel); } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; template class CopyrelSection : public Chunk { public: CopyrelSection(bool is_relro) { this->name = is_relro ? ".copyrel.rel.ro" : ".copyrel"; this->is_relro = is_relro; this->shdr.sh_type = SHT_NOBITS; this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE; } void add_symbol(Context &ctx, Symbol *sym); i64 get_reldyn_size(Context &ctx) const override { return symbols.size(); } void copy_buf(Context &ctx) override; 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 = sizeof(Word); } 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 NotePackageSection : public Chunk { public: NotePackageSection() { this->name = ".note.package"; this->shdr.sh_type = SHT_NOTE; this->shdr.sh_flags = SHF_ALLOC; this->shdr.sh_addralign = 4; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; }; 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 = sizeof(Word); } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; private: static constexpr i64 ENTRY_SIZE = E::is_64 ? 16 : 12; std::map properties; }; template class GdbIndexSection : public Chunk { public: GdbIndexSection() { this->name = ".gdb_index"; this->shdr.sh_type = SHT_PROGBITS; this->shdr.sh_addralign = 4; } }; template class CompressedSection : public Chunk { public: CompressedSection(Context &ctx, Chunk &chunk); void copy_buf(Context &ctx) override; private: ElfChdr chdr = {}; std::unique_ptr compressor; }; template class RelocSection : public Chunk { public: RelocSection(Context &ctx, OutputSection &osec); void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; private: OutputSection &output_section; std::vector offsets; }; // PT_GNU_RELRO works on page granularity. We want to align its end to // a page boundary. We append this section at end of a segment so that // the segment always ends at a page boundary. template class RelroPaddingSection : public Chunk { public: RelroPaddingSection() { this->name = ".relro_padding"; this->is_relro = true; this->shdr.sh_type = SHT_NOBITS; this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE; this->shdr.sh_addralign = 1; this->shdr.sh_size = 1; } }; template class ComdatGroupSection : public Chunk { public: ComdatGroupSection(Symbol &sym, std::vector *> members) : sym(sym), members(std::move(members)) { this->name = ".group"; this->shdr.sh_type = SHT_GROUP; this->shdr.sh_entsize = 4; this->shdr.sh_addralign = 4; this->shdr.sh_size = this->members.size() * 4 + 4; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; private: Symbol &sym; std::vector *> members; }; // // gdb-index.cc // template void write_gdb_index(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 { // The file priority of the owner file of this comdat section. Atomic owner = -1; }; template struct ComdatGroupRef { ComdatGroup *group; i32 sect_idx; std::span> members; }; template struct MergeableSection { std::pair *, i64> get_fragment(i64 offset); std::string_view get_contents(i64 idx); MergedSection *parent; std::string_view contents; std::vector frag_offsets; std::vector hashes; std::vector *> fragments; u8 p2align = 0; }; // 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; } std::span> get_phdrs(); ElfShdr *find_section(i64 type); virtual void resolve_symbols(Context &ctx) = 0; virtual void mark_live_objects(Context &ctx, std::function *)> feeder) = 0; std::span *> get_global_syms(); std::string_view get_source_name() const; 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; i64 priority; Atomic is_alive = false; std::string_view shstrtab; std::string_view symbol_strtab; bool has_init_array = false; bool has_ctors = false; // 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::vector> local_syms; std::vector> frag_syms; }; template struct ObjectFileExtras {}; template struct ObjectFileExtras { std::optional stack_align; std::optional arch; bool unaligned_access = false; }; template <> struct ObjectFileExtras { InputSection *got2 = nullptr; }; // 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 initialize_symbols(Context &ctx); void initialize_mergeable_sections(Context &ctx); void resolve_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 scan_relocations(Context &ctx); void convert_common_symbols(Context &ctx); void compute_symtab_size(Context &ctx); void populate_symtab(Context &ctx); i64 get_shndx(const ElfSym &esym); InputSection *get_section(const ElfSym &esym); std::string archive_name; std::vector>> sections; std::vector>> mergeable_sections; bool is_in_lib = false; std::vector> elf_sections2; std::vector> cies; std::vector> fdes; BitVector has_symver; std::vector> comdat_groups; std::vector *> eh_frame_sections; bool exclude_libs = false; std::map gnu_properties; bool needs_executable_stack = false; bool is_lto_obj = false; bool is_gcc_offload_obj = false; bool is_rust_obj = false; i64 num_dynrel = 0; i64 reldyn_offset = 0; i64 fde_idx = 0; i64 fde_offset = 0; i64 fde_size = 0; // For ICF std::unique_ptr> llvm_addrsig; // For .gdb_index InputSection *debug_info = nullptr; InputSection *debug_pubnames = nullptr; InputSection *debug_pubtypes = nullptr; // For LTO std::vector> lto_elf_syms; // Target-specific member [[no_unique_address]] ObjectFileExtras extra; private: ObjectFile(Context &ctx, MappedFile *mf, std::string archive_name, bool is_in_lib); void initialize_sections(Context &ctx); void sort_relocations(Context &ctx); void initialize_ehframe_sections(Context &ctx); void parse_note_gnu_property(Context &ctx, const ElfShdr &shdr); void parse_ehframe(Context &ctx); void override_symbol(Context &ctx, Symbol &sym, const ElfSym &esym, i64 symidx); void merge_visibility(Context &ctx, Symbol &sym, u8 visibility); bool has_common_symbol = false; 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::span *> get_symbols_at(Symbol *sym); i64 get_alignment(Symbol *sym); bool is_readonly(Symbol *sym); void mark_live_objects(Context &ctx, std::function *)> feeder) override; void compute_symtab_size(Context &ctx); void populate_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; const ElfShdr *symtab_sec; // Used by get_symbols_at() std::once_flag init_sorted_syms; std::vector *> sorted_syms; }; // // linker-script.cc // template void parse_linker_script(Context &ctx, MappedFile *mf); template std::string_view get_script_output_type(Context &ctx, MappedFile *mf); template void parse_version_script(Context &ctx, MappedFile *mf); struct DynamicPattern { std::string_view pattern; std::string_view source; bool is_cpp = false; }; template std::vector parse_dynamic_list(Context &ctx, std::string_view path); // // 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); // // mapfile.cc // template void print_map(Context &ctx); // // subprocess.cc // std::function fork_child(); template [[noreturn]] void process_run_subcommand(Context &ctx, int argc, char **argv); // // cmdline.cc // template std::vector expand_response_files(Context &ctx, char **argv); template std::vector parse_nonpositional_args(Context &ctx); // // passes.cc // template int redo_main(Context &, int argc, char **argv); template void create_internal_file(Context &); 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 kill_eh_frame_sections(Context &); template void split_section_pieces(Context &); template void resolve_section_pieces(Context &); template void convert_common_symbols(Context &); template void compute_merged_section_sizes(Context &); template void create_output_sections(Context &); template void add_synthetic_symbols(Context &); template void apply_section_align(Context &); template void check_cet_errors(Context &); template void print_dependencies(Context &); template void write_repro_file(Context &); template void check_duplicate_symbols(Context &); template void check_symbol_types(Context &); template void sort_init_fini(Context &); template void sort_ctor_dtor(Context &); template void fixup_ctors_in_init_array(Context &); template void shuffle_sections(Context &); template void compute_section_sizes(Context &); template void sort_output_sections(Context &); template void claim_unresolved_symbols(Context &); template void scan_relocations(Context &); template void compute_imported_symbol_weakness(Context &); template void construct_relr(Context &); template void create_output_symtab(Context &); template void report_undef_errors(Context &); template void create_reloc_sections(Context &); template void copy_chunks(Context &); template void rewrite_endbr(Context &); template void apply_version_script(Context &); template void parse_symbol_version(Context &); template void compute_import_export(Context &); template void compute_address_significance(Context &); template void clear_padding(Context &); template void compute_section_headers(Context &); template i64 set_osec_offsets(Context &); template void fix_synthetic_symbols(Context &); template i64 compress_debug_sections(Context &); template void write_dependency_file(Context &); template void show_stats(Context &); // // arch-arm32.cc // template <> u64 get_eflags(Context &ctx); void fixup_arm_exidx_section(Context &ctx); // // arch-riscv.cc // template class RiscvAttributesSection : public Chunk { public: RiscvAttributesSection() { this->name = ".riscv.attributes"; this->shdr.sh_type = SHT_RISCV_ATTRIBUTES; } void update_shdr(Context &ctx) override; void copy_buf(Context &ctx) override; std::vector contents; }; template u64 get_eflags(Context &ctx); template i64 riscv_resize_sections(Context &ctx); // // arch-ppc64v1.cc // void ppc64v1_rewrite_opd(Context &ctx); void ppc64v1_scan_symbols(Context &ctx); class PPC64OpdSection : public Chunk { public: PPC64OpdSection() { this->name = ".opd"; this->shdr.sh_type = SHT_PROGBITS; this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE; this->shdr.sh_addralign = 8; } void add_symbol(Context &ctx, Symbol *sym); i64 get_reldyn_size(Context &ctx) const override; void copy_buf(Context &ctx) override; static constexpr i64 ENTRY_SIZE = sizeof(Word) * 3; std::vector *> symbols; }; // // arch-ppc64v2.cc // extern const std::vector> ppc64_save_restore_insns; class PPC64SaveRestoreSection : public Chunk { public: PPC64SaveRestoreSection() { this->name = ".save_restore_gprs"; this->shdr.sh_type = SHT_PROGBITS; this->shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; this->shdr.sh_addralign = 16; this->shdr.sh_size = ppc64_save_restore_insns.size() * 4; } void copy_buf(Context &ctx) override; }; template <> u64 get_eflags(Context &ctx); // // arch-sparc.cc // class SparcTlsGetAddrSection : public Chunk { public: SparcTlsGetAddrSection() { this->name = ".tls_get_addr"; this->shdr.sh_type = SHT_PROGBITS; this->shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; this->shdr.sh_addralign = 4; this->shdr.sh_size = 24; } void copy_buf(Context &ctx) override; }; // // arch-alpha.cc // class AlphaGotSection : public Chunk { public: AlphaGotSection() { this->name = ".alpha_got"; this->is_relro = true; 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 // struct BuildId { i64 size() const; enum { NONE, HEX, HASH, UUID } kind = NONE; std::vector value; i64 hash_size = 0; }; typedef enum { COMPRESS_NONE, COMPRESS_ZLIB, COMPRESS_ZSTD } CompressKind; typedef enum { UNRESOLVED_ERROR, UNRESOLVED_WARN, UNRESOLVED_IGNORE, } UnresolvedKind; typedef enum { BSYMBOLIC_NONE, BSYMBOLIC_ALL, BSYMBOLIC_FUNCTIONS, BSYMBOLIC_NON_WEAK, BSYMBOLIC_NON_WEAK_FUNCTIONS, } BsymbolicKind; 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; std::string_view source; std::string_view ver_str; i64 ver_idx = -1; bool is_cpp = false; }; struct SectionOrder { enum { NONE, SECTION, GROUP, ADDR, ALIGN, SYMBOL } type = NONE; std::string name; u64 value = 0; }; // Target-specific context members template struct ContextExtras {}; template struct ContextExtras { RiscvAttributesSection *riscv_attributes = nullptr; }; template <> struct ContextExtras { Symbol *_SDA_BASE_ = nullptr; }; template <> struct ContextExtras { PPC64OpdSection *opd = nullptr; Symbol *TOC = nullptr; }; template <> struct ContextExtras { PPC64SaveRestoreSection *save_restore = nullptr; Symbol *TOC = nullptr; Atomic is_power10 = false; }; template <> struct ContextExtras { SparcTlsGetAddrSection *tls_get_addr_sec = nullptr; 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 // resource management, and other miscellaneous objects. template struct Context { Context() { arg.entry = get_symbol(*this, "_start"); arg.fini = get_symbol(*this, "_fini"); arg.init = get_symbol(*this, "_init"); } 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; MultiGlob undefined_glob; SeparateCodeKind z_separate_code = NOSEPARATE_CODE; ShuffleSectionsKind shuffle_sections = SHUFFLE_SECTIONS_NONE; Symbol *entry = nullptr; Symbol *fini = nullptr; Symbol *init = nullptr; UnresolvedKind unresolved_symbols = UNRESOLVED_ERROR; BsymbolicKind Bsymbolic = BSYMBOLIC_NONE; bool allow_multiple_definition = false; bool apply_dynamic_relocs = true; 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 enable_new_dtags = true; bool execute_only = false; bool export_dynamic = false; bool fatal_warnings = false; bool fork = true; bool gc_sections = false; bool gdb_index = false; bool hash_style_gnu = true; bool hash_style_sysv = true; bool icf = false; bool icf_all = false; bool ignore_data_address_equality = false; bool is_static = false; bool lto_pass2 = false; bool nmagic = false; bool noinhibit_exec = false; bool oformat_binary = false; bool omagic = false; bool pack_dyn_relocs_relr = false; bool perf = false; bool pic = false; bool pie = false; bool print_dependencies = 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 relocatable_merge_sections = false; bool repro = false; bool rosegment = true; bool shared = false; bool start_stop = false; bool stats = false; bool strip_all = false; bool strip_debug = false; bool suppress_warnings = false; bool trace = false; bool undefined_version = 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_dynamic_undefined_weak = true; bool z_execstack = false; bool z_execstack_if_needed = false; bool z_ibt = 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_rewrite_endbr = false; bool z_rodynamic = false; bool z_sectionheader = true; bool z_shstk = false; bool z_start_stop_visibility_protected = false; bool z_text = false; i64 filler = -1; i64 spare_dynamic_tags = 5; i64 spare_program_headers = 0; i64 thread_count = 0; i64 z_stack_size = 0; u64 shuffle_sections_seed; std::string_view emulation; std::optional unique; std::optional physical_image_base; std::string Map; std::string chroot; std::string dependency_file; std::string directory; std::string dynamic_linker; std::string output = "a.out"; std::string package_metadata; std::string plugin; std::string rpaths; std::string soname; std::string sysroot; std::unique_ptr> retain_symbols_file; std::unordered_map section_align; std::unordered_map section_start; std::unordered_set ignore_ir_file; std::unordered_set wrap; std::vector section_order; std::vector *> require_defined; std::vector *> undefined; 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 trace_symbol; u64 image_base = 0x200000; } arg; std::vector version_patterns; std::vector dynamic_list_patterns; i64 default_version = VER_NDX_UNSPECIFIED; i64 page_size = E::page_size; // Reader context bool as_needed = false; bool whole_archive = false; bool is_static; bool in_lib = false; i64 file_priority = 10000; MappedFile *script_file = nullptr; std::unordered_set visited; tbb::task_group tg; bool has_error = false; // Symbol table tbb::concurrent_hash_map, HashCmp> symbol_map; tbb::concurrent_hash_map comdat_groups; tbb::concurrent_vector>> merged_sections; 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>> chunk_pool; tbb::concurrent_vector>> osec_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; std::vector> internal_esyms; // Output buffer std::unique_ptr>> output_file; u8 *buf = nullptr; bool overwrite_output_file = true; std::vector *> chunks; Atomic needs_tlsld = false; Atomic has_textrel = false; Atomic num_ifunc_dynrels = 0; tbb::concurrent_hash_map *, std::vector> undef_errors; // Output chunks OutputEhdr *ehdr = nullptr; OutputShdr *shdr = nullptr; OutputPhdr *phdr = nullptr; InterpSection *interp = nullptr; GotSection *got = nullptr; GotPltSection *gotplt = nullptr; RelPltSection *relplt = nullptr; RelDynSection *reldyn = nullptr; RelrDynSection *relrdyn = nullptr; DynamicSection *dynamic = nullptr; StrtabSection *strtab = nullptr; DynstrSection *dynstr = nullptr; HashSection *hash = nullptr; GnuHashSection *gnu_hash = nullptr; ShstrtabSection *shstrtab = nullptr; PltSection *plt = nullptr; PltGotSection *pltgot = nullptr; SymtabSection *symtab = nullptr; SymtabShndxSection *symtab_shndx = nullptr; DynsymSection *dynsym = nullptr; EhFrameSection *eh_frame = nullptr; EhFrameHdrSection *eh_frame_hdr = nullptr; EhFrameRelocSection *eh_frame_reloc = nullptr; CopyrelSection *copyrel = nullptr; CopyrelSection *copyrel_relro = nullptr; VersymSection *versym = nullptr; VerneedSection *verneed = nullptr; VerdefSection *verdef = nullptr; BuildIdSection *buildid = nullptr; NotePackageSection *note_package = nullptr; NotePropertySection *note_property = nullptr; GdbIndexSection *gdb_index = nullptr; RelroPaddingSection *relro_padding = nullptr; [[no_unique_address]] ContextExtras extra; // For --gdb-index std::span debug_info; std::span debug_abbrev; std::span debug_ranges; std::span debug_addr; std::span debug_rnglists; // For thread-local variables u64 tls_begin = 0; u64 tp_addr = 0; u64 dtp_addr = 0; // Linker-synthesized symbols Symbol *_DYNAMIC = nullptr; Symbol *_GLOBAL_OFFSET_TABLE_ = nullptr; Symbol *_PROCEDURE_LINKAGE_TABLE_ = nullptr; Symbol *_TLS_MODULE_BASE_ = nullptr; Symbol *__GNU_EH_FRAME_HDR = nullptr; Symbol *__bss_start = nullptr; Symbol *__dso_handle = nullptr; Symbol *__ehdr_start = nullptr; Symbol *__executable_start = nullptr; Symbol *__exidx_end = nullptr; Symbol *__exidx_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 std::string_view get_machine_type(Context &ctx, MappedFile *mf); 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); template int elf_main(int argc, char **argv); 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_CPLT = 1 << 2, NEEDS_GOTTP = 1 << 3, NEEDS_TLSGD = 1 << 4, NEEDS_COPYREL = 1 << 5, NEEDS_TLSDESC = 1 << 6, NEEDS_PPC_OPD = 1 << 7, // for PPCv1 }; // A struct to hold target-dependent symbol members. template struct SymbolExtras {}; template struct SymbolExtras { // For range extension thunks i16 thunk_idx = -1; i16 thunk_sym_idx = -1; }; // Flags for Symbol::get_addr() enum { NO_PLT = 1 << 0, // Request an address other than .plt NO_OPD = 1 << 1, // Request an address other than .opd (PPC64V1 only) }; // 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, bool demangle) : nameptr(name.data()), namelen(name.size()), demangle(demangle) {} Symbol(const Symbol &other) : Symbol(other.name(), other.demangle) {} u64 get_addr(Context &ctx, i64 flags = 0) 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; u64 get_opd_addr(Context &ctx) const; u64 get_got_pltgot_addr(Context &ctx) const; void set_got_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_opd_idx(Context &ctx, i32 idx); void set_dynsym_idx(Context &ctx, i32 idx); i32 get_got_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_opd_idx(Context &ctx) const; i32 get_dynsym_idx(Context &ctx) const; bool has_plt(Context &ctx) const; bool has_got(Context &ctx) const { return get_got_idx(ctx) != -1; } bool has_gottp(Context &ctx) const { return get_gottp_idx(ctx) != -1; } bool has_tlsgd(Context &ctx) const { return get_tlsgd_idx(ctx) != -1; } bool has_tlsdesc(Context &ctx) const { return get_tlsdesc_idx(ctx) != -1; } bool has_opd(Context &ctx) const { return get_opd_idx(ctx) != -1; } u32 get_djb_hash(Context &ctx) const; void set_djb_hash(Context &ctx, u32 hash); bool is_absolute() const; bool is_relative() const { return !is_absolute(); } bool is_local(Context &ctx) const; bool is_ifunc() const { return get_type() == STT_GNU_IFUNC; } bool is_pde_ifunc(Context &ctx) const; bool is_remaining_undef_weak() const; bool is_pcrel_linktime_const(Context &ctx) const; bool is_tprel_linktime_const(Context &ctx) const; bool is_tprel_runtime_const(Context &ctx) const; InputSection *get_input_section() const; Chunk *get_output_section() const; SectionFragment *get_frag() const; void set_input_section(InputSection *); void set_output_section(Chunk *); void set_frag(SectionFragment *); void set_name(std::string_view); std::string_view name() const; u32 get_type() const; std::string_view get_version() const; i64 get_output_sym_idx(Context &ctx) const; const ElfSym &esym() const; void add_aux(Context &ctx); // 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 not defined by any input file. InputFile *file = nullptr; // A symbol usually belongs to an input section, but it can belong // to a section fragment, an output section or nothing // (i.e. absolute symbol). `origin` holds one of them. We use the // least significant two bits to distinguish type. enum : uintptr_t { TAG_ABS = 0b00, TAG_ISEC = 0b01, TAG_OSEC = 0b10, TAG_FRAG = 0b11, TAG_MASK = 0b11, }; static_assert(alignof(InputSection) >= 4); static_assert(alignof(Chunk) >= 4); static_assert(alignof(SectionFragment) >= 4); uintptr_t origin = 0; // `value` contains symbol value. If it's an absolute symbol, it is // equivalent to its address. If it belongs to an input section or a // section fragment, value is added to the base of the input section // to yield an address. u64 value = 0; const char *nameptr = nullptr; i32 namelen = 0; // Index into the symbol table of the owner file. i32 sym_idx = -1; i32 aux_idx = -1; u16 ver_idx = VER_NDX_UNSPECIFIED; // `flags` has NEEDS_ flags. Atomic flags = 0; tbb::spin_mutex mu; Atomic visibility = STV_DEFAULT; bool is_weak : 1 = false; bool write_to_symtab : 1 = false; // for --strip-all and the like bool is_traced : 1 = false; // for --trace-symbol bool is_wrapped : 1 = false; // for --wrap // If a symbol can be resolved to a symbol in a different ELF file at // runtime, `is_imported` is true. If a symbol is a dynamic symbol and // can be used by other ELF file 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 file which can be // imported by other definition at runtime. That is actually a usual // exported symbol when creating a DSO. In other words, a dynamic // symbol exported by a DSO is usually imported by itself. // // If is_imported is true and is_exported is false, it is a dynamic // symbol just 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 intercepted 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 in an // executable. If we are creating a DSO, export-only symbols // represent a protected symbol (i.e. a symbol whose visibility is // STV_PROTECTED). bool is_imported : 1 = false; bool is_exported : 1 = false; // `is_canonical` is true if this symbol represents a "canonical" PLT. // Here is the explanation as to what the canonical PLT is. // // In C/C++, the process-wide function pointer equality is guaranteed. // That is, if you take an address of a function `foo`, it's always // evaluated to the same address wherever you do that. // // For the sake of explanation, assume that `libx.so` exports a // function symbol `foo`, and there's a program that uses `libx.so`. // Both `libx.so` and the main executable take the address of `foo`, // which must be evaluated to the same address because of the above // guarantee. // // If the main executable is position-independent code (PIC), `foo` is // evaluated to the beginning of the function code, as you would have // expected. The address of `foo` is stored to GOTs, and the machine // code that takes the address of `foo` reads the GOT entries at // runtime. // // However, if it's not PIC, the main executable's code was compiled // to not use GOT (note that shared objects are always PIC, only // executables can be non-PIC). It instead assumes that `foo` (and any // other global variables/functions) has an address that is fixed at // link-time. This assumption is correct if `foo` is in the same // position-dependent executable, but it's not if `foo` is imported // from some other DSO at runtime. // // In this case, we use the address of the `foo`'s PLT entry in the // main executable (whose address is fixed at link-time) as its // address. In order to guarantee pointer equality, we also need to // fill foo's GOT entries in DSOs with the addres of the foo's PLT // entry instead of `foo`'s real address. We can do that by setting a // symbol value to `foo`'s dynamic symbol. If a symbol value is set, // the dynamic loader initialize `foo`'s GOT entries with that value // instead of the symbol's real address. // // We call such PLT entry in the main executable as "canonical". // If `foo` has a canonical PLT, its address is evaluated to its // canonical PLT's address. Otherwise, it's evaluated to `foo`'s // address. // // Only non-PIC main executables may have canonical PLTs. PIC // executables and shared objects never have a canonical PLT. // // This bit manages if we need to make this symbol's PLT canonical. // This bit is meaningful only when the symbol has a PLT entry. bool is_canonical : 1 = false; // If an input object file is not compiled with -fPIC (or with // -fno-PIC), the file not position independent. That means the // machine code included in the object file does not use GOT to access // global variables. Instead, it assumes that addresses of global // variables are known at link-time. // // Let's say `libx.so` exports a global variable `foo`, and a main // executable uses the variable. If the executable is not compiled // with -fPIC, we can't simply apply a relocation that refers `foo` // because `foo`'s address is not known at link-time. // // In this case, we could print out the "recompile with -fPIC" error // message, but there's a way to workaround. // // The loader supports a feature so-called "copy relocations". // A copy relocation instructs the loader to copy data from a DSO to a // specified location in the main executable. By using this feature, // we can copy `foo`'s data to a BSS region at runtime. With that, // we can apply relocations agianst `foo` as if `foo` existed in the // main executable's BSS area, whose address is known at link-time. // // Copy relocations are used only by position-dependent executables. // Position-independent executables and DSOs don't need them because // they use GOT to access global variables. // // `has_copyrel` is true if we need to emit a copy relocation for this // symbol. If the original symbol in a DSO is in a read-only memory // region, `is_copyrel_readonly` is set to true so that the copied data // will become read-only at run-time. bool has_copyrel : 1 = false; bool is_copyrel_readonly : 1 = false; // For LTO. True if the symbol is referenced by a regular object (as // opposed to IR object). bool referenced_by_regular_obj : 1 = false; // For `-z rewrite-endbr` bool address_taken : 1 = false; // If true, we try to dmenagle the sybmol when printing. bool demangle : 1 = false; // Target-dependent extra members. [[no_unique_address]] SymbolExtras extra; }; template Symbol *get_symbol(Context &ctx, std::string_view key, std::string_view name); template Symbol *get_symbol(Context &ctx, std::string_view name); template std::string_view demangle(const Symbol &sym); template std::ostream &operator<<(std::ostream &out, const Symbol &sym); // // Inline objects and functions // template inline std::ostream & operator<<(std::ostream &out, const InputSection &isec) { out << isec.file << ":(" << isec.name() << ")"; return out; } 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 (shdr().sh_flags & SHF_TLS) ? ".tls_common" : ".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 i64 get_addend(u8 *loc, const ElfRel &rel); template requires E::is_rela && (!is_sh4) inline i64 get_addend(u8 *loc, const ElfRel &rel) { return rel.r_addend; } template i64 get_addend(InputSection &isec, const ElfRel &rel) { return get_addend((u8 *)isec.contents.data() + rel.r_offset, rel); } template void write_addend(u8 *loc, i64 val, const ElfRel &rel); template requires E::is_rela void write_addend(u8 *loc, i64 val, const ElfRel &rel) {} 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 {}; 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 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) if (std::unique_ptr> &m = file.mergeable_sections[file.get_shndx(esym)]) return m->get_fragment(esym.st_value + get_addend(*this, rel)); return {nullptr, 0}; } template u64 InputSection::get_thunk_addr(i64 idx) { if constexpr (needs_thunk) { ThunkRef ref = extra.thunk_refs[idx]; assert(ref.thunk_idx != -1); return output_section->thunks[ref.thunk_idx]->get_addr(ref.sym_idx); } unreachable(); } // Input object files may contain duplicate code for inline functions // and such. Linkers de-duplicate them at link-time. However, linkers // generaly don't remove debug info for de-duplicated functions because // doing that requires parsing the entire debug section. // // Instead, linkers write "tombstone" values to dead debug info records // instead of bogus values so that debuggers can skip them. // // This function returns a tombstone value for the symbol if the symbol // refers a dead debug info section. template inline std::optional InputSection::get_tombstone(Symbol &sym, SectionFragment *frag) { if (frag) return {}; InputSection *isec = sym.get_input_section(); // Setting a tombstone is a special feature for a dead debug section. if (!isec || isec->is_alive) return {}; std::string_view s = name(); if (!s.starts_with(".debug")) return {}; // If the section was dead due to ICF, we don't want to emit debug // info for that section but want to set real values to .debug_line so // that users can set a breakpoint inside a merged section. if (isec->is_killed_by_icf() && s == ".debug_line") return {}; // 0 is an invalid value in most debug info sections, so we use it // as a tombstone value. .debug_loc and .debug_ranges reserve 0 as // the terminator marker, so we use 1 if that's the case. return (s == ".debug_loc" || s == ".debug_ranges") ? 1 : 0; } template inline bool InputSection::is_killed_by_icf() const { return this->leader && this->leader != this; } template std::pair *, i64> MergeableSection::get_fragment(i64 offset) { std::vector &vec = frag_offsets; auto it = std::upper_bound(vec.begin(), vec.end(), offset); i64 idx = it - 1 - vec.begin(); return {fragments[idx], offset - vec[idx]}; } template std::string_view MergeableSection::get_contents(i64 i) { i64 cur = frag_offsets[i]; if (i == frag_offsets.size() - 1) return contents.substr(cur); return contents.substr(cur, frag_offsets[i + 1] - cur); } 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) { 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(); } template u64 Symbol::get_addr(Context &ctx, i64 flags) const { if (SectionFragment *frag = get_frag()) { if (!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 occurs if a // debug info section refers a string constant in .rodata. return 0; } return frag->get_addr(ctx) + value; } if (has_copyrel) { return is_copyrel_readonly ? ctx.copyrel_relro->shdr.sh_addr + value : ctx.copyrel->shdr.sh_addr + value; } if constexpr (is_ppc64v1) if (!(flags & NO_OPD) && has_opd(ctx)) return get_opd_addr(ctx); if (!(flags & NO_PLT) && has_plt(ctx)) { assert(is_imported || is_ifunc()); return get_plt_addr(ctx); } InputSection *isec = get_input_section(); if (!isec) return value; // absolute symbol if (!isec->is_alive) { if (isec->is_killed_by_icf()) return isec->leader->get_addr() + value; if (isec->name() == ".eh_frame") { // .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 LTO is enabled, GCC may add `.lto_priv.` as a symbol // suffix. That's why we use starts_with() instead of `==` here. if (name().starts_with("__EH_FRAME_BEGIN__") || name().starts_with("__EH_FRAME_LIST__") || name().starts_with(".eh_frame_seg") || esym().st_type == STT_SECTION) return ctx.eh_frame->shdr.sh_addr; if (name().starts_with("__FRAME_END__") || name().starts_with("__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 to .eh_frame is not supported: " << *this << " " << *file; } // 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; } template inline u64 Symbol::get_got_addr(Context &ctx) const { return ctx.got->shdr.sh_addr + get_got_idx(ctx) * sizeof(Word); } template inline u64 Symbol::get_gotplt_addr(Context &ctx) const { assert(get_plt_idx(ctx) != -1); return ctx.gotplt->shdr.sh_addr + GotPltSection::HDR_SIZE + get_plt_idx(ctx) * GotPltSection::ENTRY_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) * sizeof(Word); } 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) * sizeof(Word); } 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) * sizeof(Word); } template inline u64 to_plt_offset(i32 pltidx) { if constexpr (is_ppc64v1) { // The PPC64 ELFv1 ABI requires PLT entries to vary in size // depending on their indices. For entries whose PLT index is // less than 32768, the entry size is 8 bytes. Other entries are // 12 bytes long. if (pltidx < 0x8000) return E::plt_hdr_size + pltidx * 8; return E::plt_hdr_size + 0x8000 * 8 + (pltidx - 0x8000) * 12; } else { return E::plt_hdr_size + pltidx * E::plt_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 + to_plt_offset(idx); return ctx.pltgot->shdr.sh_addr + get_pltgot_idx(ctx) * E::pltgot_size; } template inline u64 Symbol::get_opd_addr(Context &ctx) const { assert(get_opd_idx(ctx) != -1); return ctx.extra.opd->shdr.sh_addr + get_opd_idx(ctx) * PPC64OpdSection::ENTRY_SIZE; } template inline u64 Symbol::get_got_pltgot_addr(Context &ctx) const { // An ifunc symbol occupies two consecutive GOT slots in a // position-dependent executable (PDE). The first slot contains the // symbol's PLT address, and the second slot holds the resolved // address. A PDE uses the ifunc symbol's PLT entry as the address // for the symbol, akin to a canonical PLT. // // This function returns the address that the PLT entry should use // to jump to the resolved address. // // Note that we don't use this function for PPC64. In PPC64, symbols // are always accessed through the TOC table regardless of the // -fno-PIE setting. We don't need canonical PLTs on the psABIs too. if (is_pde_ifunc(ctx)) return get_got_addr(ctx) + sizeof(Word); return get_got_addr(ctx); } 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_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_opd_idx(Context &ctx, i32 idx) { assert(aux_idx != -1); assert(ctx.symbol_aux[aux_idx].opd_idx < 0); ctx.symbol_aux[aux_idx].opd_idx = idx; } template inline void Symbol::set_dynsym_idx(Context &ctx, i32 idx) { assert(aux_idx != -1); 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_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_opd_idx(Context &ctx) const { return (aux_idx == -1) ? -1 : ctx.symbol_aux[aux_idx].opd_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 u32 Symbol::get_djb_hash(Context &ctx) const { assert(aux_idx != -1); return ctx.symbol_aux[aux_idx].djb_hash; } template inline void Symbol::set_djb_hash(Context &ctx, u32 hash) { assert(aux_idx != -1); ctx.symbol_aux[aux_idx].djb_hash = hash; } template inline bool Symbol::has_plt(Context &ctx) const { return get_plt_idx(ctx) != -1 || get_pltgot_idx(ctx) != -1; } template inline bool Symbol::is_absolute() const { return !is_imported && !get_frag() && !get_input_section() && !get_output_section(); } template inline bool Symbol::is_local(Context &ctx) const { if (ctx.arg.relocatable) return esym().st_bind == STB_LOCAL; return !is_imported && !is_exported; } template inline bool Symbol::is_pde_ifunc(Context &ctx) const { // Returns true if this is an ifunc tha uses two GOT slots return is_ifunc() && !ctx.arg.pic && !is_ppc64; } // A remaining weak undefined symbol is promoted to a dynamic symbol // in DSO and resolved to 0 in an executable. This function returns // true if it's latter. template inline bool Symbol::is_remaining_undef_weak() const { return !is_imported && esym().is_undef_weak(); } // Returns true if the symbol's PC-relative address is known at link-time. template inline bool Symbol::is_pcrel_linktime_const(Context &ctx) const { return !is_imported && !is_ifunc() && (is_relative() || !ctx.arg.pic); } // Returns true if the symbol's Thread Pointer-relative address is // known at link-time. template inline bool Symbol::is_tprel_linktime_const(Context &ctx) const { assert(get_type() == STT_TLS); return !ctx.arg.shared && !is_imported; } // Returns true if the symbol's Thread Pointer-relative address is // known at load-time. template inline bool Symbol::is_tprel_runtime_const(Context &ctx) const { // Returns true unless we are creating a dlopen'able DSO. assert(get_type() == STT_TLS); return !(ctx.arg.shared && ctx.arg.z_dlopen); } template inline InputSection *Symbol::get_input_section() const { if ((origin & TAG_MASK) == TAG_ISEC) return (InputSection *)(origin & ~TAG_MASK); return nullptr; } template inline Chunk *Symbol::get_output_section() const { if ((origin & TAG_MASK) == TAG_OSEC) return (Chunk *)(origin & ~TAG_MASK); return nullptr; } template inline SectionFragment *Symbol::get_frag() const { if ((origin & TAG_MASK) == TAG_FRAG) return (SectionFragment *)(origin & ~TAG_MASK); return nullptr; } template inline void Symbol::set_input_section(InputSection *isec) { uintptr_t addr = (uintptr_t)isec; assert((addr & TAG_MASK) == 0); origin = addr | TAG_ISEC; } template inline void Symbol::set_output_section(Chunk *osec) { uintptr_t addr = (uintptr_t)osec; assert((addr & TAG_MASK) == 0); origin = addr | TAG_OSEC; } template inline void Symbol::set_frag(SectionFragment *frag) { uintptr_t addr = (uintptr_t)frag; assert((addr & TAG_MASK) == 0); origin = addr | TAG_FRAG; } 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) { std::span vers = ((SharedFile *)file)->version_strings; if (!vers.empty()) return vers[ver_idx]; } return ""; } template inline i64 Symbol::get_output_sym_idx(Context &ctx) const { i64 i = file->output_sym_indices[sym_idx]; assert(i != -1); if (is_local(ctx)) return file->local_symtab_idx + i; return file->global_symtab_idx + i; } template inline const ElfSym &Symbol::esym() const { return file->elf_syms[sym_idx]; } template inline void Symbol::set_name(std::string_view name) { nameptr = name.data(); namelen = name.size(); } template inline std::string_view Symbol::name() const { return {nameptr, (size_t)namelen}; } template inline void Symbol::add_aux(Context &ctx) { if (aux_idx == -1) { aux_idx = ctx.symbol_aux.size(); ctx.symbol_aux.resize(aux_idx + 1); } } inline bool is_c_identifier(std::string_view s) { if (s.empty()) return false; auto is_alpha = [](char c) { return c == '_' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); }; auto is_alnum = [&](char c) { return is_alpha(c) || ('0' <= c && c <= '9'); }; if (!is_alpha(s[0])) return false; for (i64 i = 1; i < s.size(); i++) if (!is_alnum(s[i])) return false; return true; } } // namespace mold::elf