Skip to content

feat: Support GDB index#1605

Closed
levietduc0712 wants to merge 16 commits intowild-linker:mainfrom
levietduc0712:test_gdb_index
Closed

feat: Support GDB index#1605
levietduc0712 wants to merge 16 commits intowild-linker:mainfrom
levietduc0712:test_gdb_index

Conversation

@levietduc0712
Copy link
Copy Markdown
Contributor

@levietduc0712 levietduc0712 commented Feb 27, 2026

This pull request adds support for generating a .gdb_index section in ELF binaries, which is used to speed up debugging with GDB. The implementation introduces a new command-line flag (--gdb-index and --no-gdb-index), updates the layout and writer logic to allocate and fill in the section, and integrates the section into the ELF output as an optional part. It also updates relevant tests and internal data structures to accommodate the new section.

Resolve #811

@levietduc0712 levietduc0712 marked this pull request as draft February 27, 2026 05:07
@levietduc0712 levietduc0712 marked this pull request as ready for review February 27, 2026 05:48
@lapla-cogito lapla-cogito self-requested a review February 27, 2026 06:31
Copy link
Copy Markdown
Member

@davidlattimore davidlattimore left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Have you tried loading a binary with the gdb index into gdb to confirm that it's happy with it?

Any ideas about any other testing - automated or otherwise we could do? Checking that the section exists is a start, but it leaves a lot of scope for things being not quite right.

Comment thread libwild/src/elf_writer.rs Outdated
self.rela_dyn_relative.len() as u64 * elf::RELA_ENTRY_SIZE,
*mem_sizes.get(part_id::RELA_DYN_RELATIVE),
));
let remaining = self.rela_dyn_relative.len() as u64 * elf::RELA_ENTRY_SIZE;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this related to this change? I'm also curious as to the motivation for this bit of the change. It looks like you've inlined the function excessive_allocation into several of its usage sites.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s not required for the .gdb_index work it was just a small refactor to make the error paths explicit, which I forgot to remove before committing. I’ve restored the original code.

Comment thread libwild/src/layout.rs Outdated
tracing::debug!(loaded_debug_section = %self.object.section_display_name(section_index),);
common.section_loaded(part_id, header, section);

if self
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently we read the section names only during section resolution. Doing an extra read of the section name can cause a noticeable slowdown. So I think we likely need to find a different way to do this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread libwild/src/elf_writer.rs Outdated

for (i, &(cu_offset, cu_length)) in cu_entries.iter().enumerate() {
let off = cu_list_offset + i * CU_ENTRY_SIZE;
buf[off..off + 8].copy_from_slice(&cu_offset.to_le_bytes());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it work to simplify some of this code by using zerocopy?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done also

@levietduc0712
Copy link
Copy Markdown
Contributor Author

Nice! Have you tried loading a binary with the gdb index into gdb to confirm that it's happy with it?

Yes. I tested that explicitly.

I built a small test binary with -Wl,--gdb-index and then loaded it into GDB in batch mode to check for any structural issues. GDB accepted the binary without reporting any corruption, errors, or warnings.

In addition, I compared the generated .gdb_index section against an LLD-linked reference build. The section exists, the size matches, and the hex dump is identical.

So from GDB’s perspective, it appears to be fully valid and functional.

@levietduc0712
Copy link
Copy Markdown
Contributor Author

Any ideas about any other testing - automated or otherwise we could do? Checking that the section exists is a start, but it leaves a lot of scope for things being not quite right.

I’ve added a ValidateGdbIndex directive that parses the raw .gdb_index and verifies: version = 7, header offsets are ordered and in-bounds, CU entries are non-overlapping and fully cover .debug_info, and address area entries fall within .text. I also added ExpectGdbIndexCuCount: 3 to assert the exact CU count. Additionally, I loaded the binary in GDB and confirmed info sources lists files from all three input objects. Would this satisfy your request?

Copy link
Copy Markdown
Member

@davidlattimore davidlattimore left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extra testing is great. Thanks!

Comment thread libwild/src/layout.rs Outdated
Comment thread libwild/src/elf_writer.rs Outdated
{
cu_entries.push(elf::GdbIndexCuEntry {
cu_offset: address - debug_info_start,
cu_length: section.capacity(),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to include alignment padding, making it inappropriate as a proper CU length.

Comment thread libwild/src/elf_writer.rs Outdated
Comment thread libwild/src/output_section_id.rs Outdated
Comment thread libwild/src/elf_writer.rs
@lapla-cogito lapla-cogito changed the title feat: Add support for --gdb-index option and enhance .gdb_index generation feat: Support GDB index Feb 28, 2026
Copy link
Copy Markdown
Member

@lapla-cogito lapla-cogito left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I haven't thoroughly reviewed the updated patch yet (today is a bit busy, so it may take some time), I'll point out one observation immediately: Wild runs mold's test suite in CI. However, some tests fail under Wild's current implementation, so they are excluded from the skip list and ignored during runs. These skipped tests are subsequently verified to ensure they still fail in CI (though false positives occasionally occur, which is why the workflow is configured with continue-on-error: true).

Through 2a20ff7 in this PR, all tests in the GDB index category appear to pass (see this run), but the latest commit seems to cause many of these tests to fail again.

@levietduc0712
Copy link
Copy Markdown
Contributor Author

Through 2a20ff7 in this PR, all tests in the GDB index category appear to pass (see this run), but the latest commit seems to cause many of these tests to fail again.

I fixed it. Check this one

@davidlattimore
Copy link
Copy Markdown
Member

Can you delete the lines from mold_skip_tests.toml for the tests that now pass?

Also, I was thinking that it might be good to enable TestUpdateInPlace for your new test, just to make sure that all the bytes of the new section are getting written.

@levietduc0712 levietduc0712 marked this pull request as draft February 28, 2026 12:17
@levietduc0712 levietduc0712 marked this pull request as ready for review February 28, 2026 13:13
@levietduc0712
Copy link
Copy Markdown
Contributor Author

This feature appears to be more complex than I initially estimated. I need to add gdb_index.rs file to implements hashing, hash-table sizing, parsing of .debug_gnu_pubnames/.debug_gnu_pubtypes, and writing of the symbol table + constant-pool required by the GDB index format see this one.

Copy link
Copy Markdown
Collaborator

@marxin marxin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really cool PR, good work. Am curious if we want to support the latest version9 (could be a subsequent PR)? Thank you.

Comment thread libwild/src/gdb_index.rs
for &c in name {
r = r
.wrapping_mul(67)
.wrapping_add(u32::from(c))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the formula: The formula is r = r * 67 + tolower (c) - 113.
Don't we miss the tolower?

Comment thread libwild/src/gdb_index.rs
return 0;
}
let min_slots = num_symbols * 4 / 3 + 1;
let mut slots = 4usize;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about slot = (num_symbols * 4 / 3 + 1).next_multiple_of(4)?

Comment thread libwild/src/layout.rs
.map(|obj| {
let pubnames = obj
.object
.section_by_name(".debug_gnu_pubnames")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread libwild/src/layout.rs

if resources
.output_sections
.custom_name_to_id(SectionName(b".debug_info"))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here.

);
}

let version = u32::from_le_bytes(data[0..4].try_into().unwrap());
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we directly read the GdbIndexHeader here (using the zerocopy library)?

let constant_pool_offset = u32::from_le_bytes(data[20..24].try_into().unwrap()) as usize;

// Check version.
if version != 7 {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should declare a constant somwhere for the currently supported version.

.collect();

let addr_entry_count = address_area_size / 20;
for i in 0..addr_entry_count {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use chunks_exact?

@levietduc0712
Copy link
Copy Markdown
Contributor Author

Really cool PR, good work. Am curious if we want to support the latest version9 (could be a subsequent PR)? Thank you.

Thanks! I’ll look into supporting the latest version (9) as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for --gdb-index

4 participants