这里的数据格式和vardef不太一样,content是一个Verneed接着多个Vednaux构成。每个Verneed表示一个文件的开始。由于这里是针对dynsym处理,因此实际Vednaux的数量和dynsym的数量相同。在分配空间的时候注释也有写到allocate large enought buffer,避免了每个文件一个dynsym的极端场景。
// Create a list of versioned symbols and sort by file and version. std::vector<Symbol<E> *> syms(ctx.dynsym->symbols.begin() + 1, ctx.dynsym->symbols.end());
// 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. virtualvoidcompute_symtab_size(Context<E> &ctx){};
ifconstexpr(std::is_same_v<E, ARM32>) this->strtab_size = 9; // for "$t", "$a" and "$d" symbols
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : thunks) { // For ARM32, we emit additional symbol "$t", "$a" and "$d" for // each thunk to mark the beginning of ARM code. ifconstexpr(std::is_same_v<E, ARM32>) this->num_local_symtab += thunk->symbols.size() * 4; else this->num_local_symtab += thunk->symbols.size();
auto is_alive = [&](Symbol<E> &sym) -> bool { if (!ctx.arg.gc_sections) returntrue;
if (SectionFragment<E> *frag = sym.get_frag()) return frag->is_alive; if (InputSection<E> *isec = sym.get_input_section()) return isec->is_alive; returntrue; };
// Compute the size of local symbols if (!ctx.arg.discard_all && !ctx.arg.strip_all && !ctx.arg.retain_symbols_file) { for (i64 i = 1; i < this->first_global; i++) { Symbol<E> &sym = *this->symbols[i];
// Compute the size of global symbols. for (i64 i = this->first_global; i < this->elf_syms.size(); i++) { Symbol<E> &sym = *this->symbols[i];
if (sym.file == this && is_alive(sym) && (!ctx.arg.retain_symbols_file || sym.write_to_symtab)) { this->strtab_size += sym.name().size() + 1; // Global symbols can be demoted to local symbols based on visibility, // version scripts etc. if (sym.is_local(ctx)) this->output_sym_indices[i] = this->num_local_symtab++; else this->output_sym_indices[i] = this->num_global_symtab++; sym.write_to_symtab = true; } } }
// Local symbols are discarded if --discard-local is given or they // are in a mergeable section. I *believe* we exclude symbols in // mergeable sections because (1) there are too many and (2) they are // merged, so their origins shouldn't matter, but I don't really // know the rationale. Anyway, this is the behavior of the // traditional linkers. if (sym.name().starts_with(".L")) { if (ctx.arg.discard_locals) returnfalse;
if (InputSection<E> *isec = sym.get_input_section()) if (isec->shdr().sh_flags & SHF_MERGE) returnfalse; }
returntrue; }
-X, –discard-locals Discard temporary local symbols
// .eh_frame is a special section from the linker's point of view, // as its contents are parsed and reconstructed by the linker, // unlike other sections that are regarded as opaque bytes. // Here, we construct output .eh_frame contents. ctx.eh_frame->construct(ctx);
// If .eh_frame is missing in all input files, we don't want to // create an output .eh_frame section. if (std::all_of(ctx.objs.begin(), ctx.objs.end(), [](ObjectFile<E> *file) { return file->cies.empty(); })) { this->shdr.sh_size = 0; return; }
// Remove dead FDEs and assign them offsets within their corresponding // CIE group. tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) { std::erase_if(file->fdes, [](FdeRecord<E> &fde) { return !fde.is_alive; });
// This page explains the format of .gdb_index: // https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html template <typename E> void GdbIndexSection<E>::construct(Context<E> &ctx) { Timer t(ctx, "GdbIndexSection::construct");
std::atomic_bool has_debug_info = false;
// Read debug sections tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) { if (file->debug_info) { // Read compilation units from .debug_info. file->compunits = read_compunits(ctx, *file);
// Count the number of address areas contained in this file. file->num_areas = estimate_address_areas(ctx, *file); has_debug_info = true; } });
if (!has_debug_info) return;
// Initialize `area_offset` and `compunits_idx`. for (i64 i = 0; i < ctx.objs.size() - 1; i++) { ctx.objs[i + 1]->area_offset = ctx.objs[i]->area_offset + ctx.objs[i]->num_areas * 20; ctx.objs[i + 1]->compunits_idx = ctx.objs[i]->compunits_idx + ctx.objs[i]->compunits.size(); }
// Estimate the unique number of pubnames. HyperLogLog estimator; tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) { HyperLogLog e; for (GdbIndexName &name : file->gdb_names) e.insert(name.hash); estimator.merge(e); });
// Uniquify pubnames by inserting all name strings into a concurrent // hashmap. map.resize(estimator.get_cardinality() * 2); tbb::enumerable_thread_specific<i64> num_names;
ent->num_attrs++; name.entry_idx = ent - map.values; } });
// Assign offsets for names and attributes within each file. tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) { for (GdbIndexName &name : file->gdb_names) { MapEntry &ent = map.values[name.entry_idx]; if (ent.owner == file) { ent.attr_offset = file->attrs_size; file->attrs_size += (ent.num_attrs + 1) * 4; ent.name_offset = file->names_size; file->names_size += name.name.size() + 1; } } });
// Compute per-file name and attributes offsets. for (i64 i = 0; i < ctx.objs.size() - 1; i++) ctx.objs[i + 1]->attrs_offset = ctx.objs[i]->attrs_offset + ctx.objs[i]->attrs_size;
for (i64 i = 0; i < ctx.objs.size() - 1; i++) ctx.objs[i + 1]->names_offset = ctx.objs[i]->names_offset + ctx.objs[i]->names_size;
// .gdb_index contains an on-disk hash table for pubnames and // pubtypes. We aim 75% utilization. As per the format specification, // It must be a power of two. i64 num_symtab_entries = std::max<i64>(bit_ceil(num_names.combine(std::plus()) * 4 / 3), 16);
// Now that we can compute the size of this section. ObjectFile<E> &last = *ctx.objs.back(); i64 compunits_size = (last.compunits_idx + last.compunits.size()) * 16; i64 areas_size = last.area_offset + last.num_areas * 20; i64 offset = sizeof(header);