Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf symbols: Allow for static executables with .plt

A statically linked executable can have a .plt due to IFUNCs, in which
case .symtab is used not .dynsym. Check the section header link to see
if that is the case, and then use symtab instead.

Example:

Before:

$ cat tstifunc.c
#include <stdio.h>

void thing1(void)
{
printf("thing1\n");
}

void thing2(void)
{
printf("thing2\n");
}

typedef void (*thing_fn_t)(void);

thing_fn_t thing_ifunc(void)
{
int x;

if (x & 1)
return thing2;
return thing1;
}

void thing(void) __attribute__ ((ifunc ("thing_ifunc")));

int main()
{
thing();
return 0;
}
$ gcc --version
gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gcc -static -Wall -Wextra -Wno-uninitialized -o tstifuncstatic tstifunc.c
$ readelf -SW tstifuncstatic | grep 'Name\|plt\|dyn'
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 4] .rela.plt RELA 00000000004002e8 0002e8 000258 18 AI 29 20 8
[ 6] .plt PROGBITS 0000000000401020 001020 000190 00 AX 0 0 16
[20] .got.plt PROGBITS 00000000004c5000 0c4000 0000e0 08 WA 0 0 8
$ perf record -e intel_pt//u --filter 'filter main @ ./tstifuncstatic' ./tstifuncstatic
thing1
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.008 MB perf.data ]
$ perf script --itrace=be --ns -F+flags,-event,+addr,-period,-comm,-tid,-cpu,-dso
15786.690189535: tr strt 0 [unknown] => 4017cd main+0x0
15786.690189535: tr end call 4017d5 main+0x8 => 401170 [unknown]
15786.690197660: tr strt 0 [unknown] => 4017da main+0xd
15786.690197660: tr end return 4017e0 main+0x13 => 401c1a __libc_start_call_main+0x6a

After:

$ perf script --itrace=be --ns -F+flags,-event,+addr,-period,-comm,-tid,-cpu,-dso
15786.690189535: tr strt 0 [unknown] => 4017cd main+0x0
15786.690189535: tr end call 4017d5 main+0x8 => 401170 thing_ifunc@plt+0x0
15786.690197660: tr strt 0 [unknown] => 4017da main+0xd
15786.690197660: tr end return 4017e0 main+0x13 => 401c1a __libc_start_call_main+0x6a

Reviewed-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Link: https://lore.kernel.org/r/20230131131625.6964-8-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Adrian Hunter and committed by
Arnaldo Carvalho de Melo
a1ab1285 60fbb3e4

+21 -10
+20 -10
tools/perf/util/symbol-elf.c
··· 483 483 GElf_Shdr shdr_rel_plt, shdr_dynsym; 484 484 Elf_Data *syms, *symstrs; 485 485 Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; 486 - size_t dynsym_idx; 487 486 GElf_Ehdr ehdr; 488 487 char sympltname[1024]; 489 488 Elf *elf; ··· 529 530 lazy_plt = true; 530 531 } 531 532 532 - scn_dynsym = ss->dynsym; 533 - shdr_dynsym = ss->dynshdr; 534 - dynsym_idx = ss->dynsym_idx; 535 - 536 - if (scn_dynsym == NULL) 537 - return 0; 538 - 539 533 scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, 540 534 ".rela.plt", NULL); 541 535 if (scn_plt_rel == NULL) { ··· 542 550 shdr_rel_plt.sh_type != SHT_REL) 543 551 return 0; 544 552 545 - if (shdr_rel_plt.sh_link != dynsym_idx) 553 + if (!shdr_rel_plt.sh_link) 554 + return 0; 555 + 556 + if (shdr_rel_plt.sh_link == ss->dynsym_idx) { 557 + scn_dynsym = ss->dynsym; 558 + shdr_dynsym = ss->dynshdr; 559 + } else if (shdr_rel_plt.sh_link == ss->symtab_idx) { 560 + /* 561 + * A static executable can have a .plt due to IFUNCs, in which 562 + * case .symtab is used not .dynsym. 563 + */ 564 + scn_dynsym = ss->symtab; 565 + shdr_dynsym = ss->symshdr; 566 + } else { 546 567 goto out_elf_end; 568 + } 569 + 570 + if (!scn_dynsym) 571 + return 0; 547 572 548 573 /* 549 574 * Fetch the relocation section to find the idxes to the GOT ··· 1086 1077 1087 1078 ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64); 1088 1079 1080 + ss->symtab_idx = 0; 1089 1081 ss->symtab = elf_section_by_name(elf, &ehdr, &ss->symshdr, ".symtab", 1090 - NULL); 1082 + &ss->symtab_idx); 1091 1083 if (ss->symshdr.sh_type != SHT_SYMTAB) 1092 1084 ss->symtab = NULL; 1093 1085
+1
tools/perf/util/symsrc.h
··· 26 26 GElf_Shdr opdshdr; 27 27 28 28 Elf_Scn *symtab; 29 + size_t symtab_idx; 29 30 GElf_Shdr symshdr; 30 31 31 32 Elf_Scn *dynsym;