aboutsummaryrefslogtreecommitdiff
path: root/sysdeps
diff options
context:
space:
mode:
authorPetr Machata <pmachata@redhat.com>2013-11-04 20:22:06 +0100
committerPetr Machata <pmachata@redhat.com>2013-11-05 01:58:07 +0100
commitfec0b3b1b8323220255f9ebdcac7db2b689783ef (patch)
treef935e666764626790f1bb9acab46ab178cd4b6fb /sysdeps
parent54bb64cf2eae7a0daa4d17e980b743b8ae69413b (diff)
downloadltrace-fec0b3b1b8323220255f9ebdcac7db2b689783ef.tar.gz
Move code for tracing IFUNC symbols
- ... from sysdeps/linux-gnu/hooks.c to sysdeps/linux-gnu/trace.c where it fits better.
Diffstat (limited to 'sysdeps')
-rw-r--r--sysdeps/linux-gnu/hooks.c224
-rw-r--r--sysdeps/linux-gnu/trace.c220
2 files changed, 220 insertions, 224 deletions
diff --git a/sysdeps/linux-gnu/hooks.c b/sysdeps/linux-gnu/hooks.c
index 10f1a11..1e375fb 100644
--- a/sysdeps/linux-gnu/hooks.c
+++ b/sysdeps/linux-gnu/hooks.c
@@ -23,22 +23,15 @@
#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 "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 *
@@ -217,220 +210,3 @@ 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;
- if (arch_translate_address_dyn(proc, u.a, &u.a) < 0) {
- fprintf(stderr, "Couldn't OPD-translate the address returned"
- " by the IFUNC resolver.\n");
- goto done;
- }
-
- 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/trace.c b/sysdeps/linux-gnu/trace.c
index 6c3258e..220486c 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -46,11 +46,14 @@
#include "breakpoint.h"
#include "debug.h"
#include "events.h"
+#include "fetch.h"
#include "ltrace-elf.h"
#include "options.h"
#include "proc.h"
+#include "prototype.h"
#include "ptrace.h"
#include "type.h"
+#include "value.h"
void
trace_fail_warning(pid_t pid)
@@ -1287,3 +1290,220 @@ linux_elf_add_plt_entry_irelative(struct process *proc, struct ltelf *lte,
free(name);
return i < 0 ? PLT_FAIL : PLT_OK;
}
+
+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;
+ if (arch_translate_address_dyn(proc, u.a, &u.a) < 0) {
+ fprintf(stderr, "Couldn't OPD-translate the address returned"
+ " by the IFUNC resolver.\n");
+ goto done;
+ }
+
+ 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;
+}