From: Francis Deslauriers Date: Fri, 31 Aug 2018 15:59:56 +0000 (-0400) Subject: elf: support dynamic symbol table lookup X-Git-Tag: v2.11.0-rc1~2 X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=commitdiff_plain;h=ef3dfe5d31c88fb548189a6441aaf8b2afc0bd4b elf: support dynamic symbol table lookup 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 Signed-off-by: Jérémie Galarneau --- diff --git a/src/common/lttng-elf.c b/src/common/lttng-elf.c index f5702ae45..52e05f0c6 100644 --- a/src/common/lttng-elf.c +++ b/src/common/lttng-elf.c @@ -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."); diff --git a/tests/regression/kernel/test_userspace_probe b/tests/regression/kernel/test_userspace_probe index fb8ce303f..f6da00dd4 100755 --- a/tests/regression/kernel/test_userspace_probe +++ b/tests/regression/kernel/test_userspace_probe @@ -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 diff --git a/tests/utils/testapp/userspace-probe-elf-binary/Makefile.am b/tests/utils/testapp/userspace-probe-elf-binary/Makefile.am index 9d4696b56..03f5d5a82 100644 --- a/tests/utils/testapp/userspace-probe-elf-binary/Makefile.am +++ b/tests/utils/testapp/userspace-probe-elf-binary/Makefile.am @@ -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 index 000000000..59dd12a06 --- /dev/null +++ b/tests/utils/testapp/userspace-probe-elf-binary/foo.c @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2018 - Francis Deslauriers + * + * 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 index 000000000..e3f85aba3 --- /dev/null +++ b/tests/utils/testapp/userspace-probe-elf-binary/foo.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 Francis Deslauriers + * + * 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); diff --git a/tests/utils/testapp/userspace-probe-elf-binary/userspace-probe-elf-binary.c b/tests/utils/testapp/userspace-probe-elf-binary/userspace-probe-elf-binary.c index 76f44027f..a9dfb22b4 100644 --- a/tests/utils/testapp/userspace-probe-elf-binary/userspace-probe-elf-binary.c +++ b/tests/utils/testapp/userspace-probe-elf-binary/userspace-probe-elf-binary.c @@ -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; }