Bug 31447

Summary: Provide a public debug section relocation function
Product: elfutils Reporter: Mark Wielaard <mark>
Component: libdwAssignee: Di Chen <dichen>
Status: NEW ---    
Severity: normal CC: amerey, dichen, elfutils-devel, tuliom
Priority: P2    
Version: unspecified   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:

Description Mark Wielaard 2024-03-03 12:19:09 UTC
When opening an ET_REL file with dwarf_begin no relocations are resolved and no error checking is done if that is necessary. Causing some functions to produce errors or wrong results (because an index is wrongly assumed to be zero).

When opening such a file through libdwfl we do resolve all cross-debug-section relocations using __libdwfl_relocate.

It would be good to make this a public function. Maybe through libdwelf dwelf_relocation_debug_sections (Elf *elf). So users can make sure ET_REL files can be handled by libdw.

And maybe add a check/flag in dwarf_begin so an error can be produced if a relocation value is needed for a correct result.
Comment 1 Mark Wielaard 2024-03-03 12:35:52 UTC
This is the current "workaround" I use when working with individual ET_REL DWARF files (also works for any other ELF type):

 static char *debuginfo_path = NULL;
 static const Dwfl_Callbacks dwfl_callbacks =
    {
      .find_elf = dwfl_build_id_find_elf,
      .find_debuginfo = dwfl_standard_find_debuginfo,
      .section_address = dwfl_offline_section_address,
      .debuginfo_path = &debuginfo_path,
    };

 int main (int argc, char **argv)
 {
    Dwfl *dwfl = dwfl_begin (&dwfl_callbacks);
    Dwfl_Module *module = dwfl_report_elf (dwfl, argv[1], argv[1], -1, 0, false);
    if (module == NULL)
      printf ("%s\n", dwfl_errmsg (-1));
    dwfl_report_end (dwfl, NULL, NULL);

    Dwarf_Addr bias;
    Dwarf *dwarf = dwfl_module_getdwarf (module, &bias);
    if (dwarf == NULL)
      printf ("%s\n", dwfl_errmsg (-1));

    /* ... do something with dwarf ... */

    dwfl_end (dwfl); // Also cleans up dwarf.
 }

But this is obviously a little cumbersome if not really using any other libdwfl features.
Comment 2 Di Chen 2024-05-12 07:29:36 UTC
Hey @mjw

```
$ eu-readelf -a qq.o | grep Type
  Type: REL (Relocatable file)

$ eu-readelf -S qq.o | grep REL
[ 2] .rela.text           RELA         0000000000000000 000004b8 00000048 24 I     19   1  8
[ 6] .rela.debug_info     RELA         0000000000000000 00000500 000000f0 24 I     19   5  8
[ 9] .rela.debug_aranges  RELA         0000000000000000 000005f0 00000030 24 I     19   8  8
[11] .rela.debug_line     RELA         0000000000000000 00000620 00000060 24 I     19  10  8
[18] .rela.eh_frame       RELA         0000000000000000 00000680 00000030 24 I     19  17  8

```


When applying relocation on a relocatable file (the above qq.o) with the new function
dwelf_relocation_debug_sections (Elf *elf), it only needs to apply relocation on debug sections (which are 
[ 6] .rela.debug_info, 
[ 9] .rela.debug_aranges,
[11] .rela.debug_line
), am I right?
Comment 3 Mark Wielaard 2024-05-12 09:06:12 UTC
Yes, just process the .rela.debug_* sections.
Note that you don't need to depend on the exact name.
You can follow the section header sh_info field, which points to the section (number) to which the relocations apply.
If that section starts with .debug you can apply the relocations.
Or better use ebl_debugscn_p, which checks against the list in libebl/eblopenbackend.c (default_debugscn_p).

See also in strip.c, where it says:

          /* Make sure that this relocation section points to a
             section to relocate with contents, that isn't
             allocated and that is a debug section.  */

Or libdwfl/relocate.c (relocate_section). Which contains similar logic.
Comment 4 Di Chen 2024-08-02 11:26:07 UTC
@mjw

I have function dwelf_relocation_debug_sections() added in libdwelf.a.

```
$ pwd
/home/dichen/elfutils
$ eu-readelf -s libdwelf/libdwelf.a | grep dwelf_relocation_debug_sections
libdwelf/libdwelf.a(dwelf_relocation_debug_sections.o):
    1: 0000000000000000      0 FILE    LOCAL  DEFAULT      ABS dwelf_relocation_debug_sections.c
  290: 000000000000077c   3783 FUNC    GLOBAL DEFAULT      126 dwelf_relocation_debug_sections

```

And I can call the new function in a test program:

```
#include <fcntl.h>
#include <gelf.h>
#include <libelf.h>
#include <stdio.h>
#include <unistd.h>

#include "libdwelf.h"


int main(int argc, char **argv) {
  const char *fname = argv[1];

  elf_version(EV_CURRENT);
  int fd = open(argv[1], O_RDWR);

  Elf *elf;
  elf = elf_begin(fd, ELF_C_RDWR, NULL);

  dwelf_relocation_debug_sections(elf, fname);
  elf_update(elf, ELF_C_WRITE);

  return 0;
}

// $ gcc reloc.c -l elf -l dwelf -l dw -l ebl -l ebl_backends -l cpu -L ../libcpu -L ../libebl -L ../backends -L ../libdwelf -I ../libdwelf -I ../libdw -g -o reloc

```

Then, I am thinking of making strip.c use this dwelf_relocation_debug_sections() so that I can reduce some code redundancy.

```
diff --git a/src/Makefile.am b/src/Makefile.am
index 1d592d4d..746c8817 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -43,6 +43,8 @@ if BUILD_STATIC
 libasm = ../libasm/libasm.a
 libdw = ../libdw/libdw.a -lz $(zip_LIBS) $(libelf) -ldl -lpthread
 libelf = ../libelf/libelf.a -lz $(zstd_LIBS)
+libebl = ../libebl/libebl.a ../backends/libebl_backends.a ../libcpu/libcpu.a
+libdwelf = ../libdwelf/libdwelf.a
 if LIBDEBUGINFOD
 libdebuginfod = ../debuginfod/libdebuginfod.a -lpthread $(libcurl_LIBS)
 else
@@ -60,6 +62,7 @@ endif
 endif
 libebl = ../libebl/libebl.a ../backends/libebl_backends.a ../libcpu/libcpu.a
 libeu = ../lib/libeu.a
+libdwelf = ../libdwelf/libdwelf.a
 
 if DEMANGLE
 demanglelib = -lstdc++
@@ -81,7 +84,7 @@ readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(argp_LDADD)
 nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(argp_LDADD) $(obstack_LIBS) \
           $(demanglelib)
 size_LDADD = $(libelf) $(libeu) $(argp_LDADD)
-strip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD)
+strip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(libdwelf) $(argp_LDADD)
 elflint_LDADD  = $(libebl) $(libdw) $(libelf) $(libeu) $(argp_LDADD)
 findtextrel_LDADD = $(libdw) $(libelf) $(libeu) $(argp_LDADD)
 addr2line_LDADD = $(libdw) $(libelf) $(libeu) $(argp_LDADD) $(demanglelib)
```

After the above Makefile.am change, still, elfutils building kept failing due to some
 undefined reference error.

```
/usr/bin/ld: strip.o: in function `handle_debug_relocs':
/home/dichen/elfutils/src/strip.c:576: undefined reference to `dwelf_relocation_debug_sections'
/usr/bin/ld: strip.o: in function `handle_elf':
/home/dichen/elfutils/src/strip.c:2123: undefined reference to `dwelf_relocation_debug_sections'
collect2: error: ld returned 1 exit status
```

I believe I am missing something in the building process(Makefile.am, Makefile.in, etc). But I don't have many clue now, could you please give a look?

CC: @amerey
Comment 5 Aaron Merey 2024-08-02 16:20:22 UTC
(In reply to Di Chen from comment #4)
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 1d592d4d..746c8817 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -43,6 +43,8 @@ if BUILD_STATIC
>  libasm = ../libasm/libasm.a
>  libdw = ../libdw/libdw.a -lz $(zip_LIBS) $(libelf) -ldl -lpthread
>  libelf = ../libelf/libelf.a -lz $(zstd_LIBS)
> +libebl = ../libebl/libebl.a ../backends/libebl_backends.a ../libcpu/libcpu.a
> +libdwelf = ../libdwelf/libdwelf.a
>  if LIBDEBUGINFOD
>  libdebuginfod = ../debuginfod/libdebuginfod.a -lpthread $(libcurl_LIBS)
>  else
> @@ -60,6 +62,7 @@ endif
>  endif
>  libebl = ../libebl/libebl.a ../backends/libebl_backends.a ../libcpu/libcpu.a
>  libeu = ../lib/libeu.a
> +libdwelf = ../libdwelf/libdwelf.a
>  
>  if DEMANGLE
>  demanglelib = -lstdc++
> @@ -81,7 +84,7 @@ readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu)
> $(argp_LDADD)
>  nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(argp_LDADD)
> $(obstack_LIBS) \
>            $(demanglelib)
>  size_LDADD = $(libelf) $(libeu) $(argp_LDADD)
> -strip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD)
> +strip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(libdwelf)
> $(argp_LDADD)
>  elflint_LDADD  = $(libebl) $(libdw) $(libelf) $(libeu) $(argp_LDADD)
>  findtextrel_LDADD = $(libdw) $(libelf) $(libeu) $(argp_LDADD)
>  addr2line_LDADD = $(libdw) $(libelf) $(libeu) $(argp_LDADD) $(demanglelib)

These Makefile changes aren't needed since strip is already built with libdwelf and using libdwelf functions. Reverting these Makefile changes should fix the linker error.
Comment 6 Di Chen 2024-08-04 09:01:14 UTC
@amerey

I believe your elfutils was lacking the dwelf_relocation_debug_sections replacement in strip.c while compiling.

Could you give a double check for whether the dwelf_relocation_debug_sections is introduced in strip binary?

$ eu-readelf -s {strip} | grep dwelf_relocation
Comment 7 Di Chen 2024-09-03 12:40:20 UTC
This issue is in code review.

https://sourceware.org/pipermail/elfutils-devel/2024q3/007388.html