All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/9] perf symbols: Improve dso__synthesize_plt_symbols() for x86
@ 2023-01-27 17:02 Adrian Hunter
  2023-01-27 17:02 ` [PATCH 1/9] perf symbols: Correct plt entry sizes " Adrian Hunter
                   ` (8 more replies)
  0 siblings, 9 replies; 16+ messages in thread
From: Adrian Hunter @ 2023-01-27 17:02 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, linux-kernel, linux-perf-users

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

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 1/9] perf symbols: Correct plt entry sizes for x86
  2023-01-27 17:02 [PATCH 0/9] perf symbols: Improve dso__synthesize_plt_symbols() for x86 Adrian Hunter
@ 2023-01-27 17:02 ` Adrian Hunter
  2023-01-27 17:02 ` [PATCH 2/9] perf symbols: Add support for x86 .plt.sec Adrian Hunter
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Adrian Hunter @ 2023-01-27 17:02 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, linux-kernel, linux-perf-users

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 <adrian.hunter@intel.com>
---
 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


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 2/9] perf symbols: Add support for x86 .plt.sec
  2023-01-27 17:02 [PATCH 0/9] perf symbols: Improve dso__synthesize_plt_symbols() for x86 Adrian Hunter
  2023-01-27 17:02 ` [PATCH 1/9] perf symbols: Correct plt entry sizes " Adrian Hunter
@ 2023-01-27 17:02 ` Adrian Hunter
  2023-01-30 17:34   ` Namhyung Kim
  2023-01-27 17:02 ` [PATCH 3/9] perf symbols: Sort plt relocations for x86 Adrian Hunter
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 16+ messages in thread
From: Adrian Hunter @ 2023-01-27 17:02 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, linux-kernel, linux-perf-users

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 <adrian.hunter@intel.com>
---
 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


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 3/9] perf symbols: Sort plt relocations for x86
  2023-01-27 17:02 [PATCH 0/9] perf symbols: Improve dso__synthesize_plt_symbols() for x86 Adrian Hunter
  2023-01-27 17:02 ` [PATCH 1/9] perf symbols: Correct plt entry sizes " Adrian Hunter
  2023-01-27 17:02 ` [PATCH 2/9] perf symbols: Add support for x86 .plt.sec Adrian Hunter
@ 2023-01-27 17:02 ` Adrian Hunter
  2023-01-27 17:02 ` [PATCH 4/9] perf symbols: Record whether a symbol is an alias for an IFUNC symbol Adrian Hunter
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Adrian Hunter @ 2023-01-27 17:02 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, linux-kernel, linux-perf-users

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 <adrian.hunter@intel.com>
---
 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


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 4/9] perf symbols: Record whether a symbol is an alias for an IFUNC symbol
  2023-01-27 17:02 [PATCH 0/9] perf symbols: Improve dso__synthesize_plt_symbols() for x86 Adrian Hunter
                   ` (2 preceding siblings ...)
  2023-01-27 17:02 ` [PATCH 3/9] perf symbols: Sort plt relocations for x86 Adrian Hunter
@ 2023-01-27 17:02 ` Adrian Hunter
  2023-01-27 17:02 ` [PATCH 5/9] perf symbols: Add support for IFUNC symbols for x86_64 Adrian Hunter
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Adrian Hunter @ 2023-01-27 17:02 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, linux-kernel, linux-perf-users

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

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 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


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 5/9] perf symbols: Add support for IFUNC symbols for x86_64
  2023-01-27 17:02 [PATCH 0/9] perf symbols: Improve dso__synthesize_plt_symbols() for x86 Adrian Hunter
                   ` (3 preceding siblings ...)
  2023-01-27 17:02 ` [PATCH 4/9] perf symbols: Record whether a symbol is an alias for an IFUNC symbol Adrian Hunter
@ 2023-01-27 17:02 ` Adrian Hunter
  2023-01-27 17:02 ` [PATCH 6/9] perf symbols: Allow for .plt without header Adrian Hunter
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Adrian Hunter @ 2023-01-27 17:02 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, linux-kernel, linux-perf-users

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 <adrian.hunter@intel.com>
---
 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


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 6/9] perf symbols: Allow for .plt without header
  2023-01-27 17:02 [PATCH 0/9] perf symbols: Improve dso__synthesize_plt_symbols() for x86 Adrian Hunter
                   ` (4 preceding siblings ...)
  2023-01-27 17:02 ` [PATCH 5/9] perf symbols: Add support for IFUNC symbols for x86_64 Adrian Hunter
@ 2023-01-27 17:02 ` Adrian Hunter
  2023-01-27 17:02 ` [PATCH 7/9] perf symbols: Allow for static executables with .plt Adrian Hunter
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Adrian Hunter @ 2023-01-27 17:02 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, linux-kernel, linux-perf-users

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 <adrian.hunter@intel.com>
---
 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


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 7/9] perf symbols: Allow for static executables with .plt
  2023-01-27 17:02 [PATCH 0/9] perf symbols: Improve dso__synthesize_plt_symbols() for x86 Adrian Hunter
                   ` (5 preceding siblings ...)
  2023-01-27 17:02 ` [PATCH 6/9] perf symbols: Allow for .plt without header Adrian Hunter
@ 2023-01-27 17:02 ` Adrian Hunter
  2023-01-27 17:02 ` [PATCH 8/9] perf symbols: Start adding support for .plt.got for x86 Adrian Hunter
  2023-01-27 17:02 ` [PATCH 9/9] perf symbols: Get symbols for .plt.got for x86-64 Adrian Hunter
  8 siblings, 0 replies; 16+ messages in thread
From: Adrian Hunter @ 2023-01-27 17:02 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, linux-kernel, linux-perf-users

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 <adrian.hunter@intel.com>
---
 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


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 8/9] perf symbols: Start adding support for .plt.got for x86
  2023-01-27 17:02 [PATCH 0/9] perf symbols: Improve dso__synthesize_plt_symbols() for x86 Adrian Hunter
                   ` (6 preceding siblings ...)
  2023-01-27 17:02 ` [PATCH 7/9] perf symbols: Allow for static executables with .plt Adrian Hunter
@ 2023-01-27 17:02 ` Adrian Hunter
  2023-01-27 17:02 ` [PATCH 9/9] perf symbols: Get symbols for .plt.got for x86-64 Adrian Hunter
  8 siblings, 0 replies; 16+ messages in thread
From: Adrian Hunter @ 2023-01-27 17:02 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, linux-kernel, linux-perf-users

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 <adrian.hunter@intel.com>
---
 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


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 9/9] perf symbols: Get symbols for .plt.got for x86-64
  2023-01-27 17:02 [PATCH 0/9] perf symbols: Improve dso__synthesize_plt_symbols() for x86 Adrian Hunter
                   ` (7 preceding siblings ...)
  2023-01-27 17:02 ` [PATCH 8/9] perf symbols: Start adding support for .plt.got for x86 Adrian Hunter
@ 2023-01-27 17:02 ` Adrian Hunter
  2023-01-30 23:26   ` Namhyung Kim
  8 siblings, 1 reply; 16+ messages in thread
From: Adrian Hunter @ 2023-01-27 17:02 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, linux-kernel, linux-perf-users

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 <adrian.hunter@intel.com>
---
 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


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/9] perf symbols: Add support for x86 .plt.sec
  2023-01-27 17:02 ` [PATCH 2/9] perf symbols: Add support for x86 .plt.sec Adrian Hunter
@ 2023-01-30 17:34   ` Namhyung Kim
  2023-01-30 18:35     ` Adrian Hunter
  0 siblings, 1 reply; 16+ messages in thread
From: Namhyung Kim @ 2023-01-30 17:34 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ian Rogers, linux-kernel,
	linux-perf-users

Hi Adrian,

On Fri, Jan 27, 2023 at 9:02 AM Adrian Hunter <adrian.hunter@intel.com> 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

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/9] perf symbols: Add support for x86 .plt.sec
  2023-01-30 17:34   ` Namhyung Kim
@ 2023-01-30 18:35     ` Adrian Hunter
  2023-01-30 22:22       ` Namhyung Kim
  0 siblings, 1 reply; 16+ messages in thread
From: Adrian Hunter @ 2023-01-30 18:35 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ian Rogers, linux-kernel,
	linux-perf-users

On 30/01/23 19:34, Namhyung Kim wrote:
> Hi Adrian,
> 
> On Fri, Jan 27, 2023 at 9:02 AM Adrian Hunter <adrian.hunter@intel.com> 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?



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/9] perf symbols: Add support for x86 .plt.sec
  2023-01-30 18:35     ` Adrian Hunter
@ 2023-01-30 22:22       ` Namhyung Kim
  2023-01-31 10:14         ` Adrian Hunter
  0 siblings, 1 reply; 16+ messages in thread
From: Namhyung Kim @ 2023-01-30 22:22 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ian Rogers, linux-kernel,
	linux-perf-users

On Mon, Jan 30, 2023 at 10:35 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
>
> On 30/01/23 19:34, Namhyung Kim wrote:
> > Hi Adrian,
> >
> > On Fri, Jan 27, 2023 at 9:02 AM Adrian Hunter <adrian.hunter@intel.com> 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

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 9/9] perf symbols: Get symbols for .plt.got for x86-64
  2023-01-27 17:02 ` [PATCH 9/9] perf symbols: Get symbols for .plt.got for x86-64 Adrian Hunter
@ 2023-01-30 23:26   ` Namhyung Kim
  2023-01-31 10:17     ` Adrian Hunter
  0 siblings, 1 reply; 16+ messages in thread
From: Namhyung Kim @ 2023-01-30 23:26 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ian Rogers, linux-kernel,
	linux-perf-users

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 <adrian.hunter@intel.com>
> ---
>  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
> 
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/9] perf symbols: Add support for x86 .plt.sec
  2023-01-30 22:22       ` Namhyung Kim
@ 2023-01-31 10:14         ` Adrian Hunter
  0 siblings, 0 replies; 16+ messages in thread
From: Adrian Hunter @ 2023-01-31 10:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ian Rogers, linux-kernel,
	linux-perf-users

On 31/01/23 00:22, Namhyung Kim wrote:
> On Mon, Jan 30, 2023 at 10:35 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
>>
>> On 30/01/23 19:34, Namhyung Kim wrote:
>>> Hi Adrian,
>>>
>>> On Fri, Jan 27, 2023 at 9:02 AM Adrian Hunter <adrian.hunter@intel.com> 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.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 9/9] perf symbols: Get symbols for .plt.got for x86-64
  2023-01-30 23:26   ` Namhyung Kim
@ 2023-01-31 10:17     ` Adrian Hunter
  0 siblings, 0 replies; 16+ messages in thread
From: Adrian Hunter @ 2023-01-31 10:17 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ian Rogers, linux-kernel,
	linux-perf-users

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 <adrian.hunter@intel.com>
>> ---
>>  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
>>
>>


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2023-01-31 10:18 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-27 17:02 [PATCH 0/9] perf symbols: Improve dso__synthesize_plt_symbols() for x86 Adrian Hunter
2023-01-27 17:02 ` [PATCH 1/9] perf symbols: Correct plt entry sizes " Adrian Hunter
2023-01-27 17:02 ` [PATCH 2/9] perf symbols: Add support for x86 .plt.sec Adrian Hunter
2023-01-30 17:34   ` Namhyung Kim
2023-01-30 18:35     ` Adrian Hunter
2023-01-30 22:22       ` Namhyung Kim
2023-01-31 10:14         ` Adrian Hunter
2023-01-27 17:02 ` [PATCH 3/9] perf symbols: Sort plt relocations for x86 Adrian Hunter
2023-01-27 17:02 ` [PATCH 4/9] perf symbols: Record whether a symbol is an alias for an IFUNC symbol Adrian Hunter
2023-01-27 17:02 ` [PATCH 5/9] perf symbols: Add support for IFUNC symbols for x86_64 Adrian Hunter
2023-01-27 17:02 ` [PATCH 6/9] perf symbols: Allow for .plt without header Adrian Hunter
2023-01-27 17:02 ` [PATCH 7/9] perf symbols: Allow for static executables with .plt Adrian Hunter
2023-01-27 17:02 ` [PATCH 8/9] perf symbols: Start adding support for .plt.got for x86 Adrian Hunter
2023-01-27 17:02 ` [PATCH 9/9] perf symbols: Get symbols for .plt.got for x86-64 Adrian Hunter
2023-01-30 23:26   ` Namhyung Kim
2023-01-31 10:17     ` Adrian Hunter

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.