aboutsummaryrefslogtreecommitdiff
path: root/sysdeps
diff options
context:
space:
mode:
authorPetr Machata <pmachata@redhat.com>2013-10-16 17:02:20 +0200
committerPetr Machata <pmachata@redhat.com>2013-10-23 01:00:03 +0200
commitb3d6180e6d1cfc188a44b60dda6a7ac4faf0fdce (patch)
treeb4020ad8dd1ff5e1a0dc33ceabc8f3767f534ff4 /sysdeps
parentb8f0d8b5859a7d69f6aed4904a87ac6a423285f6 (diff)
downloadltrace-b3d6180e6d1cfc188a44b60dda6a7ac4faf0fdce.tar.gz
Linux backend now supports tracing of IFUNC symbols
Diffstat (limited to 'sysdeps')
-rw-r--r--sysdeps/linux-gnu/hooks.c224
-rw-r--r--sysdeps/linux-gnu/os.h14
2 files changed, 236 insertions, 2 deletions
diff --git a/sysdeps/linux-gnu/hooks.c b/sysdeps/linux-gnu/hooks.c
index 9a9df76..b4ae2c0 100644
--- a/sysdeps/linux-gnu/hooks.c
+++ b/sysdeps/linux-gnu/hooks.c
@@ -23,15 +23,23 @@
#include <alloca.h>
#include <errno.h>
#include <pwd.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include "sysdep.h"
-#include "vect.h"
+#include "backend.h"
+#include "breakpoint.h"
#include "dict.h"
+#include "fetch.h"
+#include "library.h"
#include "options.h"
+#include "prototype.h"
+#include "sysdep.h"
+#include "type.h"
+#include "value.h"
+#include "vect.h"
static char *
append(const char *str1, const char *str2)
@@ -209,3 +217,215 @@ os_get_ltrace_conf_filenames(struct vect *retp)
return 0;
}
+
+static struct prototype *
+void_prototype(void)
+{
+ static struct prototype ret;
+ if (ret.return_info == NULL) {
+ prototype_init(&ret);
+ ret.return_info = type_get_voidptr();
+ ret.own_return_info = 0;
+ }
+ return &ret;
+}
+
+int
+os_library_symbol_init(struct library_symbol *libsym)
+{
+ libsym->os = (struct os_library_symbol_data){};
+ return 0;
+}
+
+void
+os_library_symbol_destroy(struct library_symbol *libsym)
+{
+}
+
+int
+os_library_symbol_clone(struct library_symbol *retp,
+ struct library_symbol *libsym)
+{
+ retp->os = libsym->os;
+ return 0;
+}
+
+enum plt_status
+os_elf_add_func_entry(struct process *proc, struct ltelf *lte,
+ const GElf_Sym *sym,
+ arch_addr_t addr, const char *name,
+ struct library_symbol **ret)
+{
+ if (GELF_ST_TYPE(sym->st_info) == STT_FUNC)
+ return PLT_DEFAULT;
+
+ bool ifunc = false;
+#ifdef STT_GNU_IFUNC
+ ifunc = GELF_ST_TYPE(sym->st_info) == STT_GNU_IFUNC;
+#endif
+
+ if (ifunc) {
+#define S ".IFUNC"
+ char *tmp_name = malloc(strlen(name) + sizeof S);
+ struct library_symbol *tmp = malloc(sizeof *tmp);
+ if (tmp_name == NULL || tmp == NULL) {
+ fail:
+ free(tmp_name);
+ free(tmp);
+ return PLT_FAIL;
+ }
+ sprintf(tmp_name, "%s%s", name, S);
+#undef S
+
+ if (library_symbol_init(tmp, addr, tmp_name, 1,
+ LS_TOPLT_NONE) < 0)
+ goto fail;
+ tmp->proto = void_prototype();
+ tmp->os.is_ifunc = 1;
+
+ *ret = tmp;
+ return PLT_OK;
+ }
+
+ *ret = NULL;
+ return PLT_OK;
+}
+
+static enum callback_status
+libsym_at_address(struct library_symbol *libsym, void *addrp)
+{
+ arch_addr_t addr = *(arch_addr_t *)addrp;
+ return CBS_STOP_IF(addr == libsym->enter_addr);
+}
+
+static void
+ifunc_ret_hit(struct breakpoint *bp, struct process *proc)
+{
+ struct fetch_context *fetch = fetch_arg_init(LT_TOF_FUNCTION, proc,
+ type_get_voidptr());
+ if (fetch == NULL)
+ return;
+
+ struct breakpoint *nbp = NULL;
+ int own_libsym = 0;
+
+ struct value value;
+ value_init(&value, proc, NULL, type_get_voidptr(), 0);
+ size_t sz = value_size(&value, NULL);
+ union {
+ uint64_t u64;
+ uint32_t u32;
+ arch_addr_t a;
+ } u;
+
+ if (fetch_retval(fetch, LT_TOF_FUNCTIONR, proc,
+ value.type, &value) < 0
+ || sz > 8 /* Captures failure as well. */
+ || value_extract_buf(&value, (void *) &u, NULL) < 0) {
+ fail:
+ fprintf(stderr,
+ "Couldn't trace the function "
+ "indicated by IFUNC resolver.\n");
+ goto done;
+ }
+
+ assert(sz == 4 || sz == 8);
+ /* XXX double casts below: */
+ if (sz == 4)
+ u.a = (arch_addr_t)(uintptr_t)u.u32;
+ else
+ u.a = (arch_addr_t)(uintptr_t)u.u64;
+
+ assert(bp->os.ret_libsym != NULL);
+
+ struct library *lib = bp->os.ret_libsym->lib;
+ assert(lib != NULL);
+
+ /* Look if we already have a symbol with this address.
+ * Otherwise create a new one. */
+ struct library_symbol *libsym
+ = library_each_symbol(lib, NULL, libsym_at_address, &u.a);
+ if (libsym == NULL) {
+ libsym = malloc(sizeof *libsym);
+ char *name = strdup(bp->os.ret_libsym->name);
+
+ if (libsym == NULL
+ || name == NULL
+ || library_symbol_init(libsym, u.a, name, 1,
+ LS_TOPLT_NONE) < 0) {
+ free(libsym);
+ free(name);
+ goto fail;
+ }
+
+ /* Snip the .IFUNC token. */
+ *strrchr(name, '.') = 0;
+
+ own_libsym = 1;
+ library_add_symbol(lib, libsym);
+ }
+
+ nbp = malloc(sizeof *bp);
+ if (nbp == NULL || breakpoint_init(nbp, proc, u.a, libsym) < 0)
+ goto fail;
+
+ /* If there already is a breakpoint at that address, that is
+ * suspicious, but whatever. */
+ struct breakpoint *pre_bp = insert_breakpoint(proc, nbp);
+ if (pre_bp == NULL)
+ goto fail;
+ if (pre_bp == nbp) {
+ /* PROC took our breakpoint, so these resources are
+ * not ours anymore. */
+ nbp = NULL;
+ own_libsym = 0;
+ }
+
+done:
+ free(nbp);
+ if (own_libsym) {
+ library_symbol_destroy(libsym);
+ free(libsym);
+ }
+ fetch_arg_done(fetch);
+}
+
+static int
+create_ifunc_ret_bp(struct breakpoint **ret,
+ struct breakpoint *bp, struct process *proc)
+{
+ *ret = create_default_return_bp(proc);
+ if (*ret == NULL)
+ return -1;
+ static struct bp_callbacks cbs = {
+ .on_hit = ifunc_ret_hit,
+ };
+ breakpoint_set_callbacks(*ret, &cbs);
+
+ (*ret)->os.ret_libsym = bp->libsym;
+
+ return 0;
+}
+
+int
+os_breakpoint_init(struct process *proc, struct breakpoint *bp)
+{
+ if (bp->libsym != NULL && bp->libsym->os.is_ifunc) {
+ static struct bp_callbacks cbs = {
+ .get_return_bp = create_ifunc_ret_bp,
+ };
+ breakpoint_set_callbacks(bp, &cbs);
+ }
+ return 0;
+}
+
+void
+os_breakpoint_destroy(struct breakpoint *bp)
+{
+}
+
+int
+os_breakpoint_clone(struct breakpoint *retp, struct breakpoint *bp)
+{
+ return 0;
+}
diff --git a/sysdeps/linux-gnu/os.h b/sysdeps/linux-gnu/os.h
index 62bf38b..60fd604 100644
--- a/sysdeps/linux-gnu/os.h
+++ b/sysdeps/linux-gnu/os.h
@@ -23,3 +23,17 @@ struct os_process_data {
arch_addr_t debug_addr;
int debug_state;
};
+
+#define OS_HAVE_LIBRARY_SYMBOL_DATA
+struct os_library_symbol_data {
+ unsigned is_ifunc : 1;
+};
+
+#define OS_HAVE_BREAKPOINT_DATA
+struct os_breakpoint_data {
+ /* For breakpoints that track return from IFUNC functions, we
+ * keep here the IFUNC symbol itself. */
+ struct library_symbol *ret_libsym;
+};
+
+#define OS_HAVE_ADD_FUNC_ENTRY