elf: support dynamic symbol table lookup
authorFrancis Deslauriers <francis.deslauriers@efficios.com>
Fri, 31 Aug 2018 15:59:56 +0000 (11:59 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Fri, 31 Aug 2018 18:41:57 +0000 (14:41 -0400)
Background
==========
There may be two symbol tables in a shared object or executable. The
normal symbol table (.symtab) and the dynamic symbol table (.dynsym).

The normal symbol table contains lots of information, such as static
linking data, but none of it is used at runtime. This is why some
shared libraries are 'stripped', reducing the final size of the file.
Stripping an object file removes the entire .symtab section of the elf
file, amongst other things.

The dynamic symbol table contains symbols that are needed for dynamic
linking of the shared object. The symbols in that section form a subset
of the symbols contained in the normal symbol section (before
stripping). The .dynsym section is left untouched when stripping a file
as it is needed at runtime.

Current limitation
==================
The current elf parsing implementation looks for the normal symbol
section (.symtab) to find the target symbol. If the .symtab is not
found, the parsing stops and returns that the symbol was not found. As
explained in the section above, a shared library might be stripped from
its normal symbol table, but still have a dynamic symbol table (.dynsym)
containing the information of the target symbol. For example, on
distributions where libc is stripped, the malloc symbol can only be
found in the .dynsym section

Solution
========
Look for the normal symbol section first and, if it's found, use it to
find the symbol, as was previously done. If the .symtab is absent,
try to use the dynamic symbol section instead.

This commit also adds a testcase for this feature.

Signed-off-by: Francis Deslauriers <francis.deslauriers@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
src/common/lttng-elf.c
tests/regression/kernel/test_userspace_probe
tests/utils/testapp/userspace-probe-elf-binary/Makefile.am
tests/utils/testapp/userspace-probe-elf-binary/foo.c [new file with mode: 0644]
tests/utils/testapp/userspace-probe-elf-binary/foo.h [new file with mode: 0644]
tests/utils/testapp/userspace-probe-elf-binary/userspace-probe-elf-binary.c

index f5702ae453f4df08625ced8e5cdcf82367b6c502..52e05f0c6fe825f60a465aabe49d39f653140e88 100644 (file)
@@ -38,6 +38,8 @@
 #define TEXT_SECTION_NAME      ".text"
 #define SYMBOL_TAB_SECTION_NAME ".symtab"
 #define STRING_TAB_SECTION_NAME ".strtab"
+#define DYNAMIC_SYMBOL_TAB_SECTION_NAME ".dynsym"
+#define DYNAMIC_STRING_TAB_SECTION_NAME ".dynstr"
 #define NOTE_STAPSDT_SECTION_NAME ".note.stapsdt"
 #define NOTE_STAPSDT_NAME "stapsdt"
 #define NOTE_STAPSDT_TYPE 3
@@ -736,6 +738,7 @@ int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset)
        char *curr_sym_str = NULL;
        char *symbol_table_data = NULL;
        char *string_table_data = NULL;
+       char *string_table_name = NULL;
        struct lttng_elf_shdr *symtab_hdr = NULL;
        struct lttng_elf_shdr *strtab_hdr = NULL;
        struct lttng_elf *elf = NULL;
@@ -751,14 +754,29 @@ int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset)
                goto end;
        }
 
-       /* Get the symbol table section header. */
+       /*
+        * The .symtab section might not exist on stripped binaries.
+        * Try to get the symbol table section header first. If it's absent,
+        * try to get the dynamic symbol table. All symbols in the dynamic
+        * symbol tab are in the (normal) symbol table if it exists.
+        */
        ret = lttng_elf_get_section_hdr_by_name(elf, SYMBOL_TAB_SECTION_NAME,
                        &symtab_hdr);
        if (ret) {
-               DBG("Cannot get ELF Symbol Table section.");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto destroy_elf;
+               DBG("Cannot get ELF Symbol Table section. Trying to get ELF Dynamic Symbol Table section.");
+               /* Get the dynamic symbol table section header. */
+               ret = lttng_elf_get_section_hdr_by_name(elf, DYNAMIC_SYMBOL_TAB_SECTION_NAME,
+                               &symtab_hdr);
+               if (ret) {
+                       DBG("Cannot get ELF Symbol Table nor Dynamic Symbol Table sections.");
+                       ret = LTTNG_ERR_ELF_PARSING;
+                       goto destroy_elf;
+               }
+               string_table_name = DYNAMIC_STRING_TAB_SECTION_NAME;
+       } else {
+               string_table_name = STRING_TAB_SECTION_NAME;
        }
+
        /* Get the data associated with the symbol table section. */
        symbol_table_data = lttng_elf_get_section_data(elf, symtab_hdr);
        if (symbol_table_data == NULL) {
@@ -768,7 +786,7 @@ int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset)
        }
 
        /* Get the string table section header. */
-       ret = lttng_elf_get_section_hdr_by_name(elf, STRING_TAB_SECTION_NAME,
+       ret = lttng_elf_get_section_hdr_by_name(elf, string_table_name,
                        &strtab_hdr);
        if (ret) {
                DBG("Cannot get ELF string table section.");
index fb8ce303ffa2719a64c3c596d19c85727403a794..f6da00dd4b0f1ebc42deec20de595c7c9e52e333 100755 (executable)
@@ -21,7 +21,7 @@ TEST_DESC="Userspace probe - Testing userspace probe on ELF symbol"
 CURDIR=$(dirname "$0")/
 TESTDIR=$CURDIR/../..
 TESTAPP_DIR="$TESTDIR/utils/testapp/"
-ELF_TEST_BIN_DIR="$TESTAPP_DIR/userspace-probe-elf-binary/"
+ELF_TEST_BIN_DIR="$TESTAPP_DIR/userspace-probe-elf-binary/.libs/"
 ELF_TEST_BIN_NAME="userspace-probe-elf-binary"
 ELF_TEST_BIN="$ELF_TEST_BIN_DIR/$ELF_TEST_BIN_NAME"
 ELF_CXX_TEST_BIN_DIR="$TESTAPP_DIR/userspace-probe-elf-cxx-binary/"
@@ -32,7 +32,7 @@ SDT_TEST_BIN_NAME="userspace-probe-sdt-binary"
 SDT_TEST_BIN="$SDT_TEST_BIN_DIR/$SDT_TEST_BIN_NAME"
 ELF_SYMBOL="test_function"
 PROBE_EVENT_NAME=userspace_probe_test_event
-NUM_TESTS=81
+NUM_TESTS=87
 OUTPUT_DEST=/dev/null
 ERROR_OUTPUT_DEST=/dev/null
 
@@ -284,6 +284,30 @@ function test_userspace_probe_elf ()
        rm -rf "$TRACE_PATH"
 }
 
+function test_userspace_probe_elf_dynamic_symbol ()
+{
+       TRACE_PATH=$(mktemp -d)
+       SESSION_NAME="test_userprobe_elf"
+       LIBFOO_PATH="$ELF_TEST_BIN_DIR/libfoo.so"
+       ENABLE_EXPR="elf:$LIBFOO_PATH:dynamic_symbol"
+
+       diag "Userspace probe on Elf dynamic symbol enabled and traced"
+
+       create_lttng_session_ok $SESSION_NAME "$TRACE_PATH"
+
+       lttng_enable_kernel_userspace_probe_event_ok $SESSION_NAME "$ENABLE_EXPR" $PROBE_EVENT_NAME
+
+       start_lttng_tracing_ok $SESSION_NAME
+       eval "$ELF_TEST_BIN"  > /dev/null
+       stop_lttng_tracing_ok $SESSION_NAME
+
+       validate_trace $PROBE_EVENT_NAME "$TRACE_PATH"
+
+       destroy_lttng_session_ok $SESSION_NAME
+
+       rm -rf "$TRACE_PATH"
+}
+
 function test_userspace_probe_elf_cxx_function ()
 {
        TRACE_PATH=$(mktemp -d)
@@ -802,6 +826,7 @@ skip $isroot "Root access is needed. Skipping all tests." $NUM_TESTS ||
 
        # Successful tracing userspace probe elf
        test_userspace_probe_elf
+       test_userspace_probe_elf_dynamic_symbol
 
        # Disable userspace-probe elf
        test_userspace_probe_elf_disable
index 9d4696b56eb750ade6948615d61a6408d1147003..03f5d5a8250a1a6eee1262dd8f7496ee6cabb488 100644 (file)
@@ -1,4 +1,20 @@
 # no optimization
 AM_CFLAGS = -O0
+noinst_LTLIBRARIES = libfoo.la
+
+libfoo_la_SOURCES = foo.c foo.h
+libfoo_la_LDFLAGS = -shared -module -avoid-version -rpath $(abs_builddir)/.libs/
+
 noinst_PROGRAMS = userspace-probe-elf-binary
 userspace_probe_elf_binary_SOURCES = userspace-probe-elf-binary.c
+userspace_probe_elf_binary_LDADD = libfoo.la
+
+libfoo.strip: libfoo.la
+       $(OBJCOPY) --strip-all .libs/libfoo.so
+
+all-local: libfoo.strip
+       @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+               for script in $(EXTRA_DIST); do \
+                       cp -f $(srcdir)/$$script $(builddir); \
+               done; \
+       fi
diff --git a/tests/utils/testapp/userspace-probe-elf-binary/foo.c b/tests/utils/testapp/userspace-probe-elf-binary/foo.c
new file mode 100644 (file)
index 0000000..59dd12a
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 - Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+int dynamic_symbol(int a)
+{
+       return a + a;
+}
diff --git a/tests/utils/testapp/userspace-probe-elf-binary/foo.h b/tests/utils/testapp/userspace-probe-elf-binary/foo.h
new file mode 100644 (file)
index 0000000..e3f85ab
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018  Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+int dynamic_symbol(int a);
index 76f44027fadbd10a68a243b1877e9f3fe49e38df..a9dfb22b4cccc1ae7cd679257cba77a8b0b700c6 100644 (file)
@@ -16,6 +16,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include "foo.h"
 volatile int not_a_function = 0;
 void __attribute__ ((noinline))  test_function()
 {
@@ -24,5 +25,6 @@ void __attribute__ ((noinline))  test_function()
 int main(int argc, char *argv[])
 {
        test_function();
+       dynamic_symbol(42);
        return 0;
 }
This page took 0.029646 seconds and 4 git commands to generate.