2023-01-27 17:03:05

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH 0/9] perf symbols: Improve dso__synthesize_plt_symbols() for x86

Hi

This is the second of 2 patchsets to improve dso__synthesize_plt_symbols().
This patchset focuses on getting rid of unknown symbols that show up in
Intel PT traces.

x86 has 2 more plt's, namely .plt.sec and .plt.got, so support is added for
synthesizing symbols for them. Special handing is needed for IFUNC symbols,
and it is also possible to have a .plt for static executables, so support is
added for that.


Adrian Hunter (9):
perf symbols: Correct plt entry sizes for x86
perf symbols: Add support for x86 .plt.sec
perf symbols: Sort plt relocations for x86
perf symbols: Record whether a symbol is an alias for an IFUNC symbol
perf symbols: Add support for IFUNC symbols for x86_64
perf symbols: Allow for .plt without header
perf symbols: Allow for static executables with .plt
perf symbols: Start adding support for .plt.got for x86
perf symbols: Get symbols for .plt.got for x86-64

tools/perf/util/symbol-elf.c | 362 ++++++++++++++++++++++++++++++++++++++++---
tools/perf/util/symbol.c | 4 +
tools/perf/util/symbol.h | 2 +
tools/perf/util/symsrc.h | 1 +
4 files changed, 347 insertions(+), 22 deletions(-)


Regards
Adrian


2023-01-27 17:03:10

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH 1/9] perf symbols: Correct plt entry sizes for x86

In 32-bit executables the .plt entry size can be set to 4 when it is really
16. In fact the only sizes used for x86 (32 or 64 bit) are 8 or 16, so
check for those and, if not, use the alignment to choose which it is.

Example on Ubuntu 22.04 gcc 11.3:

Before:

$ cat tstpltlib.c
void fn1(void) {}
void fn2(void) {}
void fn3(void) {}
void fn4(void) {}
$ cat tstplt.c
void fn1(void);
void fn2(void);
void fn3(void);
void fn4(void);

int main()
{
fn4();
fn1();
fn2();
fn3();
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 -m32 -Wall -Wextra -shared -o libtstpltlib32.so tstpltlib.c
$ gcc -m32 -Wall -Wextra -o tstplt32 tstplt.c -L . -ltstpltlib32 -Wl,-rpath=$(pwd)
$ perf record -e intel_pt//u --filter 'filter main @ ./tstplt32' ./tstplt32
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.011 MB perf.data ]
$ readelf -SW tstplt32 | grep 'plt\|Name'
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[10] .rel.plt REL 0000041c 00041c 000028 08 AI 5 22 4
[12] .plt PROGBITS 00001030 001030 000060 04 AX 0 0 16 <- ES is 0x04, should be 0x10
[13] .plt.got PROGBITS 00001090 001090 000008 08 AX 0 0 8
$ perf script --itrace=be --ns -F+flags,-event,+addr,-period,-comm,-tid,-cpu,-dso
17894.383903029: tr strt 0 [unknown] => 565b81cd main+0x0
17894.383903029: tr end call 565b81d4 main+0x7 => 565b80d0 __x86.get_pc_thunk.bx+0x0
17894.383903031: tr strt 0 [unknown] => 565b81d9 main+0xc
17894.383903031: tr end call 565b81df main+0x12 => 565b8070 [unknown]
17894.383903032: tr strt 0 [unknown] => 565b81e4 main+0x17
17894.383903032: tr end call 565b81e4 main+0x17 => 565b8050 [unknown]
17894.383903033: tr strt 0 [unknown] => 565b81e9 main+0x1c
17894.383903033: tr end call 565b81e9 main+0x1c => 565b8080 [unknown]
17894.383903033: tr strt 0 [unknown] => 565b81ee main+0x21
17894.383903033: tr end call 565b81ee main+0x21 => 565b8060 [unknown]
17894.383903237: tr strt 0 [unknown] => 565b81f3 main+0x26
17894.383903237: tr end return 565b81fc main+0x2f => f7c21519 [unknown]

After:

$ perf script --itrace=be --ns -F+flags,-event,+addr,-period,-comm,-tid,-cpu,-dso
17894.383903029: tr strt 0 [unknown] => 565b81cd main+0x0
17894.383903029: tr end call 565b81d4 main+0x7 => 565b80d0 __x86.get_pc_thunk.bx+0x0
17894.383903031: tr strt 0 [unknown] => 565b81d9 main+0xc
17894.383903031: tr end call 565b81df main+0x12 => 565b8070 fn4@plt+0x0
17894.383903032: tr strt 0 [unknown] => 565b81e4 main+0x17
17894.383903032: tr end call 565b81e4 main+0x17 => 565b8050 fn1@plt+0x0
17894.383903033: tr strt 0 [unknown] => 565b81e9 main+0x1c
17894.383903033: tr end call 565b81e9 main+0x1c => 565b8080 fn2@plt+0x0
17894.383903033: tr strt 0 [unknown] => 565b81ee main+0x21
17894.383903033: tr end call 565b81ee main+0x21 => 565b8060 fn3@plt+0x0
17894.383903237: tr strt 0 [unknown] => 565b81f3 main+0x26
17894.383903237: tr end return 565b81fc main+0x2f => f7c21519 [unknown]

Signed-off-by: Adrian Hunter <[email protected]>
---
tools/perf/util/symbol-elf.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index aa62735aea7b..9328c162d68f 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -360,14 +360,23 @@ static bool get_plt_sizes(struct dso *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr_plt,
*plt_header_size = 128;
*plt_entry_size = 32;
return true;
+ case EM_386:
+ case EM_X86_64:
+ *plt_entry_size = shdr_plt->sh_entsize;
+ /* Size is 8 or 16, if not, assume alignment indicates size */
+ if (*plt_entry_size != 8 && *plt_entry_size != 16)
+ *plt_entry_size = shdr_plt->sh_addralign == 8 ? 8 : 16;
+ *plt_header_size = *plt_entry_size;
+ break;
default: /* FIXME: s390/alpha/mips/parisc/poperpc/sh/xtensa need to be checked */
*plt_header_size = shdr_plt->sh_entsize;
*plt_entry_size = shdr_plt->sh_entsize;
- if (*plt_entry_size)
- return true;
- pr_debug("Missing PLT entry size for %s\n", dso->long_name);
- return false;
+ break;
}
+ if (*plt_entry_size)
+ return true;
+ pr_debug("Missing PLT entry size for %s\n", dso->long_name);
+ return false;
}

/*
--
2.34.1


2023-01-27 17:03:12

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH 2/9] perf symbols: Add support for x86 .plt.sec

The section .plt.sec was originally added for MPX and was first called
.plt.bnd. While MPX has been deprecated, .plt.sec is now also used for IBT.
On x86_64, IBT seems to be enabled by default, but can be switched off
using gcc option -fcf-protection=none. On 32-bit, option -z ibt will
enable IBT.

With .plt.sec, calls are made into .plt.sec instead of .plt, so it
makes more sense to put the symbols there instead of .plt. A notable
difference is that .plt.sec does not have a header entry.

For x86, when synthesizing symbols for plt, use offset and entry size of
.plt.sec instead of .plt when there is a .plt.sec section.

Example on Ubuntu 22.04 gcc 11.3:

Before:

$ cat tstpltlib.c
void fn1(void) {}
void fn2(void) {}
void fn3(void) {}
void fn4(void) {}
$ cat tstplt.c
void fn1(void);
void fn2(void);
void fn3(void);
void fn4(void);

int main()
{
fn4();
fn1();
fn2();
fn3();
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 -Wall -Wextra -shared -o libtstpltlib.so tstpltlib.c
$ gcc -Wall -Wextra -o tstplt tstplt.c -L . -ltstpltlib -Wl,-rpath=$(pwd)
$ readelf -SW tstplt | grep 'plt\|Name'
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[11] .rela.plt RELA 0000000000000698 000698 000060 18 AI 6 24 8
[13] .plt PROGBITS 0000000000001020 001020 000050 10 AX 0 0 16
[14] .plt.got PROGBITS 0000000000001070 001070 000010 10 AX 0 0 16
[15] .plt.sec PROGBITS 0000000000001080 001080 000040 10 AX 0 0 16
$ perf record -e intel_pt//u --filter 'filter main @ ./tstplt' ./tstplt
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.015 MB perf.data ]
$ perf script --itrace=be --ns -F+flags,-event,+addr,-period,-comm,-tid,-cpu,-dso
38970.522546686: tr strt 0 [unknown] => 55fc222a81a9 main+0x0
38970.522546686: tr end call 55fc222a81b1 main+0x8 => 55fc222a80a0 [unknown]
38970.522546687: tr strt 0 [unknown] => 55fc222a81b6 main+0xd
38970.522546687: tr end call 55fc222a81b6 main+0xd => 55fc222a8080 [unknown]
38970.522546688: tr strt 0 [unknown] => 55fc222a81bb main+0x12
38970.522546688: tr end call 55fc222a81bb main+0x12 => 55fc222a80b0 [unknown]
38970.522546688: tr strt 0 [unknown] => 55fc222a81c0 main+0x17
38970.522546688: tr end call 55fc222a81c0 main+0x17 => 55fc222a8090 [unknown]
38970.522546689: tr strt 0 [unknown] => 55fc222a81c5 main+0x1c
38970.522546894: tr end return 55fc222a81cb main+0x22 => 7f3a4dc29d90 __libc_start_call_main+0x80

After:

$ perf script --itrace=be --ns -F+flags,-event,+addr,-period,-comm,-tid,-cpu,-dso
38970.522546686: tr strt 0 [unknown] => 55fc222a81a9 main+0x0
38970.522546686: tr end call 55fc222a81b1 main+0x8 => 55fc222a80a0 fn4@plt+0x0
38970.522546687: tr strt 0 [unknown] => 55fc222a81b6 main+0xd
38970.522546687: tr end call 55fc222a81b6 main+0xd => 55fc222a8080 fn1@plt+0x0
38970.522546688: tr strt 0 [unknown] => 55fc222a81bb main+0x12
38970.522546688: tr end call 55fc222a81bb main+0x12 => 55fc222a80b0 fn2@plt+0x0
38970.522546688: tr strt 0 [unknown] => 55fc222a81c0 main+0x17
38970.522546688: tr end call 55fc222a81c0 main+0x17 => 55fc222a8090 fn3@plt+0x0
38970.522546689: tr strt 0 [unknown] => 55fc222a81c5 main+0x1c
38970.522546894: tr end return 55fc222a81cb main+0x22 => 7f3a4dc29d90 __libc_start_call_main+0x80

Signed-off-by: Adrian Hunter <[email protected]>
---
tools/perf/util/symbol-elf.c | 30 +++++++++++++++++++++++-------
1 file changed, 23 insertions(+), 7 deletions(-)

diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 9328c162d68f..bb1b5cb3ff12 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -379,6 +379,11 @@ static bool get_plt_sizes(struct dso *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr_plt,
return false;
}

+static bool machine_is_x86(GElf_Half e_machine)
+{
+ return e_machine == EM_386 || e_machine == EM_X86_64;
+}
+
/*
* We need to check if we have a .dynsym, so that we can handle the
* .plt, synthesizing its symbols, that aren't on the symtabs (be it
@@ -391,8 +396,8 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
uint32_t nr_rel_entries, idx;
GElf_Sym sym;
u64 plt_offset, plt_header_size, plt_entry_size;
- GElf_Shdr shdr_plt;
- struct symbol *f;
+ GElf_Shdr shdr_plt, plt_sec_shdr;
+ struct symbol *f, *plt_sym;
GElf_Shdr shdr_rel_plt, shdr_dynsym;
Elf_Data *syms, *symstrs;
Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym;
@@ -422,10 +427,23 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
return 0;

/* Add a symbol for .plt header */
- f = symbol__new(shdr_plt.sh_offset, plt_header_size, STB_GLOBAL, STT_FUNC, ".plt");
- if (!f)
+ plt_sym = symbol__new(shdr_plt.sh_offset, plt_header_size, STB_GLOBAL, STT_FUNC, ".plt");
+ if (!plt_sym)
goto out_elf_end;
- symbols__insert(&dso->symbols, f);
+ symbols__insert(&dso->symbols, plt_sym);
+
+ /* Only x86 has .plt.sec */
+ if (machine_is_x86(ehdr.e_machine) &&
+ elf_section_by_name(elf, &ehdr, &plt_sec_shdr, ".plt.sec", NULL)) {
+ if (!get_plt_sizes(dso, &ehdr, &plt_sec_shdr, &plt_header_size, &plt_entry_size))
+ return 0;
+ /* Extend .plt symbol to entire .plt */
+ plt_sym->end = plt_sym->start + shdr_plt.sh_size;
+ /* Use .plt.sec offset */
+ plt_offset = plt_sec_shdr.sh_offset;
+ } else {
+ plt_offset = shdr_plt.sh_offset + plt_header_size;
+ }

scn_dynsym = ss->dynsym;
shdr_dynsym = ss->dynshdr;
@@ -474,8 +492,6 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
goto out_elf_end;

nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
- plt_offset = shdr_plt.sh_offset;
- plt_offset += plt_header_size;

ri.is_rela = shdr_rel_plt.sh_type == SHT_RELA;

--
2.34.1


2023-01-27 17:03:27

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH 3/9] perf symbols: Sort plt relocations for x86

For x86, with the addition of IFUNCs, relocation information becomes
disordered with respect to plt. Correct that by sorting the relocations by
offset.

Example:

Before:

$ cat tstpltlib.c
void fn1(void) {}
void fn2(void) {}
void fn3(void) {}
void fn4(void) {}
$ cat tstpltifunc.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")));

void fn1(void);
void fn2(void);
void fn3(void);
void fn4(void);

int main()
{
fn4();
fn1();
thing();
fn2();
fn3();
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 -Wall -Wextra -shared -o libtstpltlib.so tstpltlib.c
$ gcc -Wall -Wextra -Wno-uninitialized -o tstpltifunc tstpltifunc.c -L . -ltstpltlib -Wl,-rpath="$(pwd)"
$ readelf -rW tstpltifunc | grep -A99 plt
Relocation section '.rela.plt' at offset 0x738 contains 8 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000003f98 0000000300000007 R_X86_64_JUMP_SLOT 0000000000000000 puts@GLIBC_2.2.5 + 0
0000000000003fa8 0000000400000007 R_X86_64_JUMP_SLOT 0000000000000000 __stack_chk_fail@GLIBC_2.4 + 0
0000000000003fb0 0000000500000007 R_X86_64_JUMP_SLOT 0000000000000000 fn1 + 0
0000000000003fb8 0000000600000007 R_X86_64_JUMP_SLOT 0000000000000000 fn3 + 0
0000000000003fc0 0000000800000007 R_X86_64_JUMP_SLOT 0000000000000000 fn4 + 0
0000000000003fc8 0000000900000007 R_X86_64_JUMP_SLOT 0000000000000000 fn2 + 0
0000000000003fd0 0000000b00000007 R_X86_64_JUMP_SLOT 0000000000000000 getrandom@GLIBC_2.25 + 0
0000000000003fa0 0000000000000025 R_X86_64_IRELATIVE 125d
$ perf record -e intel_pt//u --filter 'filter main @ ./tstpltifunc' ./tstpltifunc
thing2
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.029 MB perf.data ]
$ perf script --itrace=be --ns -F+flags,-event,+addr,-period,-comm,-tid,-cpu,-dso
20417.302513948: tr strt 0 [unknown] => 5629a74892be main+0x0
20417.302513948: tr end call 5629a74892c6 main+0x8 => 5629a7489110 fn2@plt+0x0
20417.302513949: tr strt 0 [unknown] => 5629a74892cb main+0xd
20417.302513949: tr end call 5629a74892cb main+0xd => 5629a74890f0 fn3@plt+0x0
20417.302513950: tr strt 0 [unknown] => 5629a74892d0 main+0x12
20417.302513950: tr end call 5629a74892d0 main+0x12 => 5629a74890d0 __stack_chk_fail@plt+0x0
20417.302528114: tr strt 0 [unknown] => 5629a74892d5 main+0x17
20417.302528114: tr end call 5629a74892d5 main+0x17 => 5629a7489120 getrandom@plt+0x0
20417.302528115: tr strt 0 [unknown] => 5629a74892da main+0x1c
20417.302528115: tr end call 5629a74892da main+0x1c => 5629a7489100 fn4@plt+0x0
20417.302528115: tr strt 0 [unknown] => 5629a74892df main+0x21
20417.302528115: tr end return 5629a74892e5 main+0x27 => 7ff14da29d90 __libc_start_call_main+0x80

After:

$ perf script --itrace=be --ns -F+flags,-event,+addr,-period,-comm,-tid,-cpu,-dso
20417.302513948: tr strt 0 [unknown] => 5629a74892be main+0x0
20417.302513948: tr end call 5629a74892c6 main+0x8 => 5629a7489110 fn4@plt+0x0
20417.302513949: tr strt 0 [unknown] => 5629a74892cb main+0xd
20417.302513949: tr end call 5629a74892cb main+0xd => 5629a74890f0 fn1@plt+0x0
20417.302513950: tr strt 0 [unknown] => 5629a74892d0 main+0x12
20417.302513950: tr end call 5629a74892d0 main+0x12 => 5629a74890d0 offset_0x10d0@plt+0x0
20417.302528114: tr strt 0 [unknown] => 5629a74892d5 main+0x17
20417.302528114: tr end call 5629a74892d5 main+0x17 => 5629a7489120 fn2@plt+0x0
20417.302528115: tr strt 0 [unknown] => 5629a74892da main+0x1c
20417.302528115: tr end call 5629a74892da main+0x1c => 5629a7489100 fn3@plt+0x0
20417.302528115: tr strt 0 [unknown] => 5629a74892df main+0x21
20417.302528115: tr end return 5629a74892e5 main+0x27 => 7ff14da29d90 __libc_start_call_main+0x80

Signed-off-by: Adrian Hunter <[email protected]>
---
tools/perf/util/symbol-elf.c | 60 ++++++++++++++++++++++++++++++++++--
1 file changed, 57 insertions(+), 3 deletions(-)

diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index bb1b5cb3ff12..07cfcf8f40e3 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -324,6 +324,8 @@ static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
}

struct rel_info {
+ u32 nr_entries;
+ u32 *sorted;
bool is_rela;
Elf_Data *reldata;
GElf_Rela rela;
@@ -332,6 +334,7 @@ struct rel_info {

static u32 get_rel_symidx(struct rel_info *ri, u32 idx)
{
+ idx = ri->sorted ? ri->sorted[idx] : idx;
if (ri->is_rela) {
gelf_getrela(ri->reldata, idx, &ri->rela);
return GELF_R_SYM(ri->rela.r_info);
@@ -340,6 +343,49 @@ static u32 get_rel_symidx(struct rel_info *ri, u32 idx)
return GELF_R_SYM(ri->rel.r_info);
}

+static u64 get_rel_offset(struct rel_info *ri, u32 x)
+{
+ if (ri->is_rela) {
+ GElf_Rela rela;
+
+ gelf_getrela(ri->reldata, x, &rela);
+ return rela.r_offset;
+ } else {
+ GElf_Rel rel;
+
+ gelf_getrel(ri->reldata, x, &rel);
+ return rel.r_offset;
+ }
+}
+
+static int rel_cmp(const void *a, const void *b, void *r)
+{
+ struct rel_info *ri = r;
+ u64 a_offset = get_rel_offset(ri, *(const u32 *)a);
+ u64 b_offset = get_rel_offset(ri, *(const u32 *)b);
+
+ return a_offset < b_offset ? -1 : (a_offset > b_offset ? 1 : 0);
+}
+
+static int sort_rel(struct rel_info *ri)
+{
+ size_t sz = sizeof(ri->sorted[0]);
+ u32 i;
+
+ ri->sorted = calloc(ri->nr_entries, sz);
+ if (!ri->sorted)
+ return -1;
+ for (i = 0; i < ri->nr_entries; i++)
+ ri->sorted[i] = i;
+ qsort_r(ri->sorted, ri->nr_entries, sz, rel_cmp, ri);
+ return 0;
+}
+
+static void exit_rel(struct rel_info *ri)
+{
+ free(ri->sorted);
+}
+
static bool get_plt_sizes(struct dso *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr_plt,
u64 *plt_header_size, u64 *plt_entry_size)
{
@@ -393,7 +439,7 @@ static bool machine_is_x86(GElf_Half e_machine)
*/
int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
{
- uint32_t nr_rel_entries, idx;
+ uint32_t idx;
GElf_Sym sym;
u64 plt_offset, plt_header_size, plt_entry_size;
GElf_Shdr shdr_plt, plt_sec_shdr;
@@ -491,11 +537,18 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
if (symstrs->d_size == 0)
goto out_elf_end;

- nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
+ ri.nr_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;

ri.is_rela = shdr_rel_plt.sh_type == SHT_RELA;

- for (idx = 0; idx < nr_rel_entries; idx++) {
+ /*
+ * x86 doesn't insert IFUNC relocations in .plt order, so sort to get
+ * back in order.
+ */
+ if (machine_is_x86(ehdr.e_machine) && sort_rel(&ri))
+ goto out_elf_end;
+
+ for (idx = 0; idx < ri.nr_entries; idx++) {
const char *elf_name = NULL;
char *demangled = NULL;

@@ -523,6 +576,7 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)

err = 0;
out_elf_end:
+ exit_rel(&ri);
if (err == 0)
return nr;
pr_debug("%s: problems reading %s PLT info.\n",
--
2.34.1


2023-01-27 17:03:30

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH 4/9] perf symbols: Record whether a symbol is an alias for an IFUNC symbol

To assist with synthesizing plt symbols for IFUNCs, record whether a
symbol is an alias of an IFUNC symbol.

Signed-off-by: Adrian Hunter <[email protected]>
---
tools/perf/util/symbol.c | 4 ++++
tools/perf/util/symbol.h | 2 ++
2 files changed, 6 insertions(+)

diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index a024f06f75d8..d05727fcb30d 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -201,10 +201,14 @@ void symbols__fixup_duplicate(struct rb_root_cached *symbols)
continue;

if (choose_best_symbol(curr, next) == SYMBOL_A) {
+ if (next->type == STT_GNU_IFUNC)
+ curr->ifunc_alias = true;
rb_erase_cached(&next->rb_node, symbols);
symbol__delete(next);
goto again;
} else {
+ if (curr->type == STT_GNU_IFUNC)
+ next->ifunc_alias = true;
nd = rb_next(&curr->rb_node);
rb_erase_cached(&curr->rb_node, symbols);
symbol__delete(curr);
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 2fdeb22bd02f..7558735543c2 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -64,6 +64,8 @@ struct symbol {
u8 inlined:1;
/** Has symbol__annotate2 been performed. */
u8 annotate2:1;
+ /** Symbol is an alias of an STT_GNU_IFUNC */
+ u8 ifunc_alias:1;
/** Architecture specific. Unused except on PPC where it holds st_other. */
u8 arch_sym;
/** The name of length namelen associated with the symbol. */
--
2.34.1


2023-01-27 17:03:37

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH 5/9] perf symbols: Add support for IFUNC symbols for x86_64

For x86_64, the GNU linker is putting IFUNC information in the relocation
addend, so use it to try to find a symbol for plt entries that refer to
IFUNCs.

Example:

Before:

$ cat tstpltlib.c
void fn1(void) {}
void fn2(void) {}
void fn3(void) {}
void fn4(void) {}
$ cat tstpltifunc.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")));

void fn1(void);
void fn2(void);
void fn3(void);
void fn4(void);

int main()
{
fn4();
fn1();
thing();
fn2();
fn3();
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 -Wall -Wextra -shared -o libtstpltlib.so tstpltlib.c
$ gcc -Wall -Wextra -Wno-uninitialized -o tstpltifunc tstpltifunc.c -L . -ltstpltlib -Wl,-rpath="$(pwd)"
$ readelf -rW tstpltifunc | grep -A99 plt
Relocation section '.rela.plt' at offset 0x738 contains 8 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000003f98 0000000300000007 R_X86_64_JUMP_SLOT 0000000000000000 puts@GLIBC_2.2.5 + 0
0000000000003fa8 0000000400000007 R_X86_64_JUMP_SLOT 0000000000000000 __stack_chk_fail@GLIBC_2.4 + 0
0000000000003fb0 0000000500000007 R_X86_64_JUMP_SLOT 0000000000000000 fn1 + 0
0000000000003fb8 0000000600000007 R_X86_64_JUMP_SLOT 0000000000000000 fn3 + 0
0000000000003fc0 0000000800000007 R_X86_64_JUMP_SLOT 0000000000000000 fn4 + 0
0000000000003fc8 0000000900000007 R_X86_64_JUMP_SLOT 0000000000000000 fn2 + 0
0000000000003fd0 0000000b00000007 R_X86_64_JUMP_SLOT 0000000000000000 getrandom@GLIBC_2.25 + 0
0000000000003fa0 0000000000000025 R_X86_64_IRELATIVE 125d
$ perf record -e intel_pt//u --filter 'filter main @ ./tstpltifunc' ./tstpltifunc
thing2
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.016 MB perf.data ]
$ perf script --itrace=be --ns -F+flags,-event,+addr,-period,-comm,-tid,-cpu,-dso
21860.073683659: tr strt 0 [unknown] => 561e212c42be main+0x0
21860.073683659: tr end call 561e212c42c6 main+0x8 => 561e212c4110 fn4@plt+0x0
21860.073683661: tr strt 0 [unknown] => 561e212c42cb main+0xd
21860.073683661: tr end call 561e212c42cb main+0xd => 561e212c40f0 fn1@plt+0x0
21860.073683661: tr strt 0 [unknown] => 561e212c42d0 main+0x12
21860.073683661: tr end call 561e212c42d0 main+0x12 => 561e212c40d0 offset_0x10d0@plt+0x0
21860.073698451: tr strt 0 [unknown] => 561e212c42d5 main+0x17
21860.073698451: tr end call 561e212c42d5 main+0x17 => 561e212c4120 fn2@plt+0x0
21860.073698451: tr strt 0 [unknown] => 561e212c42da main+0x1c
21860.073698451: tr end call 561e212c42da main+0x1c => 561e212c4100 fn3@plt+0x0
21860.073698452: tr strt 0 [unknown] => 561e212c42df main+0x21
21860.073698452: tr end return 561e212c42e5 main+0x27 => 7fb51cc29d90 __libc_start_call_main+0x80

After:

$ perf script --itrace=be --ns -F+flags,-event,+addr,-period,-comm,-tid,-cpu,-dso
21860.073683659: tr strt 0 [unknown] => 561e212c42be main+0x0
21860.073683659: tr end call 561e212c42c6 main+0x8 => 561e212c4110 fn4@plt+0x0
21860.073683661: tr strt 0 [unknown] => 561e212c42cb main+0xd
21860.073683661: tr end call 561e212c42cb main+0xd => 561e212c40f0 fn1@plt+0x0
21860.073683661: tr strt 0 [unknown] => 561e212c42d0 main+0x12
21860.073683661: tr end call 561e212c42d0 main+0x12 => 561e212c40d0 thing_ifunc@plt+0x0
21860.073698451: tr strt 0 [unknown] => 561e212c42d5 main+0x17
21860.073698451: tr end call 561e212c42d5 main+0x17 => 561e212c4120 fn2@plt+0x0
21860.073698451: tr strt 0 [unknown] => 561e212c42da main+0x1c
21860.073698451: tr end call 561e212c42da main+0x1c => 561e212c4100 fn3@plt+0x0
21860.073698452: tr strt 0 [unknown] => 561e212c42df main+0x21
21860.073698452: tr end return 561e212c42e5 main+0x27 => 7fb51cc29d90 __libc_start_call_main+0x80

Signed-off-by: Adrian Hunter <[email protected]>
---
tools/perf/util/symbol-elf.c | 38 +++++++++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 07cfcf8f40e3..a002fc0bea03 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -381,6 +381,42 @@ static int sort_rel(struct rel_info *ri)
return 0;
}

+/*
+ * For x86_64, the GNU linker is putting IFUNC information in the relocation
+ * addend.
+ */
+static bool addend_may_be_ifunc(GElf_Ehdr *ehdr, struct rel_info *ri)
+{
+ return ehdr->e_machine == EM_X86_64 && ri->is_rela &&
+ GELF_R_TYPE(ri->rela.r_info) == R_X86_64_IRELATIVE;
+}
+
+static bool get_ifunc_name(Elf *elf, struct dso *dso, GElf_Ehdr *ehdr,
+ struct rel_info *ri, char *buf, size_t buf_sz)
+{
+ u64 addr = ri->rela.r_addend;
+ struct symbol *sym;
+ GElf_Phdr phdr;
+
+ if (!addend_may_be_ifunc(ehdr, ri))
+ return false;
+
+ if (elf_read_program_header(elf, addr, &phdr))
+ return false;
+
+ addr -= phdr.p_vaddr - phdr.p_offset;
+
+ sym = dso__find_symbol_nocache(dso, addr);
+
+ /* Expecting the address to be an IFUNC or IFUNC alias */
+ if (!sym || sym->start != addr || (sym->type != STT_GNU_IFUNC && !sym->ifunc_alias))
+ return false;
+
+ snprintf(buf, buf_sz, "%s@plt", sym->name);
+
+ return true;
+}
+
static void exit_rel(struct rel_info *ri)
{
free(ri->sorted);
@@ -560,7 +596,7 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
elf_name = demangled;
if (*elf_name)
snprintf(sympltname, sizeof(sympltname), "%s@plt", elf_name);
- else
+ else if (!get_ifunc_name(elf, dso, &ehdr, &ri, sympltname, sizeof(sympltname)))
snprintf(sympltname, sizeof(sympltname),
"offset_%#" PRIx64 "@plt", plt_offset);
free(demangled);
--
2.34.1


2023-01-27 17:03:57

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH 6/9] perf symbols: Allow for .plt without header

A static executable can have a .plt due to the presence of IFUNCs.
In that case the .plt does not have a header. Check for whether
there is a header by comparing the number of entries to the
number of relocation entries.

Signed-off-by: Adrian Hunter <[email protected]>
---
tools/perf/util/symbol-elf.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index a002fc0bea03..8f7802097c72 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -489,6 +489,7 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
Elf *elf;
int nr = 0, err = -1;
struct rel_info ri = { .is_rela = false };
+ bool lazy_plt;

elf = ss->elf;
ehdr = ss->ehdr;
@@ -523,8 +524,10 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
plt_sym->end = plt_sym->start + shdr_plt.sh_size;
/* Use .plt.sec offset */
plt_offset = plt_sec_shdr.sh_offset;
+ lazy_plt = false;
} else {
- plt_offset = shdr_plt.sh_offset + plt_header_size;
+ plt_offset = shdr_plt.sh_offset;
+ lazy_plt = true;
}

scn_dynsym = ss->dynsym;
@@ -577,6 +580,17 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)

ri.is_rela = shdr_rel_plt.sh_type == SHT_RELA;

+ if (lazy_plt) {
+ /*
+ * Assume a .plt with the same number of entries as the number
+ * of relocation entries is not lazy and does not have a header.
+ */
+ if (ri.nr_entries * plt_entry_size == shdr_plt.sh_size)
+ dso__delete_symbol(dso, plt_sym);
+ else
+ plt_offset += plt_header_size;
+ }
+
/*
* x86 doesn't insert IFUNC relocations in .plt order, so sort to get
* back in order.
--
2.34.1


2023-01-27 17:04:00

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH 7/9] 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

Signed-off-by: Adrian Hunter <[email protected]>
---
tools/perf/util/symbol-elf.c | 30 ++++++++++++++++++++----------
tools/perf/util/symsrc.h | 1 +
2 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 8f7802097c72..9e265a726418 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -483,7 +483,6 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
GElf_Shdr shdr_rel_plt, shdr_dynsym;
Elf_Data *syms, *symstrs;
Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym;
- size_t dynsym_idx;
GElf_Ehdr ehdr;
char sympltname[1024];
Elf *elf;
@@ -530,13 +529,6 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
lazy_plt = true;
}

- scn_dynsym = ss->dynsym;
- shdr_dynsym = ss->dynshdr;
- dynsym_idx = ss->dynsym_idx;
-
- if (scn_dynsym == NULL)
- return 0;
-
scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
".rela.plt", NULL);
if (scn_plt_rel == NULL) {
@@ -550,8 +542,25 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
shdr_rel_plt.sh_type != SHT_REL)
return 0;

- if (shdr_rel_plt.sh_link != dynsym_idx)
+ if (!shdr_rel_plt.sh_link)
+ return 0;
+
+ if (shdr_rel_plt.sh_link == ss->dynsym_idx) {
+ scn_dynsym = ss->dynsym;
+ shdr_dynsym = ss->dynshdr;
+ } else if (shdr_rel_plt.sh_link == ss->symtab_idx) {
+ /*
+ * A static executable can have a .plt due to IFUNCs, in which
+ * case .symtab is used not .dynsym.
+ */
+ scn_dynsym = ss->symtab;
+ shdr_dynsym = ss->symshdr;
+ } else {
goto out_elf_end;
+ }
+
+ if (!scn_dynsym)
+ return 0;

/*
* Fetch the relocation section to find the idxes to the GOT
@@ -1077,8 +1086,9 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,

ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64);

+ ss->symtab_idx = 0;
ss->symtab = elf_section_by_name(elf, &ehdr, &ss->symshdr, ".symtab",
- NULL);
+ &ss->symtab_idx);
if (ss->symshdr.sh_type != SHT_SYMTAB)
ss->symtab = NULL;

diff --git a/tools/perf/util/symsrc.h b/tools/perf/util/symsrc.h
index 2665b4bde751..edf82028c9e6 100644
--- a/tools/perf/util/symsrc.h
+++ b/tools/perf/util/symsrc.h
@@ -26,6 +26,7 @@ struct symsrc {
GElf_Shdr opdshdr;

Elf_Scn *symtab;
+ size_t symtab_idx;
GElf_Shdr symshdr;

Elf_Scn *dynsym;
--
2.34.1


2023-01-27 17:04:11

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH 8/9] perf symbols: Start adding support for .plt.got for x86

For x86, .plt.got is used, for example, when the address is taken of a
dynamically linked function. Start adding support by synthesizing a
symbol for each entry. A subsequent patch will attempt to get a better
name for the symbol.

Example:

Before:

$ cat tstpltlib.c
void fn1(void) {}
void fn2(void) {}
void fn3(void) {}
void fn4(void) {}
$ cat tstpltgot.c
void fn1(void);
void fn2(void);
void fn3(void);
void fn4(void);

void callfn(void (*fn)(void))
{
fn();
}

int main()
{
fn4();
fn1();
callfn(fn3);
fn2();
fn3();
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 -Wall -Wextra -shared -o libtstpltlib.so tstpltlib.c
$ gcc -Wall -Wextra -o tstpltgot tstpltgot.c -L . -ltstpltlib -Wl,-rpath="$(pwd)"
$ readelf -SW tstpltgot | grep 'Name\|plt\|dyn'
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 6] .dynsym DYNSYM 00000000000003d8 0003d8 0000f0 18 A 7 1 8
[ 7] .dynstr STRTAB 00000000000004c8 0004c8 0000c6 00 A 0 0 1
[10] .rela.dyn RELA 00000000000005d8 0005d8 0000d8 18 A 6 0 8
[11] .rela.plt RELA 00000000000006b0 0006b0 000048 18 AI 6 24 8
[13] .plt PROGBITS 0000000000001020 001020 000040 10 AX 0 0 16
[14] .plt.got PROGBITS 0000000000001060 001060 000020 10 AX 0 0 16
[15] .plt.sec PROGBITS 0000000000001080 001080 000030 10 AX 0 0 16
[23] .dynamic DYNAMIC 0000000000003d90 002d90 000210 10 WA 7 0 8
$ perf record -e intel_pt//u --filter 'filter main @ ./tstpltgot , filter callfn @ ./tstpltgot' ./tstpltgot
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.011 MB perf.data ]
$ perf script --itrace=be --ns -F+flags,-event,+addr,-period,-comm,-tid,-cpu,-dso
28393.810326915: tr strt 0 [unknown] => 562350baa1b2 main+0x0
28393.810326915: tr end call 562350baa1ba main+0x8 => 562350baa090 fn4@plt+0x0
28393.810326917: tr strt 0 [unknown] => 562350baa1bf main+0xd
28393.810326917: tr end call 562350baa1bf main+0xd => 562350baa080 fn1@plt+0x0
28393.810326917: tr strt 0 [unknown] => 562350baa1c4 main+0x12
28393.810326917: call 562350baa1ce main+0x1c => 562350baa199 callfn+0x0
28393.810326917: tr end call 562350baa1ad callfn+0x14 => 7f607d36110f fn3+0x0
28393.810326922: tr strt 0 [unknown] => 562350baa1af callfn+0x16
28393.810326922: return 562350baa1b1 callfn+0x18 => 562350baa1d3 main+0x21
28393.810326922: tr end call 562350baa1d3 main+0x21 => 562350baa0a0 fn2@plt+0x0
28393.810326924: tr strt 0 [unknown] => 562350baa1d8 main+0x26
28393.810326924: tr end call 562350baa1d8 main+0x26 => 562350baa060 [unknown] <- call to fn3 via .plt.got
28393.810326925: tr strt 0 [unknown] => 562350baa1dd main+0x2b
28393.810326925: tr end return 562350baa1e3 main+0x31 => 7f607d029d90 __libc_start_call_main+0x80

After:

$ perf script --itrace=be --ns -F+flags,-event,+addr,-period,-comm,-tid,-cpu,-dso
28393.810326915: tr strt 0 [unknown] => 562350baa1b2 main+0x0
28393.810326915: tr end call 562350baa1ba main+0x8 => 562350baa090 fn4@plt+0x0
28393.810326917: tr strt 0 [unknown] => 562350baa1bf main+0xd
28393.810326917: tr end call 562350baa1bf main+0xd => 562350baa080 fn1@plt+0x0
28393.810326917: tr strt 0 [unknown] => 562350baa1c4 main+0x12
28393.810326917: call 562350baa1ce main+0x1c => 562350baa199 callfn+0x0
28393.810326917: tr end call 562350baa1ad callfn+0x14 => 7f607d36110f fn3+0x0
28393.810326922: tr strt 0 [unknown] => 562350baa1af callfn+0x16
28393.810326922: return 562350baa1b1 callfn+0x18 => 562350baa1d3 main+0x21
28393.810326922: tr end call 562350baa1d3 main+0x21 => 562350baa0a0 fn2@plt+0x0
28393.810326924: tr strt 0 [unknown] => 562350baa1d8 main+0x26
28393.810326924: tr end call 562350baa1d8 main+0x26 => 562350baa060 offset_0x1060@plt+0x0
28393.810326925: tr strt 0 [unknown] => 562350baa1dd main+0x2b
28393.810326925: tr end return 562350baa1e3 main+0x31 => 7f607d029d90 __libc_start_call_main+0x80

Signed-off-by: Adrian Hunter <[email protected]>
---
tools/perf/util/symbol-elf.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)

diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 9e265a726418..254116d40e59 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -466,6 +466,30 @@ static bool machine_is_x86(GElf_Half e_machine)
return e_machine == EM_386 || e_machine == EM_X86_64;
}

+static int dso__synthesize_plt_got_symbols(struct dso *dso, Elf *elf,
+ GElf_Ehdr *ehdr,
+ char *buf, size_t buf_sz)
+{
+ struct symbol *sym;
+ GElf_Shdr shdr;
+ Elf_Scn *scn;
+ size_t i;
+
+ scn = elf_section_by_name(elf, ehdr, &shdr, ".plt.got", NULL);
+ if (!scn || !shdr.sh_entsize)
+ return 0;
+
+ for (i = 0; i < shdr.sh_size; i += shdr.sh_entsize) {
+ snprintf(buf, buf_sz, "offset_%#zx@plt", shdr.sh_offset + i);
+ sym = symbol__new(shdr.sh_offset + i, shdr.sh_entsize, STB_GLOBAL, STT_FUNC, buf);
+ if (!sym)
+ return -1;
+ symbols__insert(&dso->symbols, sym);
+ }
+
+ return 0;
+}
+
/*
* We need to check if we have a .dynsym, so that we can handle the
* .plt, synthesizing its symbols, that aren't on the symtabs (be it
@@ -514,6 +538,11 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
goto out_elf_end;
symbols__insert(&dso->symbols, plt_sym);

+ /* Only x86 has .plt.got */
+ if (machine_is_x86(ehdr.e_machine) &&
+ dso__synthesize_plt_got_symbols(dso, elf, &ehdr, sympltname, sizeof(sympltname)))
+ goto out_elf_end;
+
/* Only x86 has .plt.sec */
if (machine_is_x86(ehdr.e_machine) &&
elf_section_by_name(elf, &ehdr, &plt_sec_shdr, ".plt.sec", NULL)) {
--
2.34.1


2023-01-27 17:04:24

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH 9/9] perf symbols: Get symbols for .plt.got for x86-64

For x86_64, determine a symbol for .plt.got entries. That requires
computing the target offset and finding that in .rela.dyn, which in
turn means .rela.dyn needs to be sorted by offset.

Example:

In this example, the GNU C Library is using .plt.got for malloc and
free.

Before:

$ 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.
$ perf record -e intel_pt//u uname
Linux
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.027 MB perf.data ]
$ perf script --itrace=be --ns -F-event,+addr,-period,-comm,-tid,-cpu > /tmp/cmp1.txt

After:

$ perf script --itrace=be --ns -F-event,+addr,-period,-comm,-tid,-cpu > /tmp/cmp2.txt
$ diff /tmp/cmp1.txt /tmp/cmp2.txt | head -12
15509,15510c15509,15510
< 27046.755390907: 7f0b2943e3ab _nl_normalize_codeset+0x5b (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b29428380 offset_0x28380@plt+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
< 27046.755390907: 7f0b29428384 offset_0x28380@plt+0x4 (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b294a5120 malloc+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
---
> 27046.755390907: 7f0b2943e3ab _nl_normalize_codeset+0x5b (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b29428380 malloc@plt+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
> 27046.755390907: 7f0b29428384 malloc@plt+0x4 (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b294a5120 malloc+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
15821,15822c15821,15822
< 27046.755394865: 7f0b2943850c _nl_load_locale_from_archive+0x5bc (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b29428370 offset_0x28370@plt+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
< 27046.755394865: 7f0b29428374 offset_0x28370@plt+0x4 (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b294a5460 cfree@GLIBC_2.2.5+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
---
> 27046.755394865: 7f0b2943850c _nl_load_locale_from_archive+0x5bc (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b29428370 free@plt+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
> 27046.755394865: 7f0b29428374 free@plt+0x4 (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b294a5460 cfree@GLIBC_2.2.5+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)

Signed-off-by: Adrian Hunter <[email protected]>
---
tools/perf/util/symbol-elf.c | 158 ++++++++++++++++++++++++++++++++++-
1 file changed, 154 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 254116d40e59..4fc8e7fc10f4 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -466,28 +466,178 @@ static bool machine_is_x86(GElf_Half e_machine)
return e_machine == EM_386 || e_machine == EM_X86_64;
}

+struct rela_dyn {
+ GElf_Addr offset;
+ u32 sym_idx;
+};
+
+struct rela_dyn_info {
+ struct dso *dso;
+ Elf_Data *plt_got_data;
+ u32 nr_entries;
+ struct rela_dyn *sorted;
+ Elf_Data *dynsym_data;
+ Elf_Data *dynstr_data;
+ Elf_Data *rela_dyn_data;
+};
+
+static void exit_rela_dyn(struct rela_dyn_info *di)
+{
+ free(di->sorted);
+}
+
+static int cmp_offset(const void *a, const void *b)
+{
+ const struct rela_dyn *va = a;
+ const struct rela_dyn *vb = b;
+
+ return va->offset < vb->offset ? -1 : (va->offset > vb->offset ? 1 : 0);
+}
+
+static int sort_rela_dyn(struct rela_dyn_info *di)
+{
+ u32 i, n;
+
+ di->sorted = calloc(di->nr_entries, sizeof(di->sorted[0]));
+ if (!di->sorted)
+ return -1;
+
+ /* Get data for sorting: the offset and symbol index */
+ for (i = 0, n = 0; i < di->nr_entries; i++) {
+ GElf_Rela rela;
+ u32 sym_idx;
+
+ gelf_getrela(di->rela_dyn_data, i, &rela);
+ sym_idx = GELF_R_SYM(rela.r_info);
+ if (sym_idx) {
+ di->sorted[n].sym_idx = sym_idx;
+ di->sorted[n].offset = rela.r_offset;
+ n += 1;
+ }
+ }
+
+ /* Sort by offset */
+ di->nr_entries = n;
+ qsort(di->sorted, n, sizeof(di->sorted[0]), cmp_offset);
+
+ return 0;
+}
+
+static void get_rela_dyn_info(Elf *elf, GElf_Ehdr *ehdr, struct rela_dyn_info *di, Elf_Scn *scn)
+{
+ GElf_Shdr rela_dyn_shdr;
+ GElf_Shdr shdr;
+
+ di->plt_got_data = elf_getdata(scn, NULL);
+
+ scn = elf_section_by_name(elf, ehdr, &rela_dyn_shdr, ".rela.dyn", NULL);
+ if (!scn || !rela_dyn_shdr.sh_link || !rela_dyn_shdr.sh_entsize)
+ return;
+
+ di->nr_entries = rela_dyn_shdr.sh_size / rela_dyn_shdr.sh_entsize;
+ di->rela_dyn_data = elf_getdata(scn, NULL);
+
+ scn = elf_getscn(elf, rela_dyn_shdr.sh_link);
+ if (!scn || !gelf_getshdr(scn, &shdr) || !shdr.sh_link)
+ return;
+
+ di->dynsym_data = elf_getdata(scn, NULL);
+ di->dynstr_data = elf_getdata(elf_getscn(elf, shdr.sh_link), NULL);
+
+ if (!di->plt_got_data || !di->dynstr_data || !di->dynsym_data || !di->rela_dyn_data)
+ return;
+
+ /* Sort into offset order */
+ sort_rela_dyn(di);
+}
+
+/* Get instruction displacement from a plt entry for x86_64 */
+static u32 get_x86_64_plt_disp(const u8 *p)
+{
+ u8 endbr64[] = {0xf3, 0x0f, 0x1e, 0xfa};
+ int n = 0;
+
+ /* Skip endbr64 */
+ if (!memcmp(p, endbr64, sizeof(endbr64)))
+ n += sizeof(endbr64);
+ /* Skip bnd prefix */
+ if (p[n] == 0xf2)
+ n += 1;
+ /* jmp with 4-byte displacement */
+ if (p[n] == 0xff && p[n + 1] == 0x25) {
+ n += 2;
+ /* Also add offset from start of entry to end of instruction */
+ return n + 4 + le32toh(*(const u32 *)(p + n));
+ }
+ return 0;
+}
+
+static bool get_plt_got_name(GElf_Shdr *shdr, size_t i,
+ struct rela_dyn_info *di,
+ char *buf, size_t buf_sz)
+{
+ void *p = di->plt_got_data->d_buf + i;
+ u32 disp = get_x86_64_plt_disp(p);
+ struct rela_dyn vi, *vr;
+ const char *sym_name;
+ char *demangled;
+ GElf_Sym sym;
+
+ if (!di->sorted || !disp)
+ return false;
+
+ /* Compute target offset of the .plt.got entry */
+ vi.offset = shdr->sh_offset + di->plt_got_data->d_off + i + disp;
+
+ /* Find that offset in .rela.dyn (sorted by offset) */
+ vr = bsearch(&vi, di->sorted, di->nr_entries, sizeof(di->sorted[0]), cmp_offset);
+ if (!vr)
+ return false;
+
+ /* Get the associated symbol */
+ gelf_getsym(di->dynsym_data, vr->sym_idx, &sym);
+ sym_name = elf_sym__name(&sym, di->dynstr_data);
+ demangled = demangle_sym(di->dso, 0, sym_name);
+ if (demangled != NULL)
+ sym_name = demangled;
+
+ snprintf(buf, buf_sz, "%s@plt", sym_name);
+
+ free(demangled);
+
+ return *sym_name;
+}
+
static int dso__synthesize_plt_got_symbols(struct dso *dso, Elf *elf,
GElf_Ehdr *ehdr,
char *buf, size_t buf_sz)
{
+ struct rela_dyn_info di = { .dso = dso };
struct symbol *sym;
GElf_Shdr shdr;
Elf_Scn *scn;
+ int err = -1;
size_t i;

scn = elf_section_by_name(elf, ehdr, &shdr, ".plt.got", NULL);
if (!scn || !shdr.sh_entsize)
return 0;

+ if (ehdr->e_machine == EM_X86_64)
+ get_rela_dyn_info(elf, ehdr, &di, scn);
+
for (i = 0; i < shdr.sh_size; i += shdr.sh_entsize) {
- snprintf(buf, buf_sz, "offset_%#zx@plt", shdr.sh_offset + i);
+ if (!get_plt_got_name(&shdr, i, &di, buf, buf_sz))
+ snprintf(buf, buf_sz, "offset_%#zx@plt", shdr.sh_offset + i);
sym = symbol__new(shdr.sh_offset + i, shdr.sh_entsize, STB_GLOBAL, STT_FUNC, buf);
if (!sym)
- return -1;
+ goto out;
symbols__insert(&dso->symbols, sym);
}
-
- return 0;
+ err = 0;
+out:
+ exit_rela_dyn(&di);
+ return err;
}

/*
--
2.34.1


2023-01-30 17:36:25

by Namhyung Kim

[permalink] [raw]
Subject: Re: [PATCH 2/9] perf symbols: Add support for x86 .plt.sec

Hi Adrian,

On Fri, Jan 27, 2023 at 9:02 AM Adrian Hunter <[email protected]> wrote:
>
> The section .plt.sec was originally added for MPX and was first called
> .plt.bnd. While MPX has been deprecated, .plt.sec is now also used for IBT.
> On x86_64, IBT seems to be enabled by default, but can be switched off
> using gcc option -fcf-protection=none. On 32-bit, option -z ibt will
> enable IBT.
>
> With .plt.sec, calls are made into .plt.sec instead of .plt, so it
> makes more sense to put the symbols there instead of .plt. A notable
> difference is that .plt.sec does not have a header entry.
>
> For x86, when synthesizing symbols for plt, use offset and entry size of
> .plt.sec instead of .plt when there is a .plt.sec section.
>
> Example on Ubuntu 22.04 gcc 11.3:
>
> Before:
>
> $ cat tstpltlib.c
> void fn1(void) {}
> void fn2(void) {}
> void fn3(void) {}
> void fn4(void) {}
> $ cat tstplt.c
> void fn1(void);
> void fn2(void);
> void fn3(void);
> void fn4(void);
>
> int main()
> {
> fn4();
> fn1();
> fn2();
> fn3();
> 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 -Wall -Wextra -shared -o libtstpltlib.so tstpltlib.c
> $ gcc -Wall -Wextra -o tstplt tstplt.c -L . -ltstpltlib -Wl,-rpath=$(pwd)
> $ readelf -SW tstplt | grep 'plt\|Name'
> [Nr] Name Type Address Off Size ES Flg Lk Inf Al
> [11] .rela.plt RELA 0000000000000698 000698 000060 18 AI 6 24 8
> [13] .plt PROGBITS 0000000000001020 001020 000050 10 AX 0 0 16
> [14] .plt.got PROGBITS 0000000000001070 001070 000010 10 AX 0 0 16
> [15] .plt.sec PROGBITS 0000000000001080 001080 000040 10 AX 0 0 16

On my machine, it's not enabled by default. And it doesn't create .plt.sec
even if I pass -fcf-protection=full option.

$ gcc --version
gcc (Debian 12.2.0-10) 12.2.0
Copyright (C) 2022 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 -Wall -Wextra -shared -fcf-protection=full -o libtstplt.so tstpltlib.c
$ gcc -Wall -Wextra -fcf-protection=full -o tstplt tstplt.c -L.
-ltstpltlib -Wl,-rpath,$(pwd)
$ readelf -SW tstplt | grep 'plt\|Name'
[Nr] Name Type Address Off Size
ES Flg Lk Inf Al
[11] .rela.plt RELA 0000000000000688 000688
000060 18 AI 6 24 8
[13] .plt PROGBITS 0000000000001020 001020
000050 10 AX 0 0 16
[14] .plt.got PROGBITS 0000000000001070 001070
000008 08 AX 0 0 8
[24] .got.plt PROGBITS 0000000000003fe8 002fe8
000038 08 WA 0 0 8

Thanks,
Namhyung

2023-01-30 18:35:15

by Adrian Hunter

[permalink] [raw]
Subject: Re: [PATCH 2/9] perf symbols: Add support for x86 .plt.sec

On 30/01/23 19:34, Namhyung Kim wrote:
> Hi Adrian,
>
> On Fri, Jan 27, 2023 at 9:02 AM Adrian Hunter <[email protected]> wrote:
>>
>> The section .plt.sec was originally added for MPX and was first called
>> .plt.bnd. While MPX has been deprecated, .plt.sec is now also used for IBT.
>> On x86_64, IBT seems to be enabled by default, but can be switched off
>> using gcc option -fcf-protection=none. On 32-bit, option -z ibt will
>> enable IBT.
>>
>> With .plt.sec, calls are made into .plt.sec instead of .plt, so it
>> makes more sense to put the symbols there instead of .plt. A notable
>> difference is that .plt.sec does not have a header entry.
>>
>> For x86, when synthesizing symbols for plt, use offset and entry size of
>> .plt.sec instead of .plt when there is a .plt.sec section.
>>
>> Example on Ubuntu 22.04 gcc 11.3:
>>
>> Before:
>>
>> $ cat tstpltlib.c
>> void fn1(void) {}
>> void fn2(void) {}
>> void fn3(void) {}
>> void fn4(void) {}
>> $ cat tstplt.c
>> void fn1(void);
>> void fn2(void);
>> void fn3(void);
>> void fn4(void);
>>
>> int main()
>> {
>> fn4();
>> fn1();
>> fn2();
>> fn3();
>> 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 -Wall -Wextra -shared -o libtstpltlib.so tstpltlib.c
>> $ gcc -Wall -Wextra -o tstplt tstplt.c -L . -ltstpltlib -Wl,-rpath=$(pwd)
>> $ readelf -SW tstplt | grep 'plt\|Name'
>> [Nr] Name Type Address Off Size ES Flg Lk Inf Al
>> [11] .rela.plt RELA 0000000000000698 000698 000060 18 AI 6 24 8
>> [13] .plt PROGBITS 0000000000001020 001020 000050 10 AX 0 0 16
>> [14] .plt.got PROGBITS 0000000000001070 001070 000010 10 AX 0 0 16
>> [15] .plt.sec PROGBITS 0000000000001080 001080 000040 10 AX 0 0 16
>
> On my machine, it's not enabled by default. And it doesn't create .plt.sec
> even if I pass -fcf-protection=full option.
>
> $ gcc --version
> gcc (Debian 12.2.0-10) 12.2.0
> Copyright (C) 2022 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 -Wall -Wextra -shared -fcf-protection=full -o libtstplt.so tstpltlib.c
> $ gcc -Wall -Wextra -fcf-protection=full -o tstplt tstplt.c -L.
> -ltstpltlib -Wl,-rpath,$(pwd)
> $ readelf -SW tstplt | grep 'plt\|Name'
> [Nr] Name Type Address Off Size
> ES Flg Lk Inf Al
> [11] .rela.plt RELA 0000000000000688 000688
> 000060 18 AI 6 24 8
> [13] .plt PROGBITS 0000000000001020 001020
> 000050 10 AX 0 0 16
> [14] .plt.got PROGBITS 0000000000001070 001070
> 000008 08 AX 0 0 8
> [24] .got.plt PROGBITS 0000000000003fe8 002fe8
> 000038 08 WA 0 0 8

That is interesting. What does it say with -v i.e.

gcc -v -Wall -Wextra -o tstplt tstplt.c -L . -ltstpltlib -Wl,-rpath=$(pwd)

And what is the distribution?



2023-01-30 22:23:17

by Namhyung Kim

[permalink] [raw]
Subject: Re: [PATCH 2/9] perf symbols: Add support for x86 .plt.sec

On Mon, Jan 30, 2023 at 10:35 AM Adrian Hunter <[email protected]> wrote:
>
> On 30/01/23 19:34, Namhyung Kim wrote:
> > Hi Adrian,
> >
> > On Fri, Jan 27, 2023 at 9:02 AM Adrian Hunter <[email protected]> wrote:
> >>
> >> The section .plt.sec was originally added for MPX and was first called
> >> .plt.bnd. While MPX has been deprecated, .plt.sec is now also used for IBT.
> >> On x86_64, IBT seems to be enabled by default, but can be switched off
> >> using gcc option -fcf-protection=none. On 32-bit, option -z ibt will
> >> enable IBT.
> >>
> >> With .plt.sec, calls are made into .plt.sec instead of .plt, so it
> >> makes more sense to put the symbols there instead of .plt. A notable
> >> difference is that .plt.sec does not have a header entry.
> >>
> >> For x86, when synthesizing symbols for plt, use offset and entry size of
> >> .plt.sec instead of .plt when there is a .plt.sec section.
> >>
> >> Example on Ubuntu 22.04 gcc 11.3:
> >>
> >> Before:
> >>
> >> $ cat tstpltlib.c
> >> void fn1(void) {}
> >> void fn2(void) {}
> >> void fn3(void) {}
> >> void fn4(void) {}
> >> $ cat tstplt.c
> >> void fn1(void);
> >> void fn2(void);
> >> void fn3(void);
> >> void fn4(void);
> >>
> >> int main()
> >> {
> >> fn4();
> >> fn1();
> >> fn2();
> >> fn3();
> >> 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 -Wall -Wextra -shared -o libtstpltlib.so tstpltlib.c
> >> $ gcc -Wall -Wextra -o tstplt tstplt.c -L . -ltstpltlib -Wl,-rpath=$(pwd)
> >> $ readelf -SW tstplt | grep 'plt\|Name'
> >> [Nr] Name Type Address Off Size ES Flg Lk Inf Al
> >> [11] .rela.plt RELA 0000000000000698 000698 000060 18 AI 6 24 8
> >> [13] .plt PROGBITS 0000000000001020 001020 000050 10 AX 0 0 16
> >> [14] .plt.got PROGBITS 0000000000001070 001070 000010 10 AX 0 0 16
> >> [15] .plt.sec PROGBITS 0000000000001080 001080 000040 10 AX 0 0 16
> >
> > On my machine, it's not enabled by default. And it doesn't create .plt.sec
> > even if I pass -fcf-protection=full option.
> >
> > $ gcc --version
> > gcc (Debian 12.2.0-10) 12.2.0
> > Copyright (C) 2022 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 -Wall -Wextra -shared -fcf-protection=full -o libtstplt.so tstpltlib.c
> > $ gcc -Wall -Wextra -fcf-protection=full -o tstplt tstplt.c -L.
> > -ltstpltlib -Wl,-rpath,$(pwd)
> > $ readelf -SW tstplt | grep 'plt\|Name'
> > [Nr] Name Type Address Off Size
> > ES Flg Lk Inf Al
> > [11] .rela.plt RELA 0000000000000688 000688
> > 000060 18 AI 6 24 8
> > [13] .plt PROGBITS 0000000000001020 001020
> > 000050 10 AX 0 0 16
> > [14] .plt.got PROGBITS 0000000000001070 001070
> > 000008 08 AX 0 0 8
> > [24] .got.plt PROGBITS 0000000000003fe8 002fe8
> > 000038 08 WA 0 0 8
>
> That is interesting. What does it say with -v i.e.
>
> gcc -v -Wall -Wextra -o tstplt tstplt.c -L . -ltstpltlib -Wl,-rpath=$(pwd)

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian
12.2.0-10' --with-bugurl=file:///usr/share/doc/gcc-12/README.Bugs
--enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2
--prefix=/usr --with-gcc-major-version-only --program-suffix=-12
--program-prefix=x86_64-linux-gnu- --enable-shared
--enable-linker-build-id --libexecdir=/usr/lib
--without-included-gettext --enable-threads=posix --libdir=/usr/lib
--enable-nls --enable-clocale=gnu --enable-libstdcxx-debug
--enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new
--enable-gnu-unique-object --disable-vtable-verify --enable-plugin
--enable-default-pie --with-system-zlib
--enable-libphobos-checking=release --with-target-system-zlib=auto
--enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet
--with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32
--enable-multilib --with-tune=generic
--enable-offload-targets=nvptx-none=/build/gcc-12-hWCYKv/gcc-12-12.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-12-hWCYKv/gcc-12-12.2.0/debian/tmp-gcn/usr
--enable-offload-defaulted --without-cuda-driver
--enable-checking=release --build=x86_64-linux-gnu
--host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 12.2.0 (Debian 12.2.0-10)
COLLECT_GCC_OPTIONS='-Wall' '-Wextra' '-fcf-protection=full' '-v' '-o'
'tstplt' '-L.' '-mtune=generic' '-march=x86-64' '-dumpdir' 'tstplt-'
/usr/lib/gcc/x86_64-linux-gnu/12/cc1 -quiet -v -imultiarch
x86_64-linux-gnu tstplt.c -quiet -dumpdir tstplt- -dumpbase tstplt.c
-dumpbase-ext .c -mtune=generic -march=x86-64 -Wall -Wextra -version
-fcf-protection=full -fasynchronous-unwind-tables -o /tmp/ccKPWeTD.s
GNU C17 (Debian 12.2.0-10) version 12.2.0 (x86_64-linux-gnu)
compiled by GNU C version 12.2.0, GMP version 6.2.1, MPFR version
4.1.0, MPC version 1.2.1, isl version isl-0.25-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/12/include-fixed"
ignoring nonexistent directory
"/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/12/include
/usr/local/include
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
GNU C17 (Debian 12.2.0-10) version 12.2.0 (x86_64-linux-gnu)
compiled by GNU C version 12.2.0, GMP version 6.2.1, MPFR version
4.1.0, MPC version 1.2.1, isl version isl-0.25-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 0bf64a455b69fb48d1b44a013a099136
COLLECT_GCC_OPTIONS='-Wall' '-Wextra' '-fcf-protection=full' '-v' '-o'
'tstplt' '-L.' '-mtune=generic' '-march=x86-64' '-dumpdir' 'tstplt-'
as -v --64 -o /tmp/cc0IMyNr.o /tmp/ccKPWeTD.s
GNU assembler version 2.39.50 (x86_64-linux-gnu) using BFD version
(GNU Binutils for Debian) 2.39.50.20221208
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-Wall' '-Wextra' '-fcf-protection=full' '-v' '-o'
'tstplt' '-L.' '-mtune=generic' '-march=x86-64' '-dumpdir' 'tstplt.'
/usr/lib/gcc/x86_64-linux-gnu/12/collect2 -plugin
/usr/lib/gcc/x86_64-linux-gnu/12/liblto_plugin.so
-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper
-plugin-opt=-fresolution=/tmp/ccU2c2jz.res
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s
-plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc
-plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m
elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker
/lib64/ld-linux-x86-64.so.2 -pie -o tstplt
/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o -L.
-L/usr/lib/gcc/x86_64-linux-gnu/12
-L/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu
-L/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib
-L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu
-L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/12/../../..
/tmp/cc0IMyNr.o -ltstpltlib -rpath=/home/namhyung/tmp/plt-test -lgcc
--push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state
--as-needed -lgcc_s --pop-state
/usr/lib/gcc/x86_64-linux-gnu/12/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-Wall' '-Wextra' '-fcf-protection=full' '-v' '-o'
'tstplt' '-L.' '-mtune=generic' '-march=x86-64' '-dumpdir' 'tstplt.'


>
> And what is the distribution?

It's a Debian (Testing) with some customization.

Thanks,
Namhyung

2023-01-30 23:26:09

by Namhyung Kim

[permalink] [raw]
Subject: Re: [PATCH 9/9] perf symbols: Get symbols for .plt.got for x86-64

On Fri, Jan 27, 2023 at 07:02:22PM +0200, Adrian Hunter wrote:
> For x86_64, determine a symbol for .plt.got entries. That requires
> computing the target offset and finding that in .rela.dyn, which in
> turn means .rela.dyn needs to be sorted by offset.
>
> Example:
>
> In this example, the GNU C Library is using .plt.got for malloc and
> free.
>
> Before:
>
> $ 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.
> $ perf record -e intel_pt//u uname
> Linux
> [ perf record: Woken up 1 times to write data ]
> [ perf record: Captured and wrote 0.027 MB perf.data ]
> $ perf script --itrace=be --ns -F-event,+addr,-period,-comm,-tid,-cpu > /tmp/cmp1.txt
>
> After:
>
> $ perf script --itrace=be --ns -F-event,+addr,-period,-comm,-tid,-cpu > /tmp/cmp2.txt
> $ diff /tmp/cmp1.txt /tmp/cmp2.txt | head -12
> 15509,15510c15509,15510
> < 27046.755390907: 7f0b2943e3ab _nl_normalize_codeset+0x5b (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b29428380 offset_0x28380@plt+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
> < 27046.755390907: 7f0b29428384 offset_0x28380@plt+0x4 (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b294a5120 malloc+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
> ---
> > 27046.755390907: 7f0b2943e3ab _nl_normalize_codeset+0x5b (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b29428380 malloc@plt+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
> > 27046.755390907: 7f0b29428384 malloc@plt+0x4 (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b294a5120 malloc+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
> 15821,15822c15821,15822
> < 27046.755394865: 7f0b2943850c _nl_load_locale_from_archive+0x5bc (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b29428370 offset_0x28370@plt+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
> < 27046.755394865: 7f0b29428374 offset_0x28370@plt+0x4 (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b294a5460 cfree@GLIBC_2.2.5+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
> ---
> > 27046.755394865: 7f0b2943850c _nl_load_locale_from_archive+0x5bc (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b29428370 free@plt+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
> > 27046.755394865: 7f0b29428374 free@plt+0x4 (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b294a5460 cfree@GLIBC_2.2.5+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
>
> Signed-off-by: Adrian Hunter <[email protected]>
> ---
> tools/perf/util/symbol-elf.c | 158 ++++++++++++++++++++++++++++++++++-
> 1 file changed, 154 insertions(+), 4 deletions(-)
>
> diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
> index 254116d40e59..4fc8e7fc10f4 100644
> --- a/tools/perf/util/symbol-elf.c
> +++ b/tools/perf/util/symbol-elf.c
> @@ -466,28 +466,178 @@ static bool machine_is_x86(GElf_Half e_machine)
> return e_machine == EM_386 || e_machine == EM_X86_64;
> }
>
> +struct rela_dyn {
> + GElf_Addr offset;
> + u32 sym_idx;
> +};
> +
> +struct rela_dyn_info {
> + struct dso *dso;
> + Elf_Data *plt_got_data;
> + u32 nr_entries;
> + struct rela_dyn *sorted;
> + Elf_Data *dynsym_data;
> + Elf_Data *dynstr_data;
> + Elf_Data *rela_dyn_data;
> +};
> +
> +static void exit_rela_dyn(struct rela_dyn_info *di)
> +{
> + free(di->sorted);
> +}
> +
> +static int cmp_offset(const void *a, const void *b)
> +{
> + const struct rela_dyn *va = a;
> + const struct rela_dyn *vb = b;
> +
> + return va->offset < vb->offset ? -1 : (va->offset > vb->offset ? 1 : 0);
> +}
> +
> +static int sort_rela_dyn(struct rela_dyn_info *di)
> +{
> + u32 i, n;
> +
> + di->sorted = calloc(di->nr_entries, sizeof(di->sorted[0]));
> + if (!di->sorted)
> + return -1;
> +
> + /* Get data for sorting: the offset and symbol index */
> + for (i = 0, n = 0; i < di->nr_entries; i++) {
> + GElf_Rela rela;
> + u32 sym_idx;
> +
> + gelf_getrela(di->rela_dyn_data, i, &rela);
> + sym_idx = GELF_R_SYM(rela.r_info);
> + if (sym_idx) {
> + di->sorted[n].sym_idx = sym_idx;
> + di->sorted[n].offset = rela.r_offset;
> + n += 1;
> + }
> + }
> +
> + /* Sort by offset */
> + di->nr_entries = n;
> + qsort(di->sorted, n, sizeof(di->sorted[0]), cmp_offset);
> +
> + return 0;
> +}
> +
> +static void get_rela_dyn_info(Elf *elf, GElf_Ehdr *ehdr, struct rela_dyn_info *di, Elf_Scn *scn)
> +{
> + GElf_Shdr rela_dyn_shdr;
> + GElf_Shdr shdr;
> +
> + di->plt_got_data = elf_getdata(scn, NULL);
> +
> + scn = elf_section_by_name(elf, ehdr, &rela_dyn_shdr, ".rela.dyn", NULL);
> + if (!scn || !rela_dyn_shdr.sh_link || !rela_dyn_shdr.sh_entsize)
> + return;
> +
> + di->nr_entries = rela_dyn_shdr.sh_size / rela_dyn_shdr.sh_entsize;
> + di->rela_dyn_data = elf_getdata(scn, NULL);
> +
> + scn = elf_getscn(elf, rela_dyn_shdr.sh_link);
> + if (!scn || !gelf_getshdr(scn, &shdr) || !shdr.sh_link)
> + return;
> +
> + di->dynsym_data = elf_getdata(scn, NULL);
> + di->dynstr_data = elf_getdata(elf_getscn(elf, shdr.sh_link), NULL);
> +
> + if (!di->plt_got_data || !di->dynstr_data || !di->dynsym_data || !di->rela_dyn_data)
> + return;
> +
> + /* Sort into offset order */
> + sort_rela_dyn(di);
> +}
> +
> +/* Get instruction displacement from a plt entry for x86_64 */
> +static u32 get_x86_64_plt_disp(const u8 *p)
> +{
> + u8 endbr64[] = {0xf3, 0x0f, 0x1e, 0xfa};
> + int n = 0;
> +
> + /* Skip endbr64 */
> + if (!memcmp(p, endbr64, sizeof(endbr64)))
> + n += sizeof(endbr64);
> + /* Skip bnd prefix */
> + if (p[n] == 0xf2)
> + n += 1;
> + /* jmp with 4-byte displacement */
> + if (p[n] == 0xff && p[n + 1] == 0x25) {
> + n += 2;
> + /* Also add offset from start of entry to end of instruction */
> + return n + 4 + le32toh(*(const u32 *)(p + n));
> + }
> + return 0;
> +}
> +
> +static bool get_plt_got_name(GElf_Shdr *shdr, size_t i,
> + struct rela_dyn_info *di,
> + char *buf, size_t buf_sz)
> +{
> + void *p = di->plt_got_data->d_buf + i;
> + u32 disp = get_x86_64_plt_disp(p);
> + struct rela_dyn vi, *vr;
> + const char *sym_name;
> + char *demangled;
> + GElf_Sym sym;
> +
> + if (!di->sorted || !disp)
> + return false;
> +
> + /* Compute target offset of the .plt.got entry */
> + vi.offset = shdr->sh_offset + di->plt_got_data->d_off + i + disp;
> +
> + /* Find that offset in .rela.dyn (sorted by offset) */
> + vr = bsearch(&vi, di->sorted, di->nr_entries, sizeof(di->sorted[0]), cmp_offset);
> + if (!vr)
> + return false;
> +
> + /* Get the associated symbol */
> + gelf_getsym(di->dynsym_data, vr->sym_idx, &sym);
> + sym_name = elf_sym__name(&sym, di->dynstr_data);
> + demangled = demangle_sym(di->dso, 0, sym_name);
> + if (demangled != NULL)
> + sym_name = demangled;
> +
> + snprintf(buf, buf_sz, "%s@plt", sym_name);
> +
> + free(demangled);
> +
> + return *sym_name;
> +}
> +
> static int dso__synthesize_plt_got_symbols(struct dso *dso, Elf *elf,
> GElf_Ehdr *ehdr,
> char *buf, size_t buf_sz)
> {
> + struct rela_dyn_info di = { .dso = dso };
> struct symbol *sym;
> GElf_Shdr shdr;
> Elf_Scn *scn;
> + int err = -1;
> size_t i;
>
> scn = elf_section_by_name(elf, ehdr, &shdr, ".plt.got", NULL);
> if (!scn || !shdr.sh_entsize)
> return 0;
>
> + if (ehdr->e_machine == EM_X86_64)
> + get_rela_dyn_info(elf, ehdr, &di, scn);

What about EM_386? Now I'm seeing segfaults on 32 bit test programs
with .plt.got section.

Thanks,
Namhyung


> +
> for (i = 0; i < shdr.sh_size; i += shdr.sh_entsize) {
> - snprintf(buf, buf_sz, "offset_%#zx@plt", shdr.sh_offset + i);
> + if (!get_plt_got_name(&shdr, i, &di, buf, buf_sz))
> + snprintf(buf, buf_sz, "offset_%#zx@plt", shdr.sh_offset + i);
> sym = symbol__new(shdr.sh_offset + i, shdr.sh_entsize, STB_GLOBAL, STT_FUNC, buf);
> if (!sym)
> - return -1;
> + goto out;
> symbols__insert(&dso->symbols, sym);
> }
> -
> - return 0;
> + err = 0;
> +out:
> + exit_rela_dyn(&di);
> + return err;
> }
>
> /*
> --
> 2.34.1
>
>

2023-01-31 10:14:45

by Adrian Hunter

[permalink] [raw]
Subject: Re: [PATCH 2/9] perf symbols: Add support for x86 .plt.sec

On 31/01/23 00:22, Namhyung Kim wrote:
> On Mon, Jan 30, 2023 at 10:35 AM Adrian Hunter <[email protected]> wrote:
>>
>> On 30/01/23 19:34, Namhyung Kim wrote:
>>> Hi Adrian,
>>>
>>> On Fri, Jan 27, 2023 at 9:02 AM Adrian Hunter <[email protected]> wrote:
>>>>
>>>> The section .plt.sec was originally added for MPX and was first called
>>>> .plt.bnd. While MPX has been deprecated, .plt.sec is now also used for IBT.
>>>> On x86_64, IBT seems to be enabled by default, but can be switched off
>>>> using gcc option -fcf-protection=none. On 32-bit, option -z ibt will
>>>> enable IBT.
>>>>
>>>> With .plt.sec, calls are made into .plt.sec instead of .plt, so it
>>>> makes more sense to put the symbols there instead of .plt. A notable
>>>> difference is that .plt.sec does not have a header entry.
>>>>
>>>> For x86, when synthesizing symbols for plt, use offset and entry size of
>>>> .plt.sec instead of .plt when there is a .plt.sec section.
>>>>
>>>> Example on Ubuntu 22.04 gcc 11.3:
>>>>
>>>> Before:
>>>>
>>>> $ cat tstpltlib.c
>>>> void fn1(void) {}
>>>> void fn2(void) {}
>>>> void fn3(void) {}
>>>> void fn4(void) {}
>>>> $ cat tstplt.c
>>>> void fn1(void);
>>>> void fn2(void);
>>>> void fn3(void);
>>>> void fn4(void);
>>>>
>>>> int main()
>>>> {
>>>> fn4();
>>>> fn1();
>>>> fn2();
>>>> fn3();
>>>> 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 -Wall -Wextra -shared -o libtstpltlib.so tstpltlib.c
>>>> $ gcc -Wall -Wextra -o tstplt tstplt.c -L . -ltstpltlib -Wl,-rpath=$(pwd)
>>>> $ readelf -SW tstplt | grep 'plt\|Name'
>>>> [Nr] Name Type Address Off Size ES Flg Lk Inf Al
>>>> [11] .rela.plt RELA 0000000000000698 000698 000060 18 AI 6 24 8
>>>> [13] .plt PROGBITS 0000000000001020 001020 000050 10 AX 0 0 16
>>>> [14] .plt.got PROGBITS 0000000000001070 001070 000010 10 AX 0 0 16
>>>> [15] .plt.sec PROGBITS 0000000000001080 001080 000040 10 AX 0 0 16
>>>
>>> On my machine, it's not enabled by default. And it doesn't create .plt.sec
>>> even if I pass -fcf-protection=full option.
>>>
>>> $ gcc --version
>>> gcc (Debian 12.2.0-10) 12.2.0
>>> Copyright (C) 2022 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 -Wall -Wextra -shared -fcf-protection=full -o libtstplt.so tstpltlib.c
>>> $ gcc -Wall -Wextra -fcf-protection=full -o tstplt tstplt.c -L.
>>> -ltstpltlib -Wl,-rpath,$(pwd)
>>> $ readelf -SW tstplt | grep 'plt\|Name'
>>> [Nr] Name Type Address Off Size
>>> ES Flg Lk Inf Al
>>> [11] .rela.plt RELA 0000000000000688 000688
>>> 000060 18 AI 6 24 8
>>> [13] .plt PROGBITS 0000000000001020 001020
>>> 000050 10 AX 0 0 16
>>> [14] .plt.got PROGBITS 0000000000001070 001070
>>> 000008 08 AX 0 0 8
>>> [24] .got.plt PROGBITS 0000000000003fe8 002fe8
>>> 000038 08 WA 0 0 8
>>
>> That is interesting. What does it say with -v i.e.
>>
>> gcc -v -Wall -Wextra -o tstplt tstplt.c -L . -ltstpltlib -Wl,-rpath=$(pwd)
>
> Using built-in specs.
> COLLECT_GCC=gcc
> COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper
> OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
> OFFLOAD_TARGET_DEFAULT=1
> Target: x86_64-linux-gnu
> Configured with: ../src/configure -v --with-pkgversion='Debian
> 12.2.0-10' --with-bugurl=file:///usr/share/doc/gcc-12/README.Bugs
> --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2
> --prefix=/usr --with-gcc-major-version-only --program-suffix=-12
> --program-prefix=x86_64-linux-gnu- --enable-shared
> --enable-linker-build-id --libexecdir=/usr/lib
> --without-included-gettext --enable-threads=posix --libdir=/usr/lib
> --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug
> --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new
> --enable-gnu-unique-object --disable-vtable-verify --enable-plugin
> --enable-default-pie --with-system-zlib
> --enable-libphobos-checking=release --with-target-system-zlib=auto
> --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet
> --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32
> --enable-multilib --with-tune=generic
> --enable-offload-targets=nvptx-none=/build/gcc-12-hWCYKv/gcc-12-12.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-12-hWCYKv/gcc-12-12.2.0/debian/tmp-gcn/usr
> --enable-offload-defaulted --without-cuda-driver
> --enable-checking=release --build=x86_64-linux-gnu
> --host=x86_64-linux-gnu --target=x86_64-linux-gnu
> Thread model: posix
> Supported LTO compression algorithms: zlib zstd
> gcc version 12.2.0 (Debian 12.2.0-10)
> COLLECT_GCC_OPTIONS='-Wall' '-Wextra' '-fcf-protection=full' '-v' '-o'
> 'tstplt' '-L.' '-mtune=generic' '-march=x86-64' '-dumpdir' 'tstplt-'
> /usr/lib/gcc/x86_64-linux-gnu/12/cc1 -quiet -v -imultiarch
> x86_64-linux-gnu tstplt.c -quiet -dumpdir tstplt- -dumpbase tstplt.c
> -dumpbase-ext .c -mtune=generic -march=x86-64 -Wall -Wextra -version
> -fcf-protection=full -fasynchronous-unwind-tables -o /tmp/ccKPWeTD.s
> GNU C17 (Debian 12.2.0-10) version 12.2.0 (x86_64-linux-gnu)
> compiled by GNU C version 12.2.0, GMP version 6.2.1, MPFR version
> 4.1.0, MPC version 1.2.1, isl version isl-0.25-GMP
>
> GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
> ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
> ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/12/include-fixed"
> ignoring nonexistent directory
> "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include"
> #include "..." search starts here:
> #include <...> search starts here:
> /usr/lib/gcc/x86_64-linux-gnu/12/include
> /usr/local/include
> /usr/include/x86_64-linux-gnu
> /usr/include
> End of search list.
> GNU C17 (Debian 12.2.0-10) version 12.2.0 (x86_64-linux-gnu)
> compiled by GNU C version 12.2.0, GMP version 6.2.1, MPFR version
> 4.1.0, MPC version 1.2.1, isl version isl-0.25-GMP
>
> GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
> Compiler executable checksum: 0bf64a455b69fb48d1b44a013a099136
> COLLECT_GCC_OPTIONS='-Wall' '-Wextra' '-fcf-protection=full' '-v' '-o'
> 'tstplt' '-L.' '-mtune=generic' '-march=x86-64' '-dumpdir' 'tstplt-'
> as -v --64 -o /tmp/cc0IMyNr.o /tmp/ccKPWeTD.s
> GNU assembler version 2.39.50 (x86_64-linux-gnu) using BFD version
> (GNU Binutils for Debian) 2.39.50.20221208
> COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/
> LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../:/lib/:/usr/lib/
> COLLECT_GCC_OPTIONS='-Wall' '-Wextra' '-fcf-protection=full' '-v' '-o'
> 'tstplt' '-L.' '-mtune=generic' '-march=x86-64' '-dumpdir' 'tstplt.'
> /usr/lib/gcc/x86_64-linux-gnu/12/collect2 -plugin
> /usr/lib/gcc/x86_64-linux-gnu/12/liblto_plugin.so
> -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper
> -plugin-opt=-fresolution=/tmp/ccU2c2jz.res
> -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s
> -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc
> -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m
> elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker
> /lib64/ld-linux-x86-64.so.2 -pie -o tstplt
> /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/Scrt1.o
> /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crti.o
> /usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o -L.
> -L/usr/lib/gcc/x86_64-linux-gnu/12
> -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu
> -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib
> -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu
> -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/12/../../..
> /tmp/cc0IMyNr.o -ltstpltlib -rpath=/home/namhyung/tmp/plt-test -lgcc
> --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state
> --as-needed -lgcc_s --pop-state
> /usr/lib/gcc/x86_64-linux-gnu/12/crtendS.o
> /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crtn.o
> COLLECT_GCC_OPTIONS='-Wall' '-Wextra' '-fcf-protection=full' '-v' '-o'
> 'tstplt' '-L.' '-mtune=generic' '-march=x86-64' '-dumpdir' 'tstplt.'
>
>
>>
>> And what is the distribution?
>
> It's a Debian (Testing) with some customization.

Seems to need also options -z ibt and/or -z ibtplt

I will send a V2 with updated examples.


2023-01-31 10:18:12

by Adrian Hunter

[permalink] [raw]
Subject: Re: [PATCH 9/9] perf symbols: Get symbols for .plt.got for x86-64

On 31/01/23 01:26, Namhyung Kim wrote:
> On Fri, Jan 27, 2023 at 07:02:22PM +0200, Adrian Hunter wrote:
>> For x86_64, determine a symbol for .plt.got entries. That requires
>> computing the target offset and finding that in .rela.dyn, which in
>> turn means .rela.dyn needs to be sorted by offset.
>>
>> Example:
>>
>> In this example, the GNU C Library is using .plt.got for malloc and
>> free.
>>
>> Before:
>>
>> $ 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.
>> $ perf record -e intel_pt//u uname
>> Linux
>> [ perf record: Woken up 1 times to write data ]
>> [ perf record: Captured and wrote 0.027 MB perf.data ]
>> $ perf script --itrace=be --ns -F-event,+addr,-period,-comm,-tid,-cpu > /tmp/cmp1.txt
>>
>> After:
>>
>> $ perf script --itrace=be --ns -F-event,+addr,-period,-comm,-tid,-cpu > /tmp/cmp2.txt
>> $ diff /tmp/cmp1.txt /tmp/cmp2.txt | head -12
>> 15509,15510c15509,15510
>> < 27046.755390907: 7f0b2943e3ab _nl_normalize_codeset+0x5b (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b29428380 offset_0x28380@plt+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
>> < 27046.755390907: 7f0b29428384 offset_0x28380@plt+0x4 (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b294a5120 malloc+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
>> ---
>> > 27046.755390907: 7f0b2943e3ab _nl_normalize_codeset+0x5b (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b29428380 malloc@plt+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
>> > 27046.755390907: 7f0b29428384 malloc@plt+0x4 (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b294a5120 malloc+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
>> 15821,15822c15821,15822
>> < 27046.755394865: 7f0b2943850c _nl_load_locale_from_archive+0x5bc (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b29428370 offset_0x28370@plt+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
>> < 27046.755394865: 7f0b29428374 offset_0x28370@plt+0x4 (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b294a5460 cfree@GLIBC_2.2.5+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
>> ---
>> > 27046.755394865: 7f0b2943850c _nl_load_locale_from_archive+0x5bc (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b29428370 free@plt+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
>> > 27046.755394865: 7f0b29428374 free@plt+0x4 (/usr/lib/x86_64-linux-gnu/libc.so.6) => 7f0b294a5460 cfree@GLIBC_2.2.5+0x0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
>>
>> Signed-off-by: Adrian Hunter <[email protected]>
>> ---
>> tools/perf/util/symbol-elf.c | 158 ++++++++++++++++++++++++++++++++++-
>> 1 file changed, 154 insertions(+), 4 deletions(-)
>>
>> diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
>> index 254116d40e59..4fc8e7fc10f4 100644
>> --- a/tools/perf/util/symbol-elf.c
>> +++ b/tools/perf/util/symbol-elf.c
>> @@ -466,28 +466,178 @@ static bool machine_is_x86(GElf_Half e_machine)
>> return e_machine == EM_386 || e_machine == EM_X86_64;
>> }
>>
>> +struct rela_dyn {
>> + GElf_Addr offset;
>> + u32 sym_idx;
>> +};
>> +
>> +struct rela_dyn_info {
>> + struct dso *dso;
>> + Elf_Data *plt_got_data;
>> + u32 nr_entries;
>> + struct rela_dyn *sorted;
>> + Elf_Data *dynsym_data;
>> + Elf_Data *dynstr_data;
>> + Elf_Data *rela_dyn_data;
>> +};
>> +
>> +static void exit_rela_dyn(struct rela_dyn_info *di)
>> +{
>> + free(di->sorted);
>> +}
>> +
>> +static int cmp_offset(const void *a, const void *b)
>> +{
>> + const struct rela_dyn *va = a;
>> + const struct rela_dyn *vb = b;
>> +
>> + return va->offset < vb->offset ? -1 : (va->offset > vb->offset ? 1 : 0);
>> +}
>> +
>> +static int sort_rela_dyn(struct rela_dyn_info *di)
>> +{
>> + u32 i, n;
>> +
>> + di->sorted = calloc(di->nr_entries, sizeof(di->sorted[0]));
>> + if (!di->sorted)
>> + return -1;
>> +
>> + /* Get data for sorting: the offset and symbol index */
>> + for (i = 0, n = 0; i < di->nr_entries; i++) {
>> + GElf_Rela rela;
>> + u32 sym_idx;
>> +
>> + gelf_getrela(di->rela_dyn_data, i, &rela);
>> + sym_idx = GELF_R_SYM(rela.r_info);
>> + if (sym_idx) {
>> + di->sorted[n].sym_idx = sym_idx;
>> + di->sorted[n].offset = rela.r_offset;
>> + n += 1;
>> + }
>> + }
>> +
>> + /* Sort by offset */
>> + di->nr_entries = n;
>> + qsort(di->sorted, n, sizeof(di->sorted[0]), cmp_offset);
>> +
>> + return 0;
>> +}
>> +
>> +static void get_rela_dyn_info(Elf *elf, GElf_Ehdr *ehdr, struct rela_dyn_info *di, Elf_Scn *scn)
>> +{
>> + GElf_Shdr rela_dyn_shdr;
>> + GElf_Shdr shdr;
>> +
>> + di->plt_got_data = elf_getdata(scn, NULL);
>> +
>> + scn = elf_section_by_name(elf, ehdr, &rela_dyn_shdr, ".rela.dyn", NULL);
>> + if (!scn || !rela_dyn_shdr.sh_link || !rela_dyn_shdr.sh_entsize)
>> + return;
>> +
>> + di->nr_entries = rela_dyn_shdr.sh_size / rela_dyn_shdr.sh_entsize;
>> + di->rela_dyn_data = elf_getdata(scn, NULL);
>> +
>> + scn = elf_getscn(elf, rela_dyn_shdr.sh_link);
>> + if (!scn || !gelf_getshdr(scn, &shdr) || !shdr.sh_link)
>> + return;
>> +
>> + di->dynsym_data = elf_getdata(scn, NULL);
>> + di->dynstr_data = elf_getdata(elf_getscn(elf, shdr.sh_link), NULL);
>> +
>> + if (!di->plt_got_data || !di->dynstr_data || !di->dynsym_data || !di->rela_dyn_data)
>> + return;
>> +
>> + /* Sort into offset order */
>> + sort_rela_dyn(di);
>> +}
>> +
>> +/* Get instruction displacement from a plt entry for x86_64 */
>> +static u32 get_x86_64_plt_disp(const u8 *p)
>> +{
>> + u8 endbr64[] = {0xf3, 0x0f, 0x1e, 0xfa};
>> + int n = 0;
>> +
>> + /* Skip endbr64 */
>> + if (!memcmp(p, endbr64, sizeof(endbr64)))
>> + n += sizeof(endbr64);
>> + /* Skip bnd prefix */
>> + if (p[n] == 0xf2)
>> + n += 1;
>> + /* jmp with 4-byte displacement */
>> + if (p[n] == 0xff && p[n + 1] == 0x25) {
>> + n += 2;
>> + /* Also add offset from start of entry to end of instruction */
>> + return n + 4 + le32toh(*(const u32 *)(p + n));
>> + }
>> + return 0;
>> +}
>> +
>> +static bool get_plt_got_name(GElf_Shdr *shdr, size_t i,
>> + struct rela_dyn_info *di,
>> + char *buf, size_t buf_sz)
>> +{
>> + void *p = di->plt_got_data->d_buf + i;
>> + u32 disp = get_x86_64_plt_disp(p);
>> + struct rela_dyn vi, *vr;
>> + const char *sym_name;
>> + char *demangled;
>> + GElf_Sym sym;
>> +
>> + if (!di->sorted || !disp)
>> + return false;
>> +
>> + /* Compute target offset of the .plt.got entry */
>> + vi.offset = shdr->sh_offset + di->plt_got_data->d_off + i + disp;
>> +
>> + /* Find that offset in .rela.dyn (sorted by offset) */
>> + vr = bsearch(&vi, di->sorted, di->nr_entries, sizeof(di->sorted[0]), cmp_offset);
>> + if (!vr)
>> + return false;
>> +
>> + /* Get the associated symbol */
>> + gelf_getsym(di->dynsym_data, vr->sym_idx, &sym);
>> + sym_name = elf_sym__name(&sym, di->dynstr_data);
>> + demangled = demangle_sym(di->dso, 0, sym_name);
>> + if (demangled != NULL)
>> + sym_name = demangled;
>> +
>> + snprintf(buf, buf_sz, "%s@plt", sym_name);
>> +
>> + free(demangled);
>> +
>> + return *sym_name;
>> +}
>> +
>> static int dso__synthesize_plt_got_symbols(struct dso *dso, Elf *elf,
>> GElf_Ehdr *ehdr,
>> char *buf, size_t buf_sz)
>> {
>> + struct rela_dyn_info di = { .dso = dso };
>> struct symbol *sym;
>> GElf_Shdr shdr;
>> Elf_Scn *scn;
>> + int err = -1;
>> size_t i;
>>
>> scn = elf_section_by_name(elf, ehdr, &shdr, ".plt.got", NULL);
>> if (!scn || !shdr.sh_entsize)
>> return 0;
>>
>> + if (ehdr->e_machine == EM_X86_64)
>> + get_rela_dyn_info(elf, ehdr, &di, scn);
>
> What about EM_386? Now I'm seeing segfaults on 32 bit test programs
> with .plt.got section.

Thanks for looking at this.

Looks like a late change in get_plt_got_name() did not
get tested for 32-bit. It should be checking di->sorted
first. EM_386 is not supported by this logic.

I will send a V2

>
> Thanks,
> Namhyung
>
>
>> +
>> for (i = 0; i < shdr.sh_size; i += shdr.sh_entsize) {
>> - snprintf(buf, buf_sz, "offset_%#zx@plt", shdr.sh_offset + i);
>> + if (!get_plt_got_name(&shdr, i, &di, buf, buf_sz))
>> + snprintf(buf, buf_sz, "offset_%#zx@plt", shdr.sh_offset + i);
>> sym = symbol__new(shdr.sh_offset + i, shdr.sh_entsize, STB_GLOBAL, STT_FUNC, buf);
>> if (!sym)
>> - return -1;
>> + goto out;
>> symbols__insert(&dso->symbols, sym);
>> }
>> -
>> - return 0;
>> + err = 0;
>> +out:
>> + exit_rela_dyn(&di);
>> + return err;
>> }
>>
>> /*
>> --
>> 2.34.1
>>
>>