namhyung / uftrace

Function graph tracer for C/C++/Rust/Python
https://uftrace.github.io/slide/
GNU General Public License v2.0
3.04k stars 472 forks source link

library tracing failure with -fno-plt in Ubuntu 19.04 #1091

Open honggyukim opened 4 years ago

honggyukim commented 4 years ago

In Ubuntu 19.04, I compiled a simple binary as follows:

$ cat /etc/os-release | grep PRETTY_NAME
PRETTY_NAME="Ubuntu 19.04"

$ gcc -pg -g -o t-abc s-abc.c 
$ gcc -pg -g -fno-plt -o t-abc-no-plt s-abc.c

Their tracing result is different and uftrace cannot trace library calls for -fno-plt compiled binary.

$ uftrace -F main t-abc
# DURATION     TID     FUNCTION
            [ 32144] | main() {
            [ 32144] |   a() {
            [ 32144] |     b() {
            [ 32144] |       c() {
   0.538 us [ 32144] |         getpid();
   1.786 us [ 32144] |       } /* c */
   2.032 us [ 32144] |     } /* b */
   2.288 us [ 32144] |   } /* a */
   2.711 us [ 32144] | } /* main */

$ uftrace -F main t-abc-no-plt 
# DURATION     TID     FUNCTION
            [ 32179] | main() {
            [ 32179] |   a() {
            [ 32179] |     b() {
   0.586 us [ 32179] |       c();
   1.506 us [ 32179] |     } /* b */
   1.782 us [ 32179] |   } /* a */
   2.224 us [ 32179] | } /* main */

It makes t212_noplt_libcall failed.

$ ./runtest.py -vdp -O0 212
    ...
t212_noplt_libcall: diff result of -pg -O0
--- expect      2020-01-13 16:39:57.727608520 +0900
+++ result      2020-01-13 16:39:57.727608520 +0900
@@ -3,5 +3,3 @@
      b() {
-       c() {
-         getpid();
-       } /* c */
+       c();
      } /* b */

212 noplt_libcall       : NG

Here is GCC compiler info.

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/8/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 8.3.0-6ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-8/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-8 --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-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 8.3.0 (Ubuntu 8.3.0-6ubuntu1)
honggyukim commented 4 years ago

The -fno-plt compiled binary shows the same problem even if it's executed in a different machine such as Ubuntu 18.04.

Here are the binaries.

The problem can be easily reproduced with t-abc-no-plt.

AnsBal commented 4 years ago

I think one problem is that we assume there's only one RELA section in no-plt binary which is not the case in this binary. Although the binary is built with -fno-plt there is a .PLT in it. This leads us to the second problem, that is, we assume that -fno-plt is used if we do not find .PLT in the binary. Which is not true, in this binary at least. Here is a quick and dirty (very very dirty) patch that gives insight about the problem.

diff --git a/arch/x86_64/symbol.c b/arch/x86_64/symbol.c
index fed7de95..60dd961b 100644
--- a/arch/x86_64/symbol.c
+++ b/arch/x86_64/symbol.c
@@ -27,15 +27,17 @@ int arch_load_dynsymtab_noplt(struct symtab *dsymtab,
        unsigned grow = SYMTAB_GROW;
        unsigned long reloc_start = 0;
        size_t reloc_entsize = 0;
+       char *sec_name;

        memset(dsymtab, 0, sizeof(*dsymtab));

        /* assumes there's only one RELA section (rela.dyn) for no-plt binary */
        elf_for_each_shdr(elf, &sec_iter) {
-               if (sec_iter.shdr.sh_type == SHT_RELA) {
+               sec_name = elf_get_name(elf, &sec_iter, sec_iter.shdr.sh_name);
+               if (sec_iter.shdr.sh_type == SHT_RELA && !strcmp(sec_name, ".rela.dyn")) {
                        memcpy(&rel_iter, &sec_iter, sizeof(sec_iter));
                        pr_dbg2("found RELA section: %s\n",
-                               elf_get_name(elf, &sec_iter, sec_iter.shdr.sh_name));
+                               sec_name);

                        reloc_start = rel_iter.shdr.sh_addr + offset;
                        reloc_entsize = rel_iter.shdr.sh_entsize;
diff --git a/libmcount/plthook.c b/libmcount/plthook.c
index 2ce62cce..7f43b625 100644
--- a/libmcount/plthook.c
+++ b/libmcount/plthook.c
@@ -199,7 +199,7 @@ static int find_got(struct uftrace_elf_data *elf,
                }
        }

-       if (!plt_found) {
+       if (1) {
                pd = mcount_arch_hook_no_plt(elf, modname, offset);
                if (pd == NULL)
                        pr_dbg2("no PLTGOT found.. ignoring...\n");
diff --git a/utils/symbol.c b/utils/symbol.c
index 1cfacdb0..d14931ae 100644
--- a/utils/symbol.c
+++ b/utils/symbol.c
@@ -549,7 +549,7 @@ int load_elf_dynsymtab(struct symtab *dsymtab, struct uftrace_elf_data *elf,
                goto out;
        }

-       if (rel_type == SHT_NULL) {
+       if (1) {
                arch_load_dynsymtab_noplt(dsymtab, elf, offset, flags);
                goto out_sort;
        }