// crtbegin.o and crtend.o contain marker symbols such as // __CTOR_LIST__ or __DTOR_LIST__. So they have to be at the // beginning or end of the section. std::smatch m; if (std::regex_search(isec->file.filename, m, re1)) return-2; if (std::regex_search(isec->file.filename, m, re2)) return65536;
std::string name(isec->name()); if (std::regex_search(name, m, re3)) return std::stoi(m[1]); return-1; };
for (Chunk<E> *chunk : ctx.chunks) { if (OutputSection<E> *osec = chunk->to_osec()) { if (osec->name == ".ctors" || osec->name == ".dtors") { if (ctx.arg.shuffle_sections != SHUFFLE_SECTIONS_REVERSE) std::reverse(osec->members.begin(), osec->members.end());
// Copy string referred by .dynamic to .dynstr. for (SharedFile<E> *file : ctx.dsos) ctx.dynstr->add_string(file->soname); for (std::string_view str : ctx.arg.auxiliary) ctx.dynstr->add_string(str); for (std::string_view str : ctx.arg.filter) ctx.dynstr->add_string(str); if (!ctx.arg.rpaths.empty()) ctx.dynstr->add_string(ctx.arg.rpaths); if (!ctx.arg.soname.empty()) ctx.dynstr->add_string(ctx.arg.soname);
template <typename E> void ObjectFile<E>::scan_relocations(Context<E> &ctx) { // Scan relocations against seciton contents for (std::unique_ptr<InputSection<E>> &isec : sections) if (isec && isec->is_alive && (isec->shdr().sh_flags & SHF_ALLOC)) isec->scan_relocations(ctx);
// Scan relocations against exception frames for (CieRecord<E> &cie : cies) { for (ElfRel<E> &rel : cie.get_rels()) { Symbol<E> &sym = *this->symbols[rel.r_sym];
if (sym.is_imported) { if (sym.get_type() != STT_FUNC) Fatal(ctx) << *this << ": " << sym << ": .eh_frame CIE record with an external data reference" << " is not supported"; sym.flags |= NEEDS_PLT; } } } }
template <typename E> static Action get_pcrel_action(Context<E> &ctx, Symbol<E> &sym){ // This is for PC-relative relocations (e.g. R_X86_64_PC32). // We cannot promote them to dynamic relocations because the dynamic // linker generally does not support PC-relative relocations. constexprstatic Action table[3][4] = { // Absolute Local Imported data Imported code { ERROR, NONE, ERROR, PLT }, // Shared object { ERROR, NONE, COPYREL, PLT }, // Position-independent exec { NONE, NONE, COPYREL, CPLT }, // Position-dependent exec };
returnget_rel_action(ctx, sym, table); }
template <typename E> static Action get_absrel_action(Context<E> &ctx, Symbol<E> &sym){ // This is a decision table for absolute relocations that is smaller // than the word size (e.g. R_X86_64_32). Since the dynamic linker // generally does not support dynamic relocations smaller than the // word size, we need to report an error if a relocation cannot be // resolved at link-time. constexprstatic Action table[3][4] = { // Absolute Local Imported data Imported code { NONE, ERROR, ERROR, ERROR }, // Shared object { NONE, ERROR, ERROR, ERROR }, // Position-independent exec { NONE, NONE, COPYREL, CPLT }, // Position-dependent exec };
// This is a decision table for absolute relocations for the word // size data (e.g. R_X86_64_64). Unlike the absrel_table, we can emit // a dynamic relocation if we cannot resolve an address at link-time. constexprstatic Action table[3][4] = { // Absolute Local Imported data Imported code { NONE, BASEREL, DYNREL, DYNREL }, // Shared object { NONE, BASEREL, DYNREL, DYNREL }, // Position-independent exec { NONE, NONE, DYN_COPYREL, DYN_CPLT }, // Position-dependent exec };
// As a special case, we do not create copy relocations nor canonical // PLTs for .toc sections. PPC64's .toc is a compiler-generated // GOT-like section, and no user-generated code directly uses values // in it. constexprstatic Action table[3][4] = { // Absolute Local Imported data Imported code { NONE, BASEREL, DYNREL, DYNREL }, // Shared object { NONE, BASEREL, DYNREL, DYNREL }, // Position-independent exec { NONE, NONE, DYNREL, DYNREL }, // Position-dependent exec };
auto error = [&] { std::string msg = sym.is_absolute() ? "-fno-PIC" : "-fPIC"; Error(ctx) << isec << ": " << rel << " relocation at offset 0x" << std::hex << rel.r_offset << " against symbol `" << sym << "' can not be used; recompile with " << msg; };
auto check_textrel = [&] { if (!writable) { if (ctx.arg.z_text) { error(); } elseif (ctx.arg.warn_textrel) { Warn(ctx) << isec << ": relocation against symbol `" << sym << "' in read-only section"; } ctx.has_textrel = true; } };
auto copyrel = [&] { assert(sym.is_imported); if (sym.esym().st_visibility == STV_PROTECTED) { Error(ctx) << isec << ": cannot make copy relocation for protected symbol '" << sym << "', defined in " << *sym.file << "; recompile with -fPIC"; } sym.flags |= NEEDS_COPYREL; };
auto dynrel = [&] { check_textrel(); isec.file.num_dynrel++; };
switch (action) { case NONE: break; case ERROR: error(); break; case COPYREL: if (!ctx.arg.z_copyreloc) error(); copyrel(); break; case DYN_COPYREL: if (writable || !ctx.arg.z_copyreloc) dynrel(); else copyrel(); break; case PLT: sym.flags |= NEEDS_PLT; break; case CPLT: sym.flags |= NEEDS_CPLT; break; case DYN_CPLT: if (writable) dynrel(); else sym.flags |= NEEDS_CPLT; break; case DYNREL: case IFUNC: dynrel(); break; case BASEREL: check_textrel(); if (!isec.is_relr_reloc(ctx, rel)) isec.file.num_dynrel++; break; default: unreachable(); } }
// Aggregate dynamic symbols to a single vector. std::vector<InputFile<E> *> files; append(files, ctx.objs); append(files, ctx.dsos); std::vector<std::vector<Symbol<E> *>> vec(files.size()); tbb::parallel_for((i64)0, (i64)files.size(), [&](i64 i) { for (Symbol<E> *sym : files[i]->symbols) if (sym->file == files[i]) if (sym->flags || sym->is_imported || sym->is_exported) vec[i].push_back(sym); }); std::vector<Symbol<E> *> syms = flatten(vec); ctx.symbol_aux.reserve(syms.size());
... }
这里将所有的文件中所有符合条件的symbol收集起来,其中flag则是在前面的阶段进行标记的
关于这里的symbol_aux
1 2
// Symbol auxiliary data std::vector<SymbolAux> symbol_aux;
SymbolAux
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 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. structSymbolAux { 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 opd_idx = -1; i32 dynsym_idx = -1; u32 djb_hash = 0; };
template <typename E> voidscan_relocations(Context<E> &ctx){ ... // Assign offsets in additional tables for each dynamic symbol. for (Symbol<E> *sym : syms) { add_aux(sym);
if (sym->is_imported || sym->is_exported) ctx.dynsym->add_symbol(ctx, sym);
if (sym->flags & NEEDS_GOT) ctx.got->add_got_symbol(ctx, sym);
if (sym->flags & NEEDS_CPLT) { sym->is_canonical = true;
// A canonical PLT needs to be visible from DSOs. sym->is_exported = true;
// We can't use .plt.got for a canonical PLT because otherwise // .plt.got and .got would refer each other, resulting in an // infinite loop at runtime. ctx.plt->add_symbol(ctx, sym); } elseif (sym->flags & NEEDS_PLT) { if (sym->flags & NEEDS_GOT) 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->copyrel_readonly) ctx.copyrel_relro->add_symbol(ctx, sym); else ctx.copyrel->add_symbol(ctx, sym);
// If a symbol needs copyrel, it is considered both imported // and exported. assert(sym->is_imported); sym->is_exported = true;
// Aliases of this symbol are also copied so that they will be // resolved to the same address at runtime. for (Symbol<E> *alias : file->find_aliases(sym)) { add_aux(alias); alias->is_imported = true; alias->is_exported = true; alias->has_copyrel = true; alias->value = sym->value; alias->copyrel_readonly = sym->copyrel_readonly; ctx.dynsym->add_symbol(ctx, alias); } }
// This pattern will be processed in the next loop. ifconstexpr (needs_thunk<E>) if ((osec->shdr.sh_flags & SHF_EXECINSTR) && !ctx.arg.relocatable) return;
// Since one output section may contain millions of input sections, // we first split input sections into groups and assign offsets to // groups. std::vector<Group> groups; constexpr i64 group_size = 10000;
for (std::span<InputSection<E> *> span : split(osec->members, group_size)) groups.push_back(Group{.members = span});
// On ARM32 or ARM64, we may need to create so-called "range extension // thunks" to extend branch instructions reach, as they can jump only // to ±16 MiB or ±128 MiB, respecitvely. // // In the following loop, We compute the sizes of sections while // inserting thunks. This pass cannot be parallelized. That is, // create_range_extension_thunks is parallelized internally, but the // function itself is not thread-safe. ifconstexpr(needs_thunk<E>){ for (Chunk<E> *chunk : ctx.chunks) { OutputSection<E> *osec = chunk->to_osec(); if (osec && (osec->shdr.sh_flags & SHF_EXECINSTR) && !ctx.arg.relocatable) { create_range_extension_thunks(ctx, *osec);
auto get_rank2 = [&](Chunk<E> *chunk) -> i64 { if (chunk->shdr.sh_type == SHT_NOTE) return -chunk->shdr.sh_addralign;
if (chunk == ctx.relro_padding) return INT_MAX; if (chunk->name == ".toc") return2; if (chunk == ctx.got) return1; return0; };
tls: This section holds Thread-Local Storage, meaning that each separate execution flow has its own distinct instance of this data.
order
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Sort sections according to a --section-order argument. template <typename E> voidsort_output_sections_by_order(Context<E> &ctx){ ... // It is an error if a section order cannot be determined by a given // section order list. for (Chunk<E> *chunk : ctx.chunks) chunk->sect_order = get_rank(chunk); // Sort output sections by --section-order sort(ctx.chunks, [&](Chunk<E> *a, Chunk<E> *b) { return a->sect_order < b->sect_order; }); }