[PATCH] ld: ensure build-id is placed near ELF headers
Fangrui Song
i@maskray.me
Wed Aug 21 20:45:31 GMT 2024
On Wed, Aug 21, 2024 at 3:57 AM Andrew Burgess <aburgess@redhat.com> 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.
>
> Thanks for taking the time to test this patch. Sorry that this failed.
> I had thought I'd seen different results, but retesting I can reproduce
> what you see, so clearly I was doing something wrong before[1].
>
> I do have one question which you might be able to enlighten me on
> though: the initial loadable segment contains both the code, but also
> the ELF headers, right? Which are clearly not executable content.
>
> So it would seem that it is OK to have a limited set of data mapped at
> the start of the code segment. Clearly we don't want _all_ the data
> placed there as that would make the separate segments pointless. But
> something that is not usually read by the program itself (e.g. ELF
> headers, or the build-id) seemed OK.
>
> My next question then would be, I guess the linker is creating a new
> segment because the permissions of the build-id section doesn't match
> that of the code section. Instead of moving all the sections, can the
> linker be told/convinced to merge the build-id into the start of the
> code?
>
> I'm just trying to understand more about how this all works.
>
> Thanks,
> Andrew
>
>
> [1] I'm pretty sure I figured out what I was doing wrong. But it was a
> stupid error my side.
>
> >
> > Cheers
> > Nick
Thanks for the .note.build-id use case (kernel core dumper). I think
--rosegment needs to place read-only sections entirely before .text
https://sourceware.org/pipermail/binutils/2024-June/134541.html
> **Proposed Changes for GNU ld:**
>
> * Add an option to place read-only sections entirely before .text, eliminating the R segment after the RX segment.
More information about the Binutils
mailing list