#include "mold.h" #include #include #include #include #include #include #include namespace mold::elf { template void apply_exclude_libs(Context &ctx) { Timer t(ctx, "apply_exclude_libs"); if (ctx.arg.exclude_libs.empty()) return; std::unordered_set set(ctx.arg.exclude_libs.begin(), ctx.arg.exclude_libs.end()); for (ObjectFile *file : ctx.objs) if (!file->archive_name.empty()) if (set.contains("ALL") || set.contains(path_filename(file->archive_name))) file->exclude_libs = true; } template void create_synthetic_sections(Context &ctx) { auto add = [&](auto &chunk) { ctx.chunks.push_back(chunk.get()); }; add(ctx.ehdr = std::make_unique>()); add(ctx.phdr = std::make_unique>()); add(ctx.shdr = std::make_unique>()); add(ctx.got = std::make_unique>()); add(ctx.gotplt = std::make_unique>()); add(ctx.reldyn = std::make_unique>()); add(ctx.relplt = std::make_unique>()); add(ctx.strtab = std::make_unique>()); add(ctx.shstrtab = std::make_unique>()); add(ctx.plt = std::make_unique>()); add(ctx.pltgot = std::make_unique>()); add(ctx.symtab = std::make_unique>()); add(ctx.dynsym = std::make_unique>()); add(ctx.dynstr = std::make_unique>()); add(ctx.eh_frame = std::make_unique>()); add(ctx.dynbss = std::make_unique>(false)); add(ctx.dynbss_relro = std::make_unique>(true)); if (!ctx.arg.dynamic_linker.empty()) add(ctx.interp = std::make_unique>()); if (ctx.arg.build_id.kind != BuildId::NONE) add(ctx.buildid = std::make_unique>()); if (ctx.arg.eh_frame_hdr) add(ctx.eh_frame_hdr = std::make_unique>()); if (ctx.arg.hash_style_sysv) add(ctx.hash = std::make_unique>()); if (ctx.arg.hash_style_gnu) add(ctx.gnu_hash = std::make_unique>()); if (!ctx.arg.version_definitions.empty()) add(ctx.verdef = std::make_unique>()); add(ctx.dynamic = std::make_unique>()); add(ctx.versym = std::make_unique>()); add(ctx.verneed = std::make_unique>()); add(ctx.note_property = std::make_unique>()); if (ctx.arg.repro) add(ctx.repro = std::make_unique>()); } template void resolve_symbols(Context &ctx) { Timer t(ctx, "resolve_obj_symbols"); // Register object symbols tbb::parallel_for_each(ctx.objs, [&](ObjectFile *file) { if (file->is_in_lib) file->resolve_lazy_symbols(ctx); else file->resolve_regular_symbols(ctx); }); // Register DSO symbols tbb::parallel_for_each(ctx.dsos, [&](SharedFile *file) { file->resolve_dso_symbols(ctx); }); // Mark reachable objects to decide which files to include // into an output. std::vector *> live_objs = ctx.objs; erase(live_objs, [](InputFile *file) { return !file->is_alive; }); auto load = [&](std::string_view name) { if (InputFile *file = intern(ctx, name)->file) if (!file->is_alive.exchange(true) && !file->is_dso) live_objs.push_back((ObjectFile *)file); }; for (std::string_view name : ctx.arg.undefined) load(name); for (std::string_view name : ctx.arg.require_defined) load(name); tbb::parallel_for_each(live_objs, [&](ObjectFile *file, tbb::feeder *> &feeder) { file->mark_live_objects(ctx, [&](ObjectFile *obj) { feeder.add(obj); }); }); // Remove symbols of eliminated objects. tbb::parallel_for_each(ctx.objs, [](ObjectFile *file) { if (!file->is_alive) for (Symbol *sym : file->get_global_syms()) if (sym->file == file) new (sym) Symbol(sym->name()); }); // Eliminate unused archive members. erase(ctx.objs, [](InputFile *file) { return !file->is_alive; }); // Mark live DSOs tbb::parallel_for_each(ctx.objs, [](ObjectFile *file) { for (i64 i = file->first_global; i < file->elf_syms.size(); i++) { const ElfSym &esym = file->elf_syms[i]; Symbol &sym = *file->symbols[i]; if (esym.is_undef_strong() && sym.file && sym.file->is_dso) { std::lock_guard lock(sym.mu); sym.file->is_alive = true; sym.is_weak = false; } } }); // DSOs referenced by live DSOs are also alive. std::vector *> live_dsos = ctx.dsos; erase(live_dsos, [](SharedFile *file) { return !file->is_alive; }); tbb::parallel_for_each(live_dsos, [&](SharedFile *file, tbb::feeder *> &feeder) { for (Symbol *sym : file->globals) if (sym->file && sym->file != file && sym->file->is_dso && !sym->file->is_alive.exchange(true)) feeder.add(file); }); // Remove symbols of unreferenced DSOs. tbb::parallel_for_each(ctx.dsos, [](SharedFile *file) { if (!file->is_alive) for (Symbol *sym : file->symbols) if (sym->file == file) new (sym) Symbol(sym->name()); }); // Remove unreferenced DSOs erase(ctx.dsos, [](InputFile *file) { return !file->is_alive; }); // Register common symbols tbb::parallel_for_each(ctx.objs, [&](ObjectFile *file) { file->resolve_common_symbols(ctx); }); if (Symbol *sym = intern(ctx, "__gnu_lto_slim"); sym->file) Fatal(ctx) << *sym->file << ": looks like this file contains a GCC " << "intermediate code, but mold does not support LTO"; } template void eliminate_comdats(Context &ctx) { Timer t(ctx, "eliminate_comdats"); tbb::parallel_for_each(ctx.objs, [](ObjectFile *file) { file->resolve_comdat_groups(); }); tbb::parallel_for_each(ctx.objs, [](ObjectFile *file) { file->eliminate_duplicate_comdat_groups(); }); } template void convert_common_symbols(Context &ctx) { Timer t(ctx, "convert_common_symbols"); tbb::parallel_for_each(ctx.objs, [&](ObjectFile *file) { file->convert_common_symbols(ctx); }); } template static std::string get_cmdline_args(Context &ctx) { std::stringstream ss; ss << ctx.cmdline_args[1]; for (i64 i = 2; i < ctx.cmdline_args.size(); i++) ss << " " << ctx.cmdline_args[i]; return ss.str(); } template void add_comment_string(Context &ctx, std::string str) { std::string_view buf = save_string(ctx, str); MergedSection *sec = MergedSection::get_instance(ctx, ".comment", SHT_PROGBITS, 0); std::string_view data(buf.data(), buf.size() + 1); SectionFragment *frag = sec->insert(data, hash_string(data), 1); frag->is_alive = true; } template void compute_merged_section_sizes(Context &ctx) { Timer t(ctx, "compute_merged_section_sizes"); // Mark section fragments referenced by live objects. if (!ctx.arg.gc_sections) { tbb::parallel_for_each(ctx.objs, [](ObjectFile *file) { for (SectionFragment *frag : file->fragments) frag->is_alive.store(true, std::memory_order_relaxed); }); } // Add an identification string to .comment. add_comment_string(ctx, mold_version); // Embed command line arguments for debugging. if (char *env = getenv("MOLD_DEBUG"); env && env[0]) add_comment_string(ctx, "mold command line: " + get_cmdline_args(ctx)); Timer t2(ctx, "MergedSection assign_offsets"); tbb::parallel_for_each(ctx.merged_sections, [&](std::unique_ptr> &sec) { sec->assign_offsets(ctx); }); } template static std::vector> split(std::vector &input, i64 unit) { assert(input.size() > 0); std::span span(input); std::vector> vec; while (span.size() >= unit) { vec.push_back(span.subspan(0, unit)); span = span.subspan(unit); } if (!span.empty()) vec.push_back(span); return vec; } // So far, each input section has a pointer to its corresponding // output section, but there's no reverse edge to get a list of // input sections from an output section. This function creates it. // // An output section may contain millions of input sections. // So, we append input sections to output sections in parallel. template void bin_sections(Context &ctx) { Timer t(ctx, "bin_sections"); static constexpr i64 num_shards = 128; i64 unit = (ctx.objs.size() + num_shards - 1) / num_shards; std::vector *>> slices = split(ctx.objs, unit); i64 num_osec = ctx.output_sections.size(); std::vector *>>> groups(slices.size()); for (i64 i = 0; i < groups.size(); i++) groups[i].resize(num_osec); tbb::parallel_for((i64)0, (i64)slices.size(), [&](i64 i) { for (ObjectFile *file : slices[i]) for (std::unique_ptr> &isec : file->sections) if (isec && isec->is_alive) groups[i][isec->output_section->idx].push_back(isec.get()); }); std::vector sizes(num_osec); for (std::span *>> group : groups) for (i64 i = 0; i < group.size(); i++) sizes[i] += group[i].size(); tbb::parallel_for((i64)0, num_osec, [&](i64 j) { ctx.output_sections[j]->members.reserve(sizes[j]); for (i64 i = 0; i < groups.size(); i++) append(ctx.output_sections[j]->members, groups[i][j]); }); } // Create a dummy object file containing linker-synthesized // symbols. template ObjectFile *create_internal_file(Context &ctx) { ObjectFile *obj = new ObjectFile; ctx.obj_pool.push_back(std::unique_ptr>(obj)); // Create linker-synthesized symbols. auto *esyms = new std::vector>(1); obj->symbols.push_back(new Symbol); obj->first_global = 1; obj->is_alive = true; obj->priority = 1; auto add = [&](std::string_view name) { ElfSym esym = {}; esym.st_type = STT_NOTYPE; esym.st_shndx = SHN_ABS; esym.st_bind = STB_GLOBAL; esym.st_visibility = STV_HIDDEN; esyms->push_back(esym); Symbol *sym = intern(ctx, name); obj->symbols.push_back(sym); return sym; }; ctx.__ehdr_start = add("__ehdr_start"); ctx.__init_array_start = add("__init_array_start"); ctx.__init_array_end = add("__init_array_end"); ctx.__fini_array_start = add("__fini_array_start"); ctx.__fini_array_end = add("__fini_array_end"); ctx.__preinit_array_start = add("__preinit_array_start"); ctx.__preinit_array_end = add("__preinit_array_end"); ctx._DYNAMIC = add("_DYNAMIC"); ctx._GLOBAL_OFFSET_TABLE_ = add("_GLOBAL_OFFSET_TABLE_"); ctx.__bss_start = add("__bss_start"); ctx._end = add("_end"); ctx._etext = add("_etext"); ctx._edata = add("_edata"); ctx.__executable_start = add("__executable_start"); ctx.__rel_iplt_start = add(E::is_rel ? "__rel_iplt_start" : "__rela_iplt_start"); ctx.__rel_iplt_end = add(E::is_rel ? "__rel_iplt_end" : "__rela_iplt_end"); if (ctx.arg.eh_frame_hdr) ctx.__GNU_EH_FRAME_HDR = add("__GNU_EH_FRAME_HDR"); if (!intern(ctx, "end")->file) ctx.end = add("end"); if (!intern(ctx, "etext")->file) ctx.etext = add("etext"); if (!intern(ctx, "edata")->file) ctx.edata = add("edata"); for (Chunk *chunk : ctx.chunks) { if (!is_c_identifier(chunk->name)) continue; add(save_string(ctx, "__start_" + std::string(chunk->name))); add(save_string(ctx, "__stop_" + std::string(chunk->name))); } obj->elf_syms = *esyms; obj->sym_fragments.resize(obj->elf_syms.size()); i64 num_globals = obj->elf_syms.size() - obj->first_global; obj->symvers.resize(num_globals); ctx.on_exit.push_back([=]() { delete esyms; delete obj->symbols[0]; }); return obj; } template void check_duplicate_symbols(Context &ctx) { Timer t(ctx, "check_dup_syms"); tbb::parallel_for_each(ctx.objs, [&](ObjectFile *file) { for (i64 i = file->first_global; i < file->elf_syms.size(); i++) { const ElfSym &esym = file->elf_syms[i]; Symbol &sym = *file->symbols[i]; if (sym.file == file || sym.file == ctx.internal_obj || esym.is_undef() || esym.is_common() || (esym.st_bind == STB_WEAK)) continue; if (!esym.is_abs() && !file->get_section(esym)->is_alive) continue; Error(ctx) << "duplicate symbol: " << *file << ": " << *sym.file << ": " << sym; } }); ctx.checkpoint(); } template void sort_init_fini(Context &ctx) { Timer t(ctx, "sort_init_fini"); auto get_priority = [](InputSection *isec) { static std::regex re(R"(_array\.(\d+)$)", std::regex_constants::optimize); std::string name = isec->name().begin(); std::smatch m; if (std::regex_search(name, m, re)) return std::stoi(m[1]); return 65536; }; for (std::unique_ptr> &osec : ctx.output_sections) { if (osec->name == ".init_array" || osec->name == ".fini_array") { sort(osec->members, [&](InputSection *a, InputSection *b) { return get_priority(a) < get_priority(b); }); } } } template std::vector *> collect_output_sections(Context &ctx) { std::vector *> vec; for (std::unique_ptr> &osec : ctx.output_sections) if (!osec->members.empty()) vec.push_back(osec.get()); for (std::unique_ptr> &osec : ctx.merged_sections) if (osec->shdr.sh_size) vec.push_back(osec.get()); // Sections are added to the section lists in an arbitrary order because // they are created in parallel. // Sort them to to make the output deterministic. sort(vec, [](Chunk *x, Chunk *y) { return std::tuple(x->name, x->shdr.sh_type, x->shdr.sh_flags) < std::tuple(y->name, y->shdr.sh_type, y->shdr.sh_flags); }); return vec; } template void compute_section_sizes(Context &ctx) { Timer t(ctx, "compute_section_sizes"); tbb::parallel_for_each(ctx.output_sections, [&](std::unique_ptr> &osec) { if (osec->members.empty()) return; struct T { i64 offset; i64 align; }; T sum = tbb::parallel_scan( tbb::blocked_range(0, osec->members.size(), 10000), T{0, 1}, [&](const tbb::blocked_range &r, T sum, bool is_final) { for (i64 i = r.begin(); i < r.end(); i++) { InputSection &isec = *osec->members[i]; sum.offset = align_to(sum.offset, isec.shdr.sh_addralign); if (is_final) isec.offset = sum.offset; sum.offset += isec.shdr.sh_size; sum.align = std::max(sum.align, isec.shdr.sh_addralign); } return sum; }, [](T lhs, T rhs) { i64 offset = align_to(lhs.offset, rhs.align) + rhs.offset; i64 align = std::max(lhs.align, rhs.align); return T{offset, align}; }, tbb::simple_partitioner()); osec->shdr.sh_size = sum.offset; osec->shdr.sh_addralign = sum.align; }); } template void claim_unresolved_symbols(Context &ctx) { Timer t(ctx, "claim_unresolved_symbols"); tbb::parallel_for_each(ctx.objs, [&](ObjectFile *file) { file->claim_unresolved_symbols(ctx); }); } template void scan_rels(Context &ctx) { Timer t(ctx, "scan_rels"); // Scan relocations to find dynamic symbols. tbb::parallel_for_each(ctx.objs, [&](ObjectFile *file) { file->scan_relocations(ctx); }); // Exit if there was a relocation that refers an undefined symbol. ctx.checkpoint(); // Add symbol aliases for COPYREL. tbb::parallel_for_each(ctx.dsos, [&](SharedFile *file) { for (Symbol *sym : file->symbols) if (sym->file == file && (sym->flags & NEEDS_COPYREL)) for (Symbol *alias : file->find_aliases(sym)) alias->flags |= NEEDS_DYNSYM; }); // Aggregate dynamic symbols to a single vector. std::vector *> files; append(files, ctx.objs); append(files, ctx.dsos); std::vector *>> vec(files.size()); tbb::parallel_for((i64)0, (i64)files.size(), [&](i64 i) { for (Symbol *sym : files[i]->symbols) { if (!files[i]->is_dso && (sym->is_imported || sym->is_exported)) sym->flags |= NEEDS_DYNSYM; if (sym->file == files[i] && sym->flags) vec[i].push_back(sym); } }); std::vector *> syms = flatten(vec); ctx.symbol_aux.resize(syms.size()); for (i64 i = 0; i < syms.size(); i++) syms[i]->aux_idx = i; // Assign offsets in additional tables for each dynamic symbol. for (Symbol *sym : syms) { if (sym->flags & NEEDS_DYNSYM) ctx.dynsym->add_symbol(ctx, sym); if (sym->flags & NEEDS_GOT) ctx.got->add_got_symbol(ctx, sym); if (sym->flags & NEEDS_PLT) { if (sym->flags & NEEDS_GOT) { // If we need to create a canonical PLT, we can't use .plt.got // because otherwise .plt.got and .got would refer each other, // resulting in an infinite loop at runtime. if (!ctx.arg.pic && sym->is_imported) ctx.plt->add_symbol(ctx, sym); else ctx.pltgot->add_symbol(ctx, sym); } else { ctx.plt->add_symbol(ctx, sym); } } if (sym->flags & NEEDS_GOTTP) ctx.got->add_gottp_symbol(ctx, sym); if (sym->flags & NEEDS_TLSGD) ctx.got->add_tlsgd_symbol(ctx, sym); if (sym->flags & NEEDS_TLSDESC) ctx.got->add_tlsdesc_symbol(ctx, sym); if (sym->flags & NEEDS_TLSLD) ctx.got->add_tlsld(ctx); if (sym->flags & NEEDS_COPYREL) { assert(sym->file->is_dso); SharedFile *file = (SharedFile *)sym->file; sym->copyrel_readonly = file->is_readonly(ctx, sym); if (sym->copyrel_readonly) ctx.dynbss_relro->add_symbol(ctx, sym); else ctx.dynbss->add_symbol(ctx, sym); for (Symbol *alias : file->find_aliases(sym)) { alias->has_copyrel = true; alias->value = sym->value; alias->copyrel_readonly = sym->copyrel_readonly; ctx.dynsym->add_symbol(ctx, alias); } } sym->flags = 0; } } template void apply_version_script(Context &ctx) { Timer t(ctx, "apply_version_script"); auto to_regex = [](std::span vec) -> std::string { switch (vec.size()) { case 0: return ""; case 1: return glob_to_regex(vec[0]); default: std::string re = glob_to_regex(vec[0]); for (std::string_view s : vec.subspan(1)) re += "|" + glob_to_regex(s); return re; } }; for (VersionPattern &elem : ctx.arg.version_patterns) { std::vector vec; for (std::string_view pat : elem.patterns) { if (pat.find('*') == pat.npos) { Symbol *sym = intern(ctx, pat); if (sym->file && !sym->file->is_dso) sym->ver_idx = elem.ver_idx; } else { vec.push_back(pat); } } if (vec.empty() && elem.cpp_patterns.empty()) continue; auto flags = std::regex_constants::optimize | std::regex_constants::nosubs; std::regex re(to_regex(vec), flags); std::regex cpp_re(to_regex(elem.cpp_patterns), flags); tbb::parallel_for_each(ctx.objs, [&](ObjectFile *file) { for (Symbol *sym : file->get_global_syms()) { if (sym->file != file) continue; std::string_view name = sym->name(); if (!vec.empty() && std::regex_match(name.begin(), name.end(), re)) { sym->ver_idx = elem.ver_idx; continue; } if (!elem.cpp_patterns.empty()) { std::string_view s = demangle(name); if (std::regex_match(s.begin(), s.end(), cpp_re)) sym->ver_idx = elem.ver_idx; } } }); } } template void parse_symbol_version(Context &ctx) { if (!ctx.arg.shared) return; Timer t(ctx, "parse_symbol_version"); std::unordered_map verdefs; for (i64 i = 0; i < ctx.arg.version_definitions.size(); i++) verdefs[ctx.arg.version_definitions[i]] = i + VER_NDX_LAST_RESERVED + 1; tbb::parallel_for_each(ctx.objs, [&](ObjectFile *file) { for (i64 i = 0; i < file->symbols.size() - file->first_global; i++) { if (!file->symvers[i]) continue; Symbol *sym = file->symbols[i + file->first_global]; if (sym->file != file) continue; std::string_view ver = file->symvers[i]; bool is_default = false; if (ver.starts_with('@')) { is_default = true; ver = ver.substr(1); } auto it = verdefs.find(ver); if (it == verdefs.end()) { Error(ctx) << *file << ": symbol " << *sym << " has undefined version " << ver; continue; } sym->ver_idx = it->second; if (!is_default) sym->ver_idx |= VERSYM_HIDDEN; } }); } template void compute_import_export(Context &ctx) { Timer t(ctx, "compute_import_export"); // Export symbols referenced by DSOs. if (!ctx.arg.shared) { tbb::parallel_for_each(ctx.dsos, [&](SharedFile *file) { for (Symbol *sym : file->globals) { if (sym->file && !sym->file->is_dso && sym->visibility != STV_HIDDEN) { std::lock_guard lock(sym->mu); sym->is_exported = true; } } }); } // Global symbols are exported from DSO by default. if (ctx.arg.shared || ctx.arg.export_dynamic) { tbb::parallel_for_each(ctx.objs, [&](ObjectFile *file) { for (Symbol *sym : file->get_global_syms()) { if (sym->file != file) continue; if (sym->visibility == STV_HIDDEN || sym->ver_idx == VER_NDX_LOCAL) continue; sym->is_exported = true; if (ctx.arg.shared && sym->visibility != STV_PROTECTED && !ctx.arg.Bsymbolic && !(ctx.arg.Bsymbolic_functions && sym->get_type() == STT_FUNC)) sym->is_imported = true; } }); } } template void clear_padding(Context &ctx) { Timer t(ctx, "clear_padding"); auto zero = [&](Chunk *chunk, i64 next_start) { i64 pos = chunk->shdr.sh_offset; if (chunk->shdr.sh_type != SHT_NOBITS) pos += chunk->shdr.sh_size; memset(ctx.buf + pos, 0, next_start - pos); }; for (i64 i = 1; i < ctx.chunks.size(); i++) zero(ctx.chunks[i - 1], ctx.chunks[i]->shdr.sh_offset); zero(ctx.chunks.back(), ctx.output_file->filesize); } // We want to sort output chunks in the following order. // // ELF header // program header // .interp // note // alloc readonly data // alloc readonly code // alloc writable tdata // alloc writable tbss // alloc writable RELRO data // alloc writable RELRO bss // alloc writable non-RELRO data // alloc writable non-RELRO bss // nonalloc // section header template i64 get_section_rank(Context &ctx, Chunk *chunk) { u64 type = chunk->shdr.sh_type; u64 flags = chunk->shdr.sh_flags; if (chunk == ctx.ehdr.get()) return -4; if (chunk == ctx.phdr.get()) return -3; if (chunk == ctx.interp.get()) return -2; if (type == SHT_NOTE && (flags & SHF_ALLOC)) return -1; if (chunk == ctx.shdr.get()) return 1 << 6; if (!(flags & SHF_ALLOC)) return 1 << 5; bool writable = (flags & SHF_WRITE); bool exec = (flags & SHF_EXECINSTR); bool tls = (flags & SHF_TLS); bool relro = is_relro(ctx, chunk); bool is_bss = (type == SHT_NOBITS); return (writable << 4) | (exec << 3) | (!tls << 2) | (!relro << 1) | is_bss; } // Returns the smallest number n such that // n >= val and n % align == skew. inline u64 align_with_skew(u64 val, u64 align, u64 skew) { return align_to(val + align - skew, align) - align + skew; } // Assign virtual addresses and file offsets to output sections. template i64 set_osec_offsets(Context &ctx) { Timer t(ctx, "osec_offset"); u64 fileoff = 0; u64 vaddr = ctx.arg.image_base; i64 i = 0; i64 end = 0; while (ctx.chunks[end]->shdr.sh_flags & SHF_ALLOC) end++; while (i < end) { fileoff = align_with_skew(fileoff, COMMON_PAGE_SIZE, vaddr % COMMON_PAGE_SIZE); // Each group consists of zero or more non-BSS sections followed // by zero or more BSS sections. Virtual addresses of non-BSS // sections need to be congruent to file offsets modulo the page size. // BSS sections don't increment file offsets. for (; i < end && ctx.chunks[i]->shdr.sh_type != SHT_NOBITS; i++) { Chunk &chunk = *ctx.chunks[i]; u64 prev_vaddr = vaddr; if (chunk.new_page) vaddr = align_to(vaddr, COMMON_PAGE_SIZE); vaddr = align_to(vaddr, chunk.shdr.sh_addralign); fileoff += vaddr - prev_vaddr; chunk.shdr.sh_addr = vaddr; vaddr += chunk.shdr.sh_size; chunk.shdr.sh_offset = fileoff; fileoff += chunk.shdr.sh_size; } for (; i < end && ctx.chunks[i]->shdr.sh_type == SHT_NOBITS; i++) { Chunk &chunk = *ctx.chunks[i]; if (chunk.new_page) vaddr = align_to(vaddr, COMMON_PAGE_SIZE); vaddr = align_to(vaddr, chunk.shdr.sh_addralign); fileoff = align_with_skew(fileoff, COMMON_PAGE_SIZE, vaddr % COMMON_PAGE_SIZE); chunk.shdr.sh_addr = vaddr; chunk.shdr.sh_offset = fileoff; if (!(chunk.shdr.sh_flags & SHF_TLS)) vaddr += chunk.shdr.sh_size; } } for (; i < ctx.chunks.size(); i++) { Chunk &chunk = *ctx.chunks[i]; assert(!(chunk.shdr.sh_flags & SHF_ALLOC)); fileoff = align_to(fileoff, chunk.shdr.sh_addralign); chunk.shdr.sh_offset = fileoff; fileoff += chunk.shdr.sh_size; } return fileoff; } template static i64 get_num_irelative_relocs(Context &ctx) { i64 n = 0; for (Symbol *sym : ctx.got->got_syms) if (sym->get_type() == STT_GNU_IFUNC) n++; return n; } template void fix_synthetic_symbols(Context &ctx) { auto start = [](Symbol *sym, auto &chunk) { if (sym && chunk) { sym->shndx = chunk->shndx; sym->value = chunk->shdr.sh_addr; } }; auto stop = [](Symbol *sym, auto &chunk) { if (sym && chunk) { sym->shndx = chunk->shndx; sym->value = chunk->shdr.sh_addr + chunk->shdr.sh_size; } }; // __bss_start for (Chunk *chunk : ctx.chunks) { if (chunk->kind == Chunk::REGULAR && chunk->name == ".bss") { start(ctx.__bss_start, chunk); break; } } // __ehdr_start and __executable_start for (Chunk *chunk : ctx.chunks) { if (chunk->shndx == 1) { ctx.__ehdr_start->shndx = 1; ctx.__ehdr_start->value = ctx.ehdr->shdr.sh_addr; ctx.__executable_start->shndx = 1; ctx.__executable_start->value = ctx.ehdr->shdr.sh_addr; break; } } // __rel_iplt_start start(ctx.__rel_iplt_start, ctx.reldyn); // __rel_iplt_end ctx.__rel_iplt_end->shndx = ctx.reldyn->shndx; ctx.__rel_iplt_end->value = ctx.reldyn->shdr.sh_addr + get_num_irelative_relocs(ctx) * sizeof(ElfRel); // __{init,fini}_array_{start,end} for (Chunk *chunk : ctx.chunks) { switch (chunk->shdr.sh_type) { case SHT_INIT_ARRAY: start(ctx.__init_array_start, chunk); stop(ctx.__init_array_end, chunk); break; case SHT_FINI_ARRAY: start(ctx.__fini_array_start, chunk); stop(ctx.__fini_array_end, chunk); break; } } // _end, _etext, _edata and the like for (Chunk *chunk : ctx.chunks) { if (chunk->kind == Chunk::HEADER) continue; if (chunk->shdr.sh_flags & SHF_ALLOC) { stop(ctx._end, chunk); stop(ctx.end, chunk); } if (chunk->shdr.sh_flags & SHF_EXECINSTR) { stop(ctx._etext, chunk); stop(ctx.etext, chunk); } if (chunk->shdr.sh_type != SHT_NOBITS && (chunk->shdr.sh_flags & SHF_ALLOC)) { stop(ctx._edata, chunk); stop(ctx.edata, chunk); } } // _DYNAMIC start(ctx._DYNAMIC, ctx.dynamic); // _GLOBAL_OFFSET_TABLE_ if (E::e_machine == EM_X86_64 || E::e_machine == EM_386) start(ctx._GLOBAL_OFFSET_TABLE_, ctx.gotplt); else if (E::e_machine == EM_AARCH64) start(ctx._GLOBAL_OFFSET_TABLE_, ctx.got); else unreachable(); // __GNU_EH_FRAME_HDR start(ctx.__GNU_EH_FRAME_HDR, ctx.eh_frame_hdr); // __start_ and __stop_ symbols for (Chunk *chunk : ctx.chunks) { if (is_c_identifier(chunk->name)) { std::string_view sym1 = save_string(ctx, "__start_" + std::string(chunk->name)); std::string_view sym2 = save_string(ctx, "__stop_" + std::string(chunk->name)); start(intern(ctx, sym1), chunk); stop(intern(ctx, sym2), chunk); } } } template void compress_debug_sections(Context &ctx) { Timer t(ctx, "compress_debug_sections"); tbb::parallel_for((i64)0, (i64)ctx.chunks.size(), [&](i64 i) { Chunk &chunk = *ctx.chunks[i]; if ((chunk.shdr.sh_flags & SHF_ALLOC) || chunk.shdr.sh_size == 0 || !chunk.name.starts_with(".debug")) return; Chunk *comp = nullptr; if (ctx.arg.compress_debug_sections == COMPRESS_GABI) comp = new GabiCompressedSection(ctx, chunk); else if (ctx.arg.compress_debug_sections == COMPRESS_GNU) comp = new GnuCompressedSection(ctx, chunk); assert(comp); ctx.output_chunks.push_back(std::unique_ptr>(comp)); ctx.chunks[i] = comp; }); ctx.shstrtab->update_shdr(ctx); ctx.ehdr->update_shdr(ctx); ctx.shdr->update_shdr(ctx); } #define INSTANTIATE(E) \ template void apply_exclude_libs(Context &ctx); \ template void create_synthetic_sections(Context &ctx); \ template void resolve_symbols(Context &ctx); \ template void eliminate_comdats(Context &ctx); \ template void convert_common_symbols(Context &ctx); \ template void compute_merged_section_sizes(Context &ctx); \ template void bin_sections(Context &ctx); \ template ObjectFile *create_internal_file(Context &ctx); \ template void check_duplicate_symbols(Context &ctx); \ template void sort_init_fini(Context &ctx); \ template std::vector *> collect_output_sections(Context &ctx); \ template void compute_section_sizes(Context &ctx); \ template void claim_unresolved_symbols(Context &ctx); \ template void scan_rels(Context &ctx); \ template void apply_version_script(Context &ctx); \ template void parse_symbol_version(Context &ctx); \ template void compute_import_export(Context &ctx); \ template void clear_padding(Context &ctx); \ template i64 get_section_rank(Context &ctx, Chunk *chunk); \ template i64 set_osec_offsets(Context &ctx); \ template void fix_synthetic_symbols(Context &ctx); \ template void compress_debug_sections(Context &ctx); INSTANTIATE(X86_64); INSTANTIATE(I386); INSTANTIATE(ARM64); } // namespace mold::elf