mirror of
https://github.com/rui314/mold.git
synced 2024-09-19 17:07:29 +03:00
201 lines
6.0 KiB
C++
201 lines
6.0 KiB
C++
// This file implements -r or --relocatable. That option forces the linker
|
|
// to combine input object files into another single large object file.
|
|
// Since the behavior of the linker when the option is given is quite
|
|
// different from that of the normal execution mode, we separate code for
|
|
// the feature into this separate file.
|
|
//
|
|
// The --relocatable option isn't used very often. After all, if you want
|
|
// to combine object files into a single file, you could use `ar`.
|
|
// However, some programs use it in a creative manner which is hard to be
|
|
// substituted with static archives, so we need to support this option in
|
|
// the same way as GNU ld does. A notable example is GHC (Glasgow Haskell
|
|
// Compiler). GHC has its own dynamic linker which can load a .o file (as
|
|
// opposed to a .so) into memory. GHC's module is not a shared object file
|
|
// but a combined object file.
|
|
//
|
|
// There are many different ways to combine object files into a single file.
|
|
// The simplest approach would be to just copy all sections from input files
|
|
// to an output file as-is with a few exceptions for singleton sections such
|
|
// as the symbol table or the string table. That works, but that's not
|
|
// compatible with GNU ld.
|
|
//
|
|
// To be compatible with GNU ld, we need to do the followings:
|
|
//
|
|
// - Regular sections containing opaque data (e.g. ".text" or ".data")
|
|
// are just copied as-is. Two sections with the same name are merged.
|
|
//
|
|
// - .symtab, .strtab and .shstrtab are merged.
|
|
//
|
|
// - COMDAT groups are uniquified.
|
|
//
|
|
// - Relocations are copied, but we need to fix symbol indices.
|
|
|
|
#include "mold.h"
|
|
|
|
#include <tbb/parallel_for.h>
|
|
#include <tbb/parallel_for_each.h>
|
|
|
|
namespace mold::elf {
|
|
|
|
// Create linker-synthesized sections
|
|
template <typename E>
|
|
static void r_create_synthetic_sections(Context<E> &ctx) {
|
|
auto push = [&](auto *x) {
|
|
ctx.chunks.push_back(x);
|
|
ctx.chunk_pool.emplace_back(x);
|
|
return x;
|
|
};
|
|
|
|
ctx.ehdr = push(new OutputEhdr<E>(0));
|
|
ctx.shdr = push(new OutputShdr<E>);
|
|
ctx.eh_frame = push(new EhFrameSection<E>);
|
|
ctx.eh_frame_reloc = push(new EhFrameRelocSection<E>);
|
|
ctx.strtab = push(new StrtabSection<E>);
|
|
ctx.symtab = push(new SymtabSection<E>);
|
|
ctx.shstrtab = push(new ShstrtabSection<E>);
|
|
ctx.note_property = push(new NotePropertySection<E>);
|
|
|
|
if constexpr (is_riscv<E>)
|
|
ctx.extra.riscv_attributes = push(new RiscvAttributesSection<E>);
|
|
}
|
|
|
|
// Create SHT_GROUP (i.e. comdat group) sections. We uniquify comdat
|
|
// sections by signature. We want to propagate input comdat groups as
|
|
// output comdat groups if they are still alive after uniquification.
|
|
template <typename E>
|
|
static void create_comdat_group_sections(Context<E> &ctx) {
|
|
Timer t(ctx, "create_comdat_group_sections");
|
|
|
|
std::vector<std::vector<Chunk<E> *>> vec{ctx.objs.size()};
|
|
|
|
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
|
|
ObjectFile<E> &file = *ctx.objs[i];
|
|
|
|
for (ComdatGroupRef<E> &ref : file.comdat_groups) {
|
|
if (ref.group->owner != file.priority)
|
|
continue;
|
|
|
|
Symbol<E> *sym = file.symbols[file.elf_sections[ref.sect_idx].sh_info];
|
|
assert(sym);
|
|
|
|
std::vector<Chunk<E> *> members;
|
|
for (u32 j : ref.members) {
|
|
const ElfShdr<E> &shdr = file.elf_sections[j];
|
|
if (shdr.sh_type == (E::is_rela ? SHT_RELA : SHT_REL)) {
|
|
InputSection<E> &isec = *file.sections[shdr.sh_info];
|
|
members.push_back(isec.output_section->reloc_sec.get());
|
|
} else {
|
|
InputSection<E> &isec = *file.sections[j];
|
|
members.push_back(isec.output_section);
|
|
}
|
|
}
|
|
|
|
vec[i].push_back(new ComdatGroupSection<E>(*sym, std::move(members)));
|
|
}
|
|
});
|
|
|
|
for (std::vector<Chunk<E> *> &vec2 : vec) {
|
|
for (Chunk<E> *chunk : vec2) {
|
|
ctx.chunks.push_back(chunk);
|
|
ctx.chunk_pool.emplace_back(chunk);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Unresolved undefined symbols in the -r mode are simply propagated to an
|
|
// output file as undefined symbols. This function guarantees that
|
|
// unresolved undefined symbols belongs to some input file.
|
|
template <typename E>
|
|
static void r_claim_unresolved_symbols(Context<E> &ctx) {
|
|
Timer t(ctx, "r_claim_unresolved_symbols");
|
|
|
|
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
|
if (!file->is_alive)
|
|
return;
|
|
|
|
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
|
|
const ElfSym<E> &esym = file->elf_syms[i];
|
|
Symbol<E> &sym = *file->symbols[i];
|
|
if (!esym.is_undef())
|
|
continue;
|
|
|
|
std::scoped_lock lock(sym.mu);
|
|
|
|
if (sym.file &&
|
|
(!sym.esym().is_undef() || sym.file->priority <= file->priority))
|
|
continue;
|
|
|
|
sym.file = file;
|
|
sym.origin = 0;
|
|
sym.value = 0;
|
|
sym.sym_idx = i;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Set output section in-file offsets. Output section memory addresses
|
|
// are left as zero.
|
|
template <typename E>
|
|
static u64 r_set_osec_offsets(Context<E> &ctx) {
|
|
u64 offset = 0;
|
|
for (Chunk<E> *chunk : ctx.chunks) {
|
|
offset = align_to(offset, chunk->shdr.sh_addralign);
|
|
chunk->shdr.sh_offset = offset;
|
|
offset += chunk->shdr.sh_size;
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
template <typename E>
|
|
void combine_objects(Context<E> &ctx) {
|
|
compute_merged_section_sizes(ctx);
|
|
|
|
create_output_sections(ctx);
|
|
|
|
r_create_synthetic_sections(ctx);
|
|
|
|
r_claim_unresolved_symbols(ctx);
|
|
|
|
compute_section_sizes(ctx);
|
|
|
|
sort_output_sections(ctx);
|
|
|
|
create_output_symtab(ctx);
|
|
|
|
ctx.eh_frame->construct(ctx);
|
|
|
|
create_reloc_sections(ctx);
|
|
|
|
create_comdat_group_sections(ctx);
|
|
|
|
compute_section_headers(ctx);
|
|
|
|
i64 filesize = r_set_osec_offsets(ctx);
|
|
ctx.output_file =
|
|
OutputFile<Context<E>>::open(ctx, ctx.arg.output, filesize, 0666);
|
|
ctx.buf = ctx.output_file->buf;
|
|
|
|
copy_chunks(ctx);
|
|
clear_padding(ctx);
|
|
ctx.output_file->close(ctx);
|
|
ctx.checkpoint();
|
|
|
|
if (ctx.arg.print_map)
|
|
print_map(ctx);
|
|
|
|
if (ctx.arg.stats)
|
|
show_stats(ctx);
|
|
|
|
if (ctx.arg.perf)
|
|
print_timer_records(ctx.timer_records);
|
|
|
|
if (ctx.arg.quick_exit)
|
|
_exit(0);
|
|
}
|
|
|
|
using E = MOLD_TARGET;
|
|
|
|
template void combine_objects(Context<E> &);
|
|
|
|
} // namespace mold::elf
|