[PATCH] ld: ensure build-id is placed near ELF headers
Andrew Burgess
aburgess@redhat.com
Fri Sep 20 13:20:38 GMT 2024
"H.J. Lu" <hjl.tools@gmail.com> writes:
> On Fri, Sep 20, 2024 at 4:54 AM Sam James <sam@gentoo.org> wrote:
>>
>> Nick Clifton <nickc@redhat.com> writes:
>>
>> > Hi Andrew,
>> >
>> > [Sorry for being so late in responding to your email]
>> >
>> >> When an executable is built with the --rosegment option GDB is no
>> >> longer able to find the build-id of the executable from a core file.
>> >
>> > For problems like this it really helps if you create a bug report
>> > using the Sourceware bugzilla system. This allows us to track the
>> > problem and any discussion surrounding it. It also means that we
>> > can refer to the bug ID in comments in the code.
>> >
>> > In order to save you time however, I have gone ahead and created
>> > a bug report for you:
>> >
>> > https://sourceware.org/bugzilla/show_bug.cgi?id=32100
>> >
>> >> This patch aims to fix this by placing the build-id first. The
>> >> build-id will then be included within the same LOAD-able segment as
>> >> the executable content, just as the ELF headers are. With this patch
>> >> in place GDB is once again able to find the build-id from a core
>> >> file.
>> >
>> > Unfortunately that patch breaks the intention of the --rosegment
>> > option by restoring two loadable, read-only segments to the executable.
>> >
>> > Here is an example:
>> >
>> > $ cat hello.c
>> >
>> > extern int printf (const char *, ...);
>> > int i = 42;
>> > const int * j = & i;
>> > int main (void) { return printf ("hello world %d\n", * j); }
>> >
>> > $ gcc -fPIC -Wl,-z,noseparate-code hello.c
>> > $ readelf -lW a.out | grep LOAD
>> >
>> > LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x0006d4 0x0006d4 R E 0x1000
>> > LOAD 0x000df8 0x0000000000401df8 0x0000000000401df8 0x000228 0x000230 RW 0x1000
>> >
>> > $ gcc -fPIX -Wl,-z,separate-code hello.c
>> > $ readelf -lW a.out | grep LOAD
>> >
>> > LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x000510 0x000510 R 0x1000
>> > LOAD 0x001000 0x0000000000401000 0x0000000000401000 0x000155 0x000155 R E 0x1000
>> > LOAD 0x002000 0x0000000000402000 0x0000000000402000 0x0000dc 0x0000dc R 0x1000
>> > LOAD 0x002df8 0x0000000000403df8 0x0000000000403df8 0x000228 0x000230 RW 0x1000
>> >
>> > $ gcc -fPIX -Wl,-z,separate-code -Wl,--rosegment hello.c
>> > $ readelf -lW a.out | grep LOAD
>> >
>> > LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x00115d 0x00115d R E 0x1000
>> > LOAD 0x002000 0x0000000000402000 0x0000000000402000 0x0002d4 0x0002d4 R 0x1000
>> > LOAD 0x002df8 0x0000000000403df8 0x0000000000403df8 0x000220 0x000228 RW 0x1000
>> >
>> > $ gcc -fPIX -Wl,-z,separate-code -Wl,--rosegment hello.c -fuse-ld=patched-linker
>> > $ readelf -lW a.out | grep LOAD
>> >
>> > LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x00039c 0x00039c R 0x1000
>> > LOAD 0x001000 0x0000000000401000 0x0000000000401000 0x00015d 0x00015d R E 0x1000
>> > LOAD 0x002000 0x0000000000402000 0x0000000000402000 0x00024c 0x00024c R 0x1000
>> > LOAD 0x002df8 0x0000000000403df8 0x0000000000403df8 0x000220 0x000228 RW 0x1000
>> >
>> > (I invented the "-fuse-ld=patched-linker" option to keep the presentation simple. In
>> > reality I used a full path to a linker built with your proposed patch applied).
>> >
>> > The section to segment mapping is changed by using --rosegment, but with your patch
>> > applied we still get an early read-only segment containing the note sections.
>> >
>> > I think that what is needed is a patch to move all of the read-only segments to
>> > before the read-execute segment. But this weill need testing.
>>
>> I'm reading over the rest of the thread, so apologies if this got
>> addressed later, but did we add a testcase like this?
>
> Hi Andrew,
>
> Why does GDB need the build-id to be placed close to the ELF header?
This is specifically to help with core-file debugging.
In the Linux kernel commit:
commit 82df39738ba9e02c057fa99b7461a56117d36119
Date: Tue Oct 16 23:27:02 2007 -0700
Add MMF_DUMP_ELF_HEADERS
When writing out a core-file the kernel will always include in the core
file the first page of any file backed mapping that starts at offset
zero within a file. The idea is that this mapping will (likely) include
the section headers and (hopefully) the build-id.
When loading the core-file GDB then uses the
elf_backend_core_find_build_id elf backend hook to try and find the
build-id within each file backed mapping that is included in the core
file.
What this means of course, is that GDB can magically "know" the build-id
for both the main executable, and every shared library that was mapped
into the inferior given just the core file.
This means that GDB can help the user understand if say the executable
or libraries have changed since the core file was created.
Or GDB can even go and fetch the missing files (possibly) using things
like debuginfod.
> Can't GDB read the program header and search for the build-id in PT_NOTE
> segment?
That's exactly what we do. In a follow up you ask about
elfobj_grok_gnu_build_id, and that is exactly what ends up being
called.
On the GDB side we start in linux-tdep.c with this call into bfd:
if (sec->flags & SEC_LOAD
&& (get_elf_backend_data (cbfd)->elf_backend_core_find_build_id
(cbfd, (bfd_vma) sec->filepos)))
vma_map[sec->vma] = cbfd->build_id;
This lands us (eventually) into elfcore.h in the function:
NAME(_bfd_elf, core_find_build_id)
Which ends up calling:
elf_read_notes (abfd, offset + i_phdr->p_offset,
i_phdr->p_filesz, i_phdr->p_align);
to read the NOTES, which will, eventually, call
elfobj_grok_gnu_build_id, at least, that's my understanding.
> If GDB needs help, we can add a PT_BUILD_ID segment.
I don't understand exactly what this would mean in relation to the
above, so I'm unclear if this would help or not.
I hope this all makes things a little clearer. Let me know if you still
have questions.
Thanks,
Andrew
More information about the Binutils
mailing list