Skip to content

Commit

Permalink
[ELF] Support --gdb-index
Browse files Browse the repository at this point in the history
This is a tough one because .gdb_index, .debug_gnu_pubnames,
.debug_gnu_pubtypes and DWARF are underdocumented, and DWARF is
complicated even if you have a right documentation. But, I believe I
managed to create a correct .gdb_index section.

Just like ld.lld, mold's --gdb-index needs all input object files to
have been compiled with -ggnu-pubnames. We read symbol names and type
names from the sections generated by -ggnu-pubnames.

Unlike ld.gold and ld.lld, we do not use an external library to read
DWARF debug info records.

As always, this feature is implemented with speed in mind. For Clang
15 which is built with -ggnu-pubnames, mold takes ~150 ms to create a
~300 MiB .gdb_index section on a simulated 16-core machine.

Fixes #396
  • Loading branch information
rui314 committed Apr 13, 2022
1 parent c4af138 commit a7475dd
Show file tree
Hide file tree
Showing 9 changed files with 709 additions and 3 deletions.
7 changes: 5 additions & 2 deletions elf/cmdline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ static const char helpmsg[] = R"(
--no-fork
--gc-sections Remove unreferenced sections
--no-gc-sections
--gdb-index Ignored
--gdb-index Create .gdb_index for faster gdb startup
--hash-style [sysv,gnu,both]
Set hash style
--icf=[all,none] Fold identical code
Expand Down Expand Up @@ -626,6 +626,10 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.relax = true;
} else if (read_flag(args, "no-relax")) {
ctx.arg.relax = false;
} else if (read_flag(args, "gdb-index")) {
ctx.arg.gdb_index = true;
} else if (read_flag(args, "no-gdb-index")) {
ctx.arg.gdb_index = false;
} else if (read_flag(args, "r") || read_flag(args, "relocatable")) {
ctx.arg.relocatable = true;
} else if (read_flag(args, "perf")) {
Expand Down Expand Up @@ -950,7 +954,6 @@ void parse_nonpositional_args(Context<E> &ctx,
} else if (read_flag(args, "O2")) {
} else if (read_flag(args, "verbose")) {
} else if (read_flag(args, "color-diagnostics")) {
} else if (read_flag(args, "gdb-index")) {
} else if (read_flag(args, "eh-frame-hdr")) {
} else if (read_flag(args, "start-group")) {
} else if (read_flag(args, "end-group")) {
Expand Down
32 changes: 32 additions & 0 deletions elf/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,38 @@ static constexpr u32 DW_EH_PE_datarel = 0x30;
static constexpr u32 DW_EH_PE_funcrel = 0x40;
static constexpr u32 DW_EH_PE_aligned = 0x50;

static constexpr u32 DW_AT_low_pc = 0x11;
static constexpr u32 DW_AT_high_pc = 0x12;
static constexpr u32 DW_AT_producer = 0x25;
static constexpr u32 DW_AT_ranges = 0x55;

static constexpr u32 DW_TAG_compile_unit = 0x11;

static constexpr u32 DW_FORM_addr = 0x01;
static constexpr u32 DW_FORM_block2 = 0x03;
static constexpr u32 DW_FORM_block4 = 0x04;
static constexpr u32 DW_FORM_data2 = 0x05;
static constexpr u32 DW_FORM_data4 = 0x06;
static constexpr u32 DW_FORM_data8 = 0x07;
static constexpr u32 DW_FORM_string = 0x08;
static constexpr u32 DW_FORM_block = 0x09;
static constexpr u32 DW_FORM_block1 = 0x0a;
static constexpr u32 DW_FORM_data1 = 0x0b;
static constexpr u32 DW_FORM_flag = 0x0c;
static constexpr u32 DW_FORM_sdata = 0x0d;
static constexpr u32 DW_FORM_strp = 0x0e;
static constexpr u32 DW_FORM_udata = 0x0f;
static constexpr u32 DW_FORM_ref_addr = 0x10;
static constexpr u32 DW_FORM_ref1 = 0x11;
static constexpr u32 DW_FORM_ref2 = 0x12;
static constexpr u32 DW_FORM_ref4 = 0x13;
static constexpr u32 DW_FORM_ref8 = 0x14;
static constexpr u32 DW_FORM_ref_udata = 0x15;
static constexpr u32 DW_FORM_indirect = 0x16;
static constexpr u32 DW_FORM_sec_offset = 0x17;
static constexpr u32 DW_FORM_exprloc = 0x18;
static constexpr u32 DW_FORM_flag_present = 0x19;

struct Elf64Sym {
bool is_defined() const { return !is_undef(); }
bool is_undef() const { return st_shndx == SHN_UNDEF; }
Expand Down
25 changes: 25 additions & 0 deletions elf/input-files.cc
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,31 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {

this->sections[i] = std::make_unique<InputSection<E>>(ctx, *this, name, i);

// Save debug sections for --gdb-index.
if (ctx.arg.gdb_index) {
InputSection<E> *isec = this->sections[i].get();

if (name == ".debug_info")
debug_info = isec;
if (name == ".debug_abbrev")
debug_abbrev = isec;
if (name == ".debug_ranges")
debug_ranges = isec;

// If --gdb-index is given, contents of .debug_gnu_pubnames and
// .debug_gnu_pubtypes are copied to .gdb_index, so keeping them
// in an output file is just a waste of space.
if (name == ".debug_gnu_pubnames") {
debug_pubnames = isec;
isec->is_alive = false;
}

if (name == ".debug_gnu_pubtypes") {
debug_pubtypes = isec;
isec->is_alive = false;
}
}

static Counter counter("regular_sections");
counter++;
break;
Expand Down
10 changes: 10 additions & 0 deletions elf/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,10 @@ static int elf_main(int argc, char **argv) {
// Here, we construct output .eh_frame contents.
ctx.eh_frame->construct(ctx);

// Handle --gdb-index.
if (ctx.arg.gdb_index)
ctx.gdb_index->construct(ctx);

// If --emit-relocs is given, we'll copy relocation sections from input
// files to an output file.
if (ctx.arg.emit_relocs)
Expand Down Expand Up @@ -690,6 +694,12 @@ static int elf_main(int argc, char **argv) {
if constexpr (std::is_same_v<E, ARM32>)
sort_arm_exidx(ctx);

// Some part of .gdb_index couldn't be computed until other debug
// sections are complete. We have complete debug sections now, so
// write the rest of .gdb_index.
if (ctx.gdb_index)
ctx.gdb_index->write_address_areas(ctx);

// Dynamic linker works better with sorted .rela.dyn section,
// so we sort them.
ctx.reldyn->sort(ctx);
Expand Down
75 changes: 75 additions & 0 deletions elf/mold.h
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,67 @@ class NotePropertySection : public Chunk<E> {
u32 features = 0;
};

struct GdbIndexName {
std::string_view name;
u32 hash = 0;
u32 offset = 0;
u32 attr = 0;
u32 entry_idx = 0;
};

template <typename E>
class GdbIndexSection : public Chunk<E> {
public:
GdbIndexSection() {
this->name = ".gdb_index";
this->shdr.sh_type = SHT_PROGBITS;
}

void construct(Context<E> &ctx);
void copy_buf(Context<E> &ctx) override;
void write_address_areas(Context<E> &ctx);

private:
struct SectionHeader {
u32 version = 7;
u32 cu_list_offset = 0;
u32 cu_types_offset = 0;
u32 areas_offset = 0;
u32 symtab_offset = 0;
u32 const_pool_offset = 0;
};

struct MapEntry {
MapEntry(u32 hash) : hash(hash) {}

MapEntry(const MapEntry &other) {
hash = other.hash;
name_offset = other.name_offset;
attr_offset = other.attr_offset;
num_attrs = other.num_attrs.load();
}

u32 hash = 0;
u32 name_offset = 0;
u32 attr_offset = 0;
std::atomic_uint32_t num_attrs = 0;
};

SectionHeader header;
i64 num_symtab_entries = 0;
i64 attrs_size = 0;

std::vector<std::string_view>
read_compunits(Context<E> &ctx, ObjectFile<E> &file);

std::vector<GdbIndexName> read_pubnames(Context<E> &ctx, ObjectFile<E> &file);

std::vector<u64> read_address_areas(Context<E> &ctx, ObjectFile<E> &file,
i64 offset);

ConcurrentMap<MapEntry> map;
};

template <typename E>
class GabiCompressedSection : public Chunk<E> {
public:
Expand Down Expand Up @@ -1059,6 +1120,18 @@ class ObjectFile : public InputFile<E> {
std::vector<std::vector<ElfRel<E>>> sorted_rels;
std::vector<std::vector<Symbol<E> *>> sorted_symbols;

// For .gdb_index
InputSection<E> *debug_info = nullptr;
InputSection<E> *debug_abbrev = nullptr;
InputSection<E> *debug_ranges = nullptr;
InputSection<E> *debug_pubnames = nullptr;
InputSection<E> *debug_pubtypes = nullptr;
std::vector<std::string_view> compunits;
i64 num_areas = 0;
i64 area_offset = 0;
i64 compunits_idx = 0;
std::vector<GdbIndexName> pubnames;

private:
ObjectFile(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name, bool is_in_lib);
Expand Down Expand Up @@ -1485,6 +1558,7 @@ struct Context {
bool fatal_warnings = false;
bool fork = true;
bool gc_sections = false;
bool gdb_index = false;
bool hash_style_gnu = false;
bool hash_style_sysv = true;
bool icf = false;
Expand Down Expand Up @@ -1655,6 +1729,7 @@ struct Context {
std::unique_ptr<VerdefSection<E>> verdef;
std::unique_ptr<BuildIdSection<E>> buildid;
std::unique_ptr<NotePropertySection<E>> note_property;
std::unique_ptr<GdbIndexSection<E>> gdb_index;
std::unique_ptr<ThumbToArmSection> thumb_to_arm;
std::unique_ptr<TlsTrampolineSection> tls_trampoline;

Expand Down

0 comments on commit a7475dd

Please sign in to comment.