1
1
mirror of https://github.com/rui314/mold.git synced 2024-09-19 17:07:29 +03:00
mold/elf/relocatable.cc
2023-08-15 15:08:34 +09:00

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