Re: [PATCH] libbpf: Fix uprobe offset calculation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Mar 13, 2025 at 7:30 AM Jiri Olsa <olsajiri@xxxxxxxxx> wrote:
>
> On Thu, Mar 13, 2025 at 12:23:10PM +0800, Hengqi Chen wrote:
>
> SNIP
>
> > > > > >   tools/lib/bpf/elf.c | 32 ++++++++++++++++++++++++--------
> > > > > >   1 file changed, 24 insertions(+), 8 deletions(-)
> > > > > >
> > > > > > diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c
> > > > > > index 823f83ad819c..9b561c8d1eec 100644
> > > > > > --- a/tools/lib/bpf/elf.c
> > > > > > +++ b/tools/lib/bpf/elf.c
> > > > > > @@ -260,13 +260,29 @@ static bool symbol_match(struct elf_sym_iter *iter, int sh_type, struct elf_sym
> > > > > >    * for shared libs) into file offset, which is what kernel is expecting
> > > > > >    * for uprobe/uretprobe attachment.
> > > > > >    * See Documentation/trace/uprobetracer.rst for more details. This is done
> > > > > > - * by looking up symbol's containing section's header and using iter's virtual
> > > > > > - * address (sh_addr) and corresponding file offset (sh_offset) to transform
> > > > > > + * by looking up symbol's containing program header and using its virtual
> > > > > > + * address (p_vaddr) and corresponding file offset (p_offset) to transform
> > > > > >    * sym.st_value (virtual address) into desired final file offset.
> > > > > >    */
> > > > > > -static unsigned long elf_sym_offset(struct elf_sym *sym)
> > > > > > +static unsigned long elf_sym_offset(Elf *elf, struct elf_sym *sym)
> > > > > >   {
> > > > > > -     return sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset;
> > > > > > +     size_t nhdrs, i;
> > > > > > +     GElf_Phdr phdr;
> > > > > > +
> > > > > > +     if (elf_getphdrnum(elf, &nhdrs))
> > > > > > +             return -1;
> > > > > > +
> > > > > > +     for (i = 0; i < nhdrs; i++) {
> > > > > > +             if (!gelf_getphdr(elf, (int)i, &phdr))
> > > > > > +                     continue;
> > > > > > +             if (phdr.p_type != PT_LOAD || !(phdr.p_flags & PF_X))
> > > > > > +                     continue;
> > > > > > +             if (sym->sym.st_value >= phdr.p_vaddr &&
> > > > > > +                 sym->sym.st_value < (phdr.p_vaddr + phdr.p_memsz))
> > > > > > +                     return sym->sym.st_value - phdr.p_vaddr + phdr.p_offset;
> > >
> > > Hengqi,
> > >
> > > Can you please provide an example where existing code doesn't work? I think that
> > >
> > > sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset
> > >
> > > and
> > >
> > > sym->sym.st_value - phdr.p_vaddr + phdr.p_offset
> > >
> > >
> > > Should result in the same, and we don't need to search for a matching
> > > segment if we have an ELF symbol and its section. But maybe I'm
> > > mistaken, so can you please elaborate a bit?
> >
> > The binary ([0]) provided in the issue shows some counterexamples.
> > I could't find an authoritative documentation describing this though.
> > A modified version ([1]) of this patch could pass the CI now.
>
> yes, I tried that binary and it gives me different offsets
>
> IIUC the symbol seems to be from .eh_frame_hdr (odd?) while the new logic
> base it on offset of .text section.. I'm still not following that binary
> layout completely.. will try to check on that more later today
>

Yep, something is off with the way that this ELF file is linked.
Symbols information is just wrong.

In sections he have:

  [Nr] Name              Type            Address          Off    Size
 ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000
000000 00      0   0  0
  [ 1] .gnu.version      VERSYM          0000000000299b38 299b38
03774a 02   A  5   0  2
  [ 2] .gnu.version_r    VERNEED         00000000002d1284 2d1284
000340 00   A  6  12  4
  [ 3] .gnu.hash         GNU_HASH        00000000002d15c8 2d15c8
0c9538 00   A  5   0  8
  [ 4] .dynamic          DYNAMIC         000000000b29fb10 b29db10
000380 10  WA  6   0  8
  [ 5] .dynsym           DYNSYM          00000000d7b03070 b2b4070
299778 18   A  6   1  8
  [ 6] .dynstr           STRTAB          000000000039ab00 39ab00
17c8e7e 00   A  0   0  1
  [ 7] .rela.dyn         RELA            0000000001b63980 1b63980
513630 18   A  5   0  8
  [ 8] .rela.plt         RELA            0000000002076fb0 2076fb0
000288 18  AI  5  26  8
  [ 9] .rodata           PROGBITS        0000000002078000 2078000
a30306 00 AMS  0   0 4096
  [10] .gcc_except_table PROGBITS        0000000002aa8308 2aa8308
3cd368 00   A  0   0  4
  [11] protodesc_cold    PROGBITS        0000000002e75670 2e75670
007400 00   A  0   0 16
  [12] .stapsdt.base     PROGBITS        0000000002e7ca70 2e7ca70
000001 00   A  0   0  1
  [13] .eh_frame_hdr     PROGBITS        0000000002e7ca74 2e7ca74
0f35bc 00   A  0   0  4
  [14] .eh_frame         PROGBITS        0000000002f70030 2f70030
66f6a4 00   A  0   0  8
  [15] .text             PROGBITS        00000000035e0700 35df700
7a9de55 00  AX  0   0 64
  [16] .init             PROGBITS        000000000b07e558 b07d558
00001b 00  AX  0   0  4
  [17] .fini             PROGBITS        000000000b07e574 b07d574
00000d 00  AX  0   0  4
  ...


Symbol itself says:

Symbol table '.dynsym' contains 113573 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
   ...
109776: 00000000081658d0  4132 FUNC    GLOBAL DEFAULT   13
_ZN7cluster15topics_frontend13create_topicsE17fragmented_vectorINS_37custom_assignable_topic_configurationELm18446744073709551615EENSt3__16chrono10time_pointIN7seastar12lowres_clockENS5_8durationIxNS4_5ratioILl1ELl1000000000EEEEEEE
   ...

Note how the symbol points to section #13, which is .eh_frame_hdr. But
value (virtual address) 00000000081658d0 actually belongs to section
#15 (.text):


[15] .text             PROGBITS        00000000035e0700 35df700
7a9de55 00  AX  0   0 64

So symbol information has section index off by 2. And this seems to be
the case for lots of symbols, they all point to section #13.

Libbpf's logic is correct, as long as symbol information is correct.
If that index was 15, we'd calculate that 0x1000 additional offset.

So let's figure out why ELF is invalid instead of trying to "fix"
libbpf's logic, which is correct and much faster than linearly
searching through segments. Could it be that that binary was modified
post final linking to add custom sections before .text (like that
protodesc_cold)?


> jirka
>
> >
> >   [0]: https://github.com/libbpf/libbpf-rs/issues/1110#issuecomment-2699221802
> >   [1]: https://github.com/kernel-patches/bpf/pull/8647
> >
> > >
> > > > > > +     }
> > > > > > +
> > > > > > +     return -1;
> > > > > >   }
> > > > > >
> > > > > >   /* Find offset of function name in the provided ELF object. "binary_path" is
> > > > > > @@ -329,7 +345,7 @@ long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
> > > > > >
> > > > > >                       if (ret > 0) {
> > > > > >                               /* handle multiple matches */
> > > > > > -                             if (elf_sym_offset(sym) == ret) {
> > > > > > +                             if (elf_sym_offset(elf, sym) == ret) {
> > > > > >                                       /* same offset, no problem */
> > > > > >                                       continue;
> > > > > >                               } else if (last_bind != STB_WEAK && cur_bind != STB_WEAK) {
> > > > >
> > > > > [...]
> > > > >
> >





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux