#pragma once #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "elf.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; typedef int8_t i8; typedef int16_t i16; typedef int32_t i32; typedef int64_t i64; static constexpr i64 SECTOR_SIZE = 512; static constexpr i64 PAGE_SIZE = 4096; static constexpr i64 GOT_SIZE = 8; static constexpr i64 PLT_SIZE = 16; static constexpr i64 PLT_GOT_SIZE = 8; static constexpr i64 SHA256_SIZE = 32; class InputFile; class InputSection; class MergedSection; class ObjectFile; class OutputChunk; class OutputSection; class ReadContext; class SharedFile; class Symbol; struct BuildId { i64 size() const; enum { NONE, HEX, HASH, UUID } kind = NONE; std::vector value; i64 hash_size = 0; }; struct Config { BuildId build_id; bool Bsymbolic = false; bool Bsymbolic_functions = false; bool allow_multiple_definition = false; bool demangle = true; bool discard_all = false; bool discard_locals = false; bool eh_frame_hdr = true; bool export_dynamic = 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 pic = false; bool pie = false; bool preload = false; bool print_gc_sections = false; bool print_icf_sections = false; bool print_map = false; bool print_perf = false; bool print_stats = false; bool quick_exit = true; bool relax = true; bool shared = false; bool strip_all = false; bool trace = false; bool z_execstack = false; bool z_now = false; bool z_relro = true; i16 default_version = VER_NDX_GLOBAL; std::vector version_definitions; std::vector> version_patterns; i64 filler = -1; i64 thread_count = -1; std::string dynamic_linker; std::string entry = "_start"; std::string fini = "_fini"; std::string init = "_init"; std::string output; std::string rpaths; std::string soname; std::string sysroot; std::vector exclude_libs; std::vector library_paths; std::vector trace_symbol; std::vector undefined; std::vector build_id_value; u64 image_base = 0x200000; }; inline Config config; void cleanup(); class SyncOut { public: SyncOut(std::ostream &out = std::cout) : out(out) {} ~SyncOut() { static std::mutex mu; std::lock_guard lock(mu); out << ss.str() << "\n"; } template SyncOut &operator<<(T &&val) { ss << std::forward(val); return *this; } private: std::ostream &out; std::stringstream ss; }; class Error { public: Error() { has_error = true; } template Error &operator<<(T &&val) { out << std::forward(val); return *this; } static void checkpoint() { if (!has_error) return; cleanup(); _exit(1); } private: static inline std::atomic_bool has_error = false; SyncOut out{std::cerr}; }; class Fatal { public: [[noreturn]] ~Fatal() { out.~SyncOut(); cleanup(); _exit(1); } template Fatal &operator<<(T &&val) { out << std::forward(val); return *this; } private: SyncOut out{std::cerr}; }; #define unreachable() \ do { Fatal() << "internal error at " << __FILE__ << ":" << __LINE__; } while (0) std::ostream &operator<<(std::ostream &out, const InputFile &file); // // Interned string // namespace tbb { template<> struct tbb_hash_compare { static size_t hash(const std::string_view &k) { return std::hash()(k); } static bool equal(const std::string_view &k1, const std::string_view &k2) { return k1 == k2; } }; } template class ConcurrentMap { public: ValueT *insert(std::string_view key, const ValueT &val) { typename decltype(map)::const_accessor acc; map.insert(acc, std::make_pair(key, val)); return const_cast(&acc->second); } tbb::concurrent_hash_map map; }; // // Symbol // struct SectionFragment { SectionFragment(MergedSection *sec, std::string_view data) : output_section(*sec), data(data) {} SectionFragment(const SectionFragment &other) : output_section(other.output_section), data(other.data), offset(other.offset), alignment(other.alignment.load()), is_alive(other.is_alive.load()) {} inline u64 get_addr() const; MergedSection &output_section; std::string_view data; u32 offset = -1; std::atomic_uint16_t alignment = 1; std::atomic_bool is_alive = !config.gc_sections; }; struct SectionFragmentRef { SectionFragment *frag = nullptr; i32 addend = 0; }; enum { NEEDS_GOT = 1 << 0, NEEDS_PLT = 1 << 1, NEEDS_GOTTPOFF = 1 << 2, NEEDS_TLSGD = 1 << 3, NEEDS_TLSLD = 1 << 4, NEEDS_COPYREL = 1 << 5, NEEDS_DYNSYM = 1 << 6, }; class Symbol { public: Symbol() = default; Symbol(std::string_view name) : name(name) {} Symbol(const Symbol &other) : name(other.name) {} static Symbol *intern(std::string_view key, std::string_view name) { static ConcurrentMap map; return map.insert(key, {name}); } static Symbol *intern(std::string_view name) { return intern(name, name); } static Symbol *intern_alloc(std::string name) { return intern(*new std::string(name)); } inline u64 get_addr() const; inline u64 get_got_addr() const; inline u64 get_gotplt_addr() const; inline u64 get_gottpoff_addr() const; inline u64 get_tlsgd_addr() const; inline u64 get_plt_addr() const; inline bool is_alive() const; inline bool is_absolute() const; inline bool is_relative() const { return !is_absolute(); } inline bool is_undef() const; inline bool is_undef_weak() const; inline u32 get_type() const; inline std::string_view get_version() const; std::string_view name; InputFile *file = nullptr; const ElfSym *esym = nullptr; InputSection *input_section = nullptr; SectionFragment *frag = nullptr; u64 value = -1; u32 got_idx = -1; u32 gotplt_idx = -1; u32 gottpoff_idx = -1; u32 tlsgd_idx = -1; u32 plt_idx = -1; u32 dynsym_idx = -1; u16 shndx = 0; u16 ver_idx = 0; tbb::spin_mutex mu; std::atomic_uint8_t visibility = STV_DEFAULT; std::atomic_uint8_t flags = 0; u8 is_lazy : 1 = false; u8 is_weak : 1 = false; u8 write_to_symtab : 1 = false; u8 traced : 1 = false; u8 has_copyrel : 1 = false; u8 copyrel_readonly : 1 = false; u8 is_imported : 1 = false; u8 is_exported : 1 = false; }; std::ostream &operator<<(std::ostream &out, const Symbol &sym); // // input_sections.cc // enum RelType : u8 { R_NONE = 1, R_ABS, R_DYN, R_BASEREL, R_PC, R_GOT, R_GOTPC, R_GOTPCREL, R_TLSGD, R_TLSGD_RELAX_LE, R_TLSLD, R_TLSLD_RELAX_LE, R_DTPOFF, R_TPOFF, R_GOTTPOFF, }; struct EhReloc { Symbol &sym; u32 type; u32 offset; i64 addend; }; inline bool operator==(const EhReloc &a, const EhReloc &b) { return std::tuple(&a.sym, a.type, a.offset, a.addend) == std::tuple(&b.sym, b.type, b.offset, b.addend); } struct FdeRecord { FdeRecord(std::string_view contents, std::vector &&rels, u32 cie_idx) : contents(contents), rels(std::move(rels)), cie_idx(cie_idx) {} FdeRecord(const FdeRecord &&other) : contents(other.contents), rels(std::move(other.rels)), cie_idx(other.cie_idx), offset(other.offset), is_alive(other.is_alive.load()) {} std::string_view contents; std::vector rels; u32 cie_idx = -1; u32 offset = -1; std::atomic_bool is_alive = true; }; struct CieRecord { bool should_merge(const CieRecord &other) const; std::string_view contents; std::vector rels; std::vector fdes; // For .eh_frame u32 offset = -1; u32 leader_offset = -1; u32 fde_size = -1; // For .eh_frame_hdr u32 num_fdes = 0; u32 fde_idx = -1; // For ICF u32 icf_idx = -1; }; class InputSection { public: static InputSection *create(ObjectFile &file, const ElfShdr *shdr, std::string_view name, i64 section_idx); void scan_relocations(); void report_undefined_symbols(); void copy_buf(); void apply_reloc_alloc(u8 *base); void apply_reloc_nonalloc(u8 *base); void kill(); inline i64 get_priority() const; inline u64 get_addr() const; ObjectFile &file; const ElfShdr &shdr; OutputSection *output_section = nullptr; std::string_view name; std::string_view contents; u32 offset = -1; std::span rels; std::vector has_fragments; std::vector rel_fragments; std::vector rel_types; std::span fdes; u64 reldyn_offset = 0; u32 section_idx = -1; bool is_ehframe = false; // For COMDAT de-duplication and garbage collection bool is_alive = true; // 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; private: InputSection(ObjectFile &file, const ElfShdr &shdr, std::string_view name, i64 section_idx, std::string_view contents, OutputSection *osec); }; // // output_chunks.cc // bool is_relro(OutputChunk *chunk); class OutputChunk { public: enum Kind : u8 { HEADER, REGULAR, SYNTHETIC }; virtual void copy_buf() {} virtual void update_shdr() {} std::string_view name; i64 shndx = 0; Kind kind; bool starts_new_ptload = false; ElfShdr shdr = { .sh_addralign = 1 }; protected: OutputChunk(Kind kind) : kind(kind) {} }; // ELF header class OutputEhdr : public OutputChunk { public: OutputEhdr() : OutputChunk(HEADER) { shdr.sh_flags = SHF_ALLOC; shdr.sh_size = sizeof(ElfEhdr); } void copy_buf() override; }; // Section header class OutputShdr : public OutputChunk { public: OutputShdr() : OutputChunk(HEADER) { shdr.sh_flags = SHF_ALLOC; } void update_shdr() override; void copy_buf() override; }; // Program header class OutputPhdr : public OutputChunk { public: OutputPhdr() : OutputChunk(HEADER) { shdr.sh_flags = SHF_ALLOC; } void update_shdr() override; void copy_buf() override; }; class InterpSection : public OutputChunk { public: InterpSection() : OutputChunk(SYNTHETIC) { name = ".interp"; shdr.sh_type = SHT_PROGBITS; shdr.sh_flags = SHF_ALLOC; shdr.sh_size = config.dynamic_linker.size() + 1; } void copy_buf() override; }; // Sections class OutputSection : public OutputChunk { public: static OutputSection * get_instance(std::string_view name, u64 type, u64 flags); OutputSection(std::string_view name, u32 type, u64 flags) : OutputChunk(REGULAR) { this->name = name; shdr.sh_type = type; shdr.sh_flags = flags; idx = instances.size(); instances.push_back(this); } void copy_buf() override; static inline std::vector instances; std::vector members; u32 idx; }; class GotSection : public OutputChunk { public: GotSection() : OutputChunk(SYNTHETIC) { name = ".got"; shdr.sh_type = SHT_PROGBITS; shdr.sh_flags = SHF_ALLOC | SHF_WRITE; shdr.sh_addralign = GOT_SIZE; } void add_got_symbol(Symbol *sym); void add_gottpoff_symbol(Symbol *sym); void add_tlsgd_symbol(Symbol *sym); void add_tlsld(); u64 get_tlsld_addr() const { assert(tlsld_idx != -1); return shdr.sh_addr + tlsld_idx * GOT_SIZE; } void copy_buf() override; std::vector got_syms; std::vector gottpoff_syms; std::vector tlsgd_syms; u32 tlsld_idx = -1; }; class GotPltSection : public OutputChunk { public: GotPltSection() : OutputChunk(SYNTHETIC) { name = ".got.plt"; shdr.sh_type = SHT_PROGBITS; shdr.sh_flags = SHF_ALLOC | SHF_WRITE; shdr.sh_addralign = GOT_SIZE; shdr.sh_size = GOT_SIZE * 3; } void copy_buf() override; }; class PltSection : public OutputChunk { public: PltSection() : OutputChunk(SYNTHETIC) { name = ".plt"; shdr.sh_type = SHT_PROGBITS; shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; shdr.sh_addralign = 8; shdr.sh_size = PLT_SIZE; } void add_symbol(Symbol *sym); void copy_buf() override; std::vector symbols; }; class PltGotSection : public OutputChunk { public: PltGotSection() : OutputChunk(SYNTHETIC) { name = ".plt.got"; shdr.sh_type = SHT_PROGBITS; shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; shdr.sh_addralign = 8; } void add_symbol(Symbol *sym); void copy_buf() override; std::vector symbols; }; class RelPltSection : public OutputChunk { public: RelPltSection() : OutputChunk(SYNTHETIC) { name = ".rela.plt"; shdr.sh_type = SHT_RELA; shdr.sh_flags = SHF_ALLOC; shdr.sh_entsize = sizeof(ElfRela); shdr.sh_addralign = 8; } void update_shdr() override; void copy_buf() override; }; class RelDynSection : public OutputChunk { public: RelDynSection() : OutputChunk(SYNTHETIC) { name = ".rela.dyn"; shdr.sh_type = SHT_RELA; shdr.sh_flags = SHF_ALLOC; shdr.sh_entsize = sizeof(ElfRela); shdr.sh_addralign = 8; } void update_shdr() override; void copy_buf() override; }; class StrtabSection : public OutputChunk { public: StrtabSection() : OutputChunk(SYNTHETIC) { name = ".strtab"; shdr.sh_type = SHT_STRTAB; shdr.sh_size = 1; } void update_shdr() override; }; class ShstrtabSection : public OutputChunk { public: ShstrtabSection() : OutputChunk(SYNTHETIC) { name = ".shstrtab"; shdr.sh_type = SHT_STRTAB; } void update_shdr() override; void copy_buf() override; }; class DynstrSection : public OutputChunk { public: DynstrSection() : OutputChunk(SYNTHETIC) { name = ".dynstr"; shdr.sh_type = SHT_STRTAB; shdr.sh_flags = SHF_ALLOC; shdr.sh_size = 1; } i64 add_string(std::string_view str); i64 find_string(std::string_view str); void copy_buf() override; private: std::unordered_map strings; }; class DynamicSection : public OutputChunk { public: DynamicSection() : OutputChunk(SYNTHETIC) { name = ".dynamic"; shdr.sh_type = SHT_DYNAMIC; shdr.sh_flags = SHF_ALLOC | SHF_WRITE; shdr.sh_addralign = 8; shdr.sh_entsize = sizeof(ElfDyn); } void update_shdr() override; void copy_buf() override; }; class SymtabSection : public OutputChunk { public: SymtabSection() : OutputChunk(SYNTHETIC) { name = ".symtab"; shdr.sh_type = SHT_SYMTAB; shdr.sh_entsize = sizeof(ElfSym); shdr.sh_addralign = 8; shdr.sh_size = sizeof(ElfSym); } void update_shdr() override; void copy_buf() override; }; class DynsymSection : public OutputChunk { public: DynsymSection() : OutputChunk(SYNTHETIC) { name = ".dynsym"; shdr.sh_type = SHT_DYNSYM; shdr.sh_flags = SHF_ALLOC; shdr.sh_entsize = sizeof(ElfSym); shdr.sh_addralign = 8; shdr.sh_size = sizeof(ElfSym); } void add_symbol(Symbol *sym); void sort_symbols(); void update_shdr() override; void copy_buf() override; std::vector symbols = {nullptr}; std::vector name_indices = {(u32)-1}; }; class HashSection : public OutputChunk { public: HashSection() : OutputChunk(SYNTHETIC) { name = ".hash"; shdr.sh_type = SHT_HASH; shdr.sh_flags = SHF_ALLOC; shdr.sh_entsize = 4; shdr.sh_addralign = 4; } void update_shdr() override; void copy_buf() override; }; class GnuHashSection : public OutputChunk { public: GnuHashSection() : OutputChunk(SYNTHETIC) { name = ".gnu.hash"; shdr.sh_type = SHT_GNU_HASH; shdr.sh_flags = SHF_ALLOC; shdr.sh_addralign = 8; } void update_shdr() override; void copy_buf() override; static constexpr i64 LOAD_FACTOR = 8; static constexpr i64 HEADER_SIZE = 16; static constexpr i64 BLOOM_SHIFT = 26; static constexpr i64 ELFCLASS_BITS = 64; u32 num_buckets = -1; u32 symoffset = -1; u32 num_bloom = 1; }; class MergedSection : public OutputChunk { public: static MergedSection * get_instance(std::string_view name, u64 type, u64 flags); SectionFragment *insert(std::string_view data, i64 alignment); void assign_offsets(); void copy_buf() override; static inline std::vector instances; private: typedef tbb::concurrent_hash_map MapTy; static constexpr i64 NUM_SHARDS = 32; MergedSection(std::string_view name, u64 flags, u32 type) : OutputChunk(SYNTHETIC) { this->name = name; shdr.sh_flags = flags; shdr.sh_type = type; } MapTy maps[NUM_SHARDS]; std::vector fragments; }; class EhFrameSection : public OutputChunk { public: EhFrameSection() : OutputChunk(SYNTHETIC) { name = ".eh_frame"; shdr.sh_type = SHT_X86_64_UNWIND; shdr.sh_flags = SHF_ALLOC; shdr.sh_addralign = 8; } void construct(); void copy_buf() override; u64 get_addr(const Symbol &sym); std::vector cies; u32 num_fdes = 0; }; class EhFrameHdrSection : public OutputChunk { public: EhFrameHdrSection() : OutputChunk(SYNTHETIC) { name = ".eh_frame_hdr"; shdr.sh_type = SHT_PROGBITS; shdr.sh_flags = SHF_ALLOC; shdr.sh_addralign = 4; shdr.sh_size = HEADER_SIZE; } static constexpr i64 HEADER_SIZE = 12; }; class CopyrelSection : public OutputChunk { public: CopyrelSection(std::string_view name) : OutputChunk(SYNTHETIC) { this->name = name; shdr.sh_type = SHT_NOBITS; shdr.sh_flags = SHF_ALLOC | SHF_WRITE; shdr.sh_addralign = 32; } void add_symbol(Symbol *sym); std::vector symbols; }; class VersymSection : public OutputChunk { public: VersymSection() : OutputChunk(SYNTHETIC) { name = ".gnu.version"; shdr.sh_type = SHT_GNU_VERSYM; shdr.sh_flags = SHF_ALLOC; shdr.sh_entsize = 2; shdr.sh_addralign = 2; } void update_shdr() override; void copy_buf() override; std::vector contents; }; class VerneedSection : public OutputChunk { public: VerneedSection() : OutputChunk(SYNTHETIC) { name = ".gnu.version_r"; shdr.sh_type = SHT_GNU_VERNEED; shdr.sh_flags = SHF_ALLOC; shdr.sh_addralign = 8; } void update_shdr() override; void copy_buf() override; std::vector contents; }; class VerdefSection : public OutputChunk { public: VerdefSection() : OutputChunk(SYNTHETIC) { name = ".gnu.version_d"; shdr.sh_type = SHT_GNU_VERDEF; shdr.sh_flags = SHF_ALLOC; shdr.sh_addralign = 8; } void update_shdr() override; void copy_buf() override; std::vector contents; }; class BuildIdSection : public OutputChunk { public: BuildIdSection() : OutputChunk(SYNTHETIC) { name = ".note.gnu.build-id"; shdr.sh_type = SHT_NOTE; shdr.sh_flags = SHF_ALLOC; shdr.sh_addralign = 4; shdr.sh_size = 1; } void update_shdr() override; void copy_buf() override; void write_buildid(i64 filesize); static constexpr i64 HEADER_SIZE = 16; }; bool is_c_identifier(std::string_view name); std::vector create_phdr(); // // object_file.cc // struct ComdatGroup { ComdatGroup() = default; ComdatGroup(const ComdatGroup &other) : owner(other.owner.load()) {} std::atomic owner = nullptr; }; class MemoryMappedFile { public: static MemoryMappedFile *open(std::string path); static MemoryMappedFile *must_open(std::string path); MemoryMappedFile(std::string name, u8 *data, u64 size, u64 mtime = 0) : name(name), data_(data), size_(size), mtime(mtime) {} MemoryMappedFile() = delete; MemoryMappedFile *slice(std::string name, u64 start, u64 size); u8 *data(); i64 size() const { return size_; } std::string_view get_contents() { return std::string_view((char *)data(), size()); } std::string name; i64 mtime = 0; private: std::mutex mu; MemoryMappedFile *parent; std::atomic data_; i64 size_ = 0; }; class InputFile { public: InputFile(MemoryMappedFile *mb); InputFile() : name("") {} std::string_view get_string(const ElfShdr &shdr); std::string_view get_string(i64 idx); MemoryMappedFile *mb; std::span elf_sections; std::vector symbols; std::string name; bool is_dso = false; u32 priority; std::atomic_bool is_alive = false; protected: template std::span get_data(const ElfShdr &shdr); template std::span get_data(i64 idx); ElfShdr *find_section(i64 type); std::string_view shstrtab; }; class ObjectFile : public InputFile { public: ObjectFile(MemoryMappedFile *mb, std::string archive_name, bool is_in_lib); ObjectFile(); void parse(); void resolve_lazy_symbols(); void resolve_regular_symbols(); void mark_live_objects(std::function feeder); void handle_undefined_weak_symbols(); void resolve_comdat_groups(); void eliminate_duplicate_comdat_groups(); void claim_unresolved_symbols(); void scan_relocations(); void convert_common_symbols(); void compute_symtab(); void write_symtab(); inline i64 get_shndx(const ElfSym &esym); inline InputSection *get_section(const ElfSym &esym); inline std::span get_global_syms(); std::string archive_name; std::vector sections; std::span elf_syms; i64 first_global = 0; const bool is_in_lib = false; std::vector cies; std::vector symvers; bool exclude_libs = false; u64 num_dynrel = 0; u64 reldyn_offset = 0; u64 local_symtab_offset = 0; u64 global_symtab_offset = 0; u64 num_local_symtab = 0; u64 num_global_symtab = 0; u64 strtab_offset = 0; u64 strtab_size = 0; private: void initialize_sections(); void initialize_symbols(); void initialize_mergeable_sections(); void initialize_ehframe_sections(); void read_ehframe(InputSection &isec); void maybe_override_symbol(Symbol &sym, i64 symidx); void merge_visibility(Symbol &sym, u8 visibility); std::vector>> comdat_groups; std::vector sym_fragments; bool has_common_symbol; std::string_view symbol_strtab; const ElfShdr *symtab_sec; std::span symtab_shndx_sec; }; class SharedFile : public InputFile { public: SharedFile(MemoryMappedFile *mb, bool as_needed) : InputFile(mb) { is_alive = !as_needed; } void parse(); void resolve_symbols(); std::vector find_aliases(Symbol *sym); bool is_readonly(Symbol *sym); std::string_view soname; std::vector version_strings; std::vector undefs; private: std::string_view get_soname(); void maybe_override_symbol(Symbol &sym, const ElfSym &esym); std::vector read_verdef(); std::vector elf_syms; std::vector versyms; std::string_view symbol_strtab; const ElfShdr *symtab_sec; }; inline std::ostream &operator<<(std::ostream &out, const InputSection &isec) { out << isec.file << ":(" << isec.name << ")"; return out; } // // archive_file.cc // std::vector read_archive_members(MemoryMappedFile *mb); std::vector read_fat_archive_members(MemoryMappedFile *mb); std::vector read_thin_archive_members(MemoryMappedFile *mb); // // linker_script.cc // void parse_linker_script(MemoryMappedFile *mb, ReadContext &ctx); void parse_version_script(std::string path); void parse_dynamic_list(std::string path); // // output_file.cc // class OutputFile { public: static OutputFile *open(std::string path, u64 filesize); virtual void close() = 0; u8 *buf; static inline char *tmpfile; protected: OutputFile(std::string path, u64 filesize) : path(path), filesize(filesize) {} std::string path; u64 filesize; }; // // filepath.cc // std::string path_dirname(std::string_view path); std::string path_basename(std::string_view path); // // perf.cc // class Counter { public: Counter(std::string_view name, i64 value = 0) : name(name), values(value) { static std::mutex mu; std::lock_guard lock(mu); instances.push_back(this); } Counter &operator++(int) { if (enabled) values.local()++; return *this; } Counter &operator+=(int delta) { if (enabled) values.local() += delta; return *this; } static void print(); static inline bool enabled = false; private: i64 get_value(); std::string_view name; tbb::enumerable_thread_specific values; static inline std::vector instances; }; struct TimerRecord { TimerRecord(std::string name); void stop(); std::string name; i64 start; i64 end; i64 user; i64 sys; bool stopped = false; }; class Timer { public: Timer(std::string name); ~Timer(); void stop(); static void print(); private: static inline std::vector records; TimerRecord *record; }; // // gc_sections.cc // void gc_sections(); // // icf.cc // void icf_sections(); // // mapfile.cc // void print_map(); // // subprocess.cc // inline char *socket_tmpfile; std::function fork_child(); bool resume_daemon(char **argv, i64 *code); void daemonize(char **argv, std::function *wait_for_client, std::function *on_complete); // // commandline.cc // std::vector expand_response_files(char **argv); bool read_flag(std::span &args, std::string name); bool read_arg(std::span &args, std::string_view &arg, std::string name); void parse_nonpositional_args(std::span args, std::vector &remaining); // // main.cc // class ReadContext { public: ReadContext(bool x) : is_preloading(x) {} std::function parse; bool as_needed = false; bool whole_archive = false; bool is_preloading = false; tbb::task_group tg; }; MemoryMappedFile *find_library(std::string path, std::span lib_paths); void read_file(MemoryMappedFile *mb, ReadContext &ctx); // // Inline objects and functions // namespace out { inline std::vector objs; inline std::vector dsos; inline std::vector chunks; inline u8 *buf; inline ObjectFile *internal_obj; inline OutputEhdr *ehdr; inline OutputShdr *shdr; inline OutputPhdr *phdr; inline InterpSection *interp; inline GotSection *got; inline GotPltSection *gotplt; inline RelPltSection *relplt; inline RelDynSection *reldyn; inline DynamicSection *dynamic; inline StrtabSection *strtab; inline DynstrSection *dynstr; inline HashSection *hash; inline GnuHashSection *gnu_hash; inline ShstrtabSection *shstrtab; inline PltSection *plt; inline PltGotSection *pltgot; inline SymtabSection *symtab; inline DynsymSection *dynsym; inline EhFrameSection *eh_frame; inline EhFrameHdrSection *eh_frame_hdr; inline CopyrelSection *copyrel; inline CopyrelSection *copyrel_relro; inline VersymSection *versym; inline VerneedSection *verneed; inline VerdefSection *verdef; inline BuildIdSection *buildid; inline u64 tls_begin; inline u64 tls_end; inline Symbol *__bss_start; inline Symbol *__ehdr_start; inline Symbol *__rela_iplt_start; inline Symbol *__rela_iplt_end; inline Symbol *__init_array_start; inline Symbol *__init_array_end; inline Symbol *__fini_array_start; inline Symbol *__fini_array_end; inline Symbol *__preinit_array_start; inline Symbol *__preinit_array_end; inline Symbol *_DYNAMIC; inline Symbol *_GLOBAL_OFFSET_TABLE_; inline Symbol *__GNU_EH_FRAME_HDR; inline Symbol *_end; inline Symbol *_etext; inline Symbol *_edata; inline Symbol *__executable_start; } inline u64 align_to(u64 val, u64 align) { if (align == 0) return val; assert(__builtin_popcount(align) == 1); return (val + align - 1) & ~(align - 1); } inline u64 next_power_of_two(u64 val) { assert(val >> 63 == 0); if (val == 0 || val == 1) return 1; return (u64)1 << (64 - __builtin_clzl(val - 1)); } inline bool Symbol::is_alive() const { if (frag) return frag->is_alive; if (input_section) return input_section->is_alive; return true; } inline bool Symbol::is_absolute() const { if (file == out::internal_obj) return false; if (file->is_dso) return esym->is_abs(); return input_section == nullptr; } inline bool Symbol::is_undef() const { return esym->is_undef() && esym->st_bind != STB_WEAK; } inline bool Symbol::is_undef_weak() const { return esym->is_undef() && esym->st_bind == STB_WEAK; } inline u32 Symbol::get_type() const { if (esym->st_type == STT_GNU_IFUNC && file->is_dso) return STT_FUNC; return esym->st_type; } inline std::string_view Symbol::get_version() const { if (file->is_dso) return ((SharedFile *)file)->version_strings[ver_idx]; return ""; } inline u64 Symbol::get_addr() const { if (frag) { if (frag->is_alive) return frag->get_addr() + value; return 0; // todo: do not return 0 } if (has_copyrel) { return copyrel_readonly ? out::copyrel_relro->shdr.sh_addr + value : out::copyrel->shdr.sh_addr + value; } if (plt_idx != -1 && esym->st_type == STT_GNU_IFUNC) return get_plt_addr(); if (input_section) { if (input_section->is_ehframe) return out::eh_frame->get_addr(*this); if (!input_section->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 input_section->get_addr() + value; } if (plt_idx != -1) return get_plt_addr(); return value; } inline u64 Symbol::get_got_addr() const { assert(got_idx != -1); return out::got->shdr.sh_addr + got_idx * GOT_SIZE; } inline u64 Symbol::get_gotplt_addr() const { assert(gotplt_idx != -1); return out::gotplt->shdr.sh_addr + gotplt_idx * GOT_SIZE; } inline u64 Symbol::get_gottpoff_addr() const { assert(gottpoff_idx != -1); return out::got->shdr.sh_addr + gottpoff_idx * GOT_SIZE; } inline u64 Symbol::get_tlsgd_addr() const { assert(tlsgd_idx != -1); return out::got->shdr.sh_addr + tlsgd_idx * GOT_SIZE; } inline u64 Symbol::get_plt_addr() const { assert(plt_idx != -1); if (got_idx == -1) return out::plt->shdr.sh_addr + plt_idx * PLT_SIZE; return out::pltgot->shdr.sh_addr + plt_idx * PLT_GOT_SIZE; } inline u64 SectionFragment::get_addr() const { return output_section.shdr.sh_addr + offset; } inline u64 InputSection::get_addr() const { return output_section->shdr.sh_addr + offset; } inline i64 InputSection::get_priority() const { return ((i64)file.priority << 32) | section_idx; } inline i64 ObjectFile::get_shndx(const ElfSym &esym) { if (esym.st_shndx == SHN_XINDEX) return symtab_shndx_sec[&esym - &elf_syms[0]]; return esym.st_shndx; } inline InputSection *ObjectFile::get_section(const ElfSym &esym) { return sections[get_shndx(esym)]; } std::span ObjectFile::get_global_syms() { return std::span(symbols).subspan(first_global); } inline u32 elf_hash(std::string_view name) { u32 h = 0; for (u8 c : name) { h = (h << 4) + c; u32 g = h & 0xf0000000; if (g != 0) h ^= g >> 24; h &= ~g; } return h; } inline u32 gnu_hash(std::string_view name) { u32 h = 5381; for (u8 c : name) h = (h << 5) + h + c; return h; } inline void write_string(u8 *buf, std::string_view str) { memcpy(buf, str.data(), str.size()); buf[str.size()] = '\0'; } template inline void write_vector(u8 *buf, const std::vector &vec) { memcpy(buf, vec.data(), vec.size() * sizeof(T)); } template inline void append(std::vector &vec1, std::vector vec2) { vec1.insert(vec1.end(), vec2.begin(), vec2.end()); } template inline std::vector flatten(std::vector> &vec) { std::vector ret; for (std::vector &v : vec) append(ret, v); return ret; } template inline void erase(std::vector &vec, U pred) { vec.erase(std::remove_if(vec.begin(), vec.end(), pred), vec.end()); } template inline void sort(T &vec, U less) { std::stable_sort(vec.begin(), vec.end(), less); }