From c167c2a3a404f5166ffd70a15a09c97cd1883ee8 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 31 Jan 2011 20:27:59 +0100 Subject: adding support for global symbol config one global tree to rule them all - only one tree is searched during the plt entry/exit - symbols are added during the bind audit callback --- ChangeLog | 6 +++ src/Makefile | 3 +- src/args.c | 54 +++++++++---------- src/args.h | 10 ++-- src/audit-init.c | 4 ++ src/audit.c | 47 ++++++++++++----- src/config.h | 17 ++++++ src/symbol.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 251 insertions(+), 44 deletions(-) create mode 100644 src/symbol.c diff --git a/ChangeLog b/ChangeLog index 27022f2..1bae726 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2010-01-31 Jiri Olsa + * adding support for global symbol config + one global symbol tree to rule them all + - only one tree is searched during the plt entry/exit + - symbols are added during the bind audit callback + 2010-10-17 Jiri Olsa * Artur Skawina - enhancing names check with *-logic for diff --git a/src/Makefile b/src/Makefile index 796028a..eb16064 100644 --- a/src/Makefile +++ b/src/Makefile @@ -28,7 +28,8 @@ AUDIT_OBJS=\ src/fifo.o \ src/output.o \ src/objsearch.o \ - src/stack.o + src/stack.o \ + src/symbol.o ifeq ($(CONFIG_ARCH_HAVE_ARGS),y) AUDIT_OBJS+=\ diff --git a/src/args.c b/src/args.c index 8672156..a7b745e 100644 --- a/src/args.c +++ b/src/args.c @@ -749,25 +749,6 @@ int lt_args_init(struct lt_config_shared *cfg) return ret; } -static struct lt_args_sym* getsym(struct lt_config_shared *cfg, char *sym) -{ - struct lt_args_sym *a; - ENTRY e, *ep; - - PRINT_VERBOSE(cfg, 1, "request for <%s>\n", sym); - - e.key = sym; - hsearch_r(e, FIND, &ep, &cfg->args_tab); - - if (!ep) - return NULL; - - a = (struct lt_args_sym*) ep->data; - - PRINT_VERBOSE(cfg, 1, "found %p <%s>\n", a, a->name); - return a; -} - static int getstr_addenum(struct lt_config_shared *cfg, struct lt_arg *arg, char *argbuf, int alen, long val) { @@ -1096,12 +1077,32 @@ struct lt_args_include* lt_args_buf_get(void) return inc; } -int lt_args_sym_entry(struct lt_config_shared *cfg, char *sym, La_regs *regs, - char **argbuf, char **argdbuf) +struct lt_args_sym* lt_args_sym_get(struct lt_config_shared *cfg, + const char *sym) +{ + struct lt_args_sym *a; + ENTRY e, *ep; + + PRINT_VERBOSE(cfg, 1, "request for <%s>\n", sym); + + e.key = (char*) sym; + hsearch_r(e, FIND, &ep, &cfg->args_tab); + + if (!ep) + return NULL; + + a = (struct lt_args_sym*) ep->data; + + PRINT_VERBOSE(cfg, 1, "found %p <%s>\n", a, a->name); + return a; +} + +int lt_args_sym_entry(struct lt_config_shared *cfg, struct lt_symbol *sym, + La_regs *regs, char **argbuf, char **argdbuf) { - struct lt_args_sym *asym; + struct lt_args_sym *asym = sym ? sym->args : NULL; - if (NULL == (asym = getsym(cfg, sym))) + if (!asym) return -1; return getargs(cfg, asym, regs, argbuf, argdbuf); @@ -1146,12 +1147,13 @@ static int getargs_ret(struct lt_config_shared *cfg, struct lt_args_sym *asym, return lt_stack_process_ret(cfg, asym, regs, &data); } -int lt_args_sym_exit(struct lt_config_shared *cfg, char *sym, La_regs *inregs, La_retval *outregs, +int lt_args_sym_exit(struct lt_config_shared *cfg, struct lt_symbol *sym, + La_regs *inregs, La_retval *outregs, char **argbuf, char **argdbuf) { - struct lt_args_sym *asym; + struct lt_args_sym *asym = sym ? sym->args : NULL; - if (NULL == (asym = getsym(cfg, sym))) + if (!asym) return -1; return getargs_ret(cfg, asym, outregs, argbuf, argdbuf); diff --git a/src/args.h b/src/args.h index b7fa427..e0f4846 100644 --- a/src/args.h +++ b/src/args.h @@ -23,6 +23,7 @@ #define ARGS_H struct lt_config_shared; +struct lt_symbol; enum { LT_ARGS_DTYPE_POD = 1, @@ -126,10 +127,13 @@ struct lt_args_data { /* arguments */ int lt_args_init(struct lt_config_shared *cfg); -int lt_args_sym_entry(struct lt_config_shared *cfg, char *sym, La_regs *regs, +struct lt_args_sym* lt_args_sym_get(struct lt_config_shared *cfg, + const char *sym); +int lt_args_sym_entry(struct lt_config_shared *cfg, struct lt_symbol *sym, + La_regs *regs, char **argbuf, char **argdbuf); +int lt_args_sym_exit(struct lt_config_shared *cfg, struct lt_symbol *sym, + La_regs *inregs, La_retval *outregs, char **argbuf, char **argdbuf); -int lt_args_sym_exit(struct lt_config_shared *cfg, char *sym, La_regs *inregs, - La_retval *outregs, char **argbuf, char **argdbuf); int lt_args_add_enum(struct lt_config_shared *cfg, char *name, struct lt_list_head *h); struct lt_enum_elem* lt_args_get_enum(struct lt_config_shared *cfg, char *name, char *val); diff --git a/src/audit-init.c b/src/audit-init.c index 686c56d..fad89ca 100644 --- a/src/audit-init.c +++ b/src/audit-init.c @@ -242,6 +242,10 @@ int audit_init(int argc, char **argv, char **env) if (lt_sh(&cfg, not_follow_fork)) lt_sh(&cfg, pid) = getpid(); + /* enable global symbols if needed */ + lt_sh(&cfg, global_symbols) = lt_sh(&cfg, args_enabled); + PRINT_VERBOSE(&cfg, 1, "global_symbols %d\n", lt_sh(&cfg, global_symbols)); + cfg.init_ok = 1; return 0; } diff --git a/src/audit.c b/src/audit.c index 2313c7c..d26a539 100644 --- a/src/audit.c +++ b/src/audit.c @@ -70,12 +70,13 @@ static int check_flow_below(const char *symname, int in) return ret; } -static int sym_entry(const char *symname, char *lib_from, char *lib_to, - La_regs *regs) +static int sym_entry(const char *symname, void *ptr, + char *lib_from, char *lib_to, La_regs *regs) { int argret = -1; char *argbuf = "", *argdbuf = ""; struct timeval tv; + struct lt_symbol *sym = NULL; PRINT_VERBOSE(&cfg, 2, "%s@%s\n", symname, lib_to); @@ -85,9 +86,12 @@ static int sym_entry(const char *symname, char *lib_from, char *lib_to, if (lt_sh(&cfg, timestamp) || lt_sh(&cfg, counts)) gettimeofday(&tv, NULL); + if (lt_sh(&cfg, global_symbols)) + sym = lt_symbol_get(cfg.sh, ptr, symname); + #ifdef CONFIG_ARCH_HAVE_ARGS argret = lt_sh(&cfg, args_enabled) ? - lt_args_sym_entry(cfg.sh, (char*) symname, regs, &argbuf, &argdbuf) : -1; + lt_args_sym_entry(cfg.sh, sym, regs, &argbuf, &argdbuf) : -1; #endif if (lt_sh(&cfg, pipe)) { @@ -119,12 +123,14 @@ static int sym_entry(const char *symname, char *lib_from, char *lib_to, return 0; } -static int sym_exit(const char *symname, char *lib_from, char *lib_to, - const La_regs *inregs, La_retval *outregs) +static int sym_exit(const char *symname, void *ptr, + char *lib_from, char *lib_to, + const La_regs *inregs, La_retval *outregs) { int argret = -1; char *argbuf = "", *argdbuf = ""; struct timeval tv; + struct lt_symbol *sym = NULL; PRINT_VERBOSE(&cfg, 2, "%s@%s\n", symname, lib_to); @@ -134,9 +140,12 @@ static int sym_exit(const char *symname, char *lib_from, char *lib_to, if (lt_sh(&cfg, timestamp) || lt_sh(&cfg, counts)) gettimeofday(&tv, NULL); + if (lt_sh(&cfg, global_symbols)) + sym = lt_symbol_get(cfg.sh, ptr, symname); + #ifdef CONFIG_ARCH_HAVE_ARGS argret = lt_sh(&cfg, args_enabled) ? - lt_args_sym_exit(cfg.sh, (char*) symname, + lt_args_sym_exit(cfg.sh, sym, (La_regs*) inregs, outregs, &argbuf, &argdbuf) : -1; #endif @@ -225,7 +234,7 @@ unsigned int la_objopen(struct link_map *l, Lmid_t a, uintptr_t *cookie) return 0; } -static unsigned int la_symbind(const char *symname) +static unsigned int la_symbind(ElfW(Sym) *sym, const char *symname) { unsigned int flags = 0; @@ -240,6 +249,10 @@ static unsigned int la_symbind(const char *symname) flags = LA_SYMB_NOPLTENTER|LA_SYMB_NOPLTEXIT; } + /* we are interested in this symbol */ + if (!(flags & LA_SYMB_NOPLTENTER)) + lt_symbol_bind(cfg.sh, (void*) sym->st_value, symname); + return flags; } @@ -267,19 +280,21 @@ unsigned int la_objclose(uintptr_t *__cookie) return 0; } +#if __ELF_NATIVE_CLASS == 32 uintptr_t la_symbind32(Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook, uintptr_t *defcook, unsigned int *flags, const char *symname) { - *flags = la_symbind(symname); + *flags = la_symbind(sym, symname); return sym->st_value; } - +#elif __ELF_NATIVE_CLASS == 64 uintptr_t la_symbind64(Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook, uintptr_t *defcook, unsigned int *flags, const char *symname) { - *flags = la_symbind(symname); + *flags = la_symbind(sym, symname); return sym->st_value; } +#endif ElfW(Addr) pltenter(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook, @@ -295,8 +310,10 @@ pltenter(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook, CHECK_PID(sym->st_value); - sym_entry(symname, lr ? lr->l_name : NULL, - ld ? ld->l_name : NULL, regs); + sym_entry(symname, (void*) sym->st_value, + lr ? lr->l_name : NULL, + ld ? ld->l_name : NULL, + regs); } while(0); @@ -318,8 +335,10 @@ unsigned int pltexit(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook, CHECK_PID(0); - sym_exit(symname, lr ? lr->l_name : NULL, - ld ? ld->l_name : NULL, inregs, outregs); + sym_exit(symname, (void*) sym->st_value, + lr ? lr->l_name : NULL, + ld ? ld->l_name : NULL, + inregs, outregs); } while(0); diff --git a/src/config.h b/src/config.h index 29c9c4f..c7f88ee 100644 --- a/src/config.h +++ b/src/config.h @@ -118,6 +118,7 @@ struct lt_config_shared { int not_follow_fork; int framesize_check; unsigned int framesize; + int global_symbols; /* for 'not_follow_fork' */ pid_t pid; @@ -260,6 +261,15 @@ struct lt_thread { struct lt_thread *next; }; +struct lt_symbol { + struct lt_args_sym *args; + + /* symbol name */ + const char *name; + /* symbol address */ + void *ptr; +}; + /* ctl */ int main_ctl(int argc, char **argv); @@ -307,6 +317,12 @@ char* lt_objsearch(struct lt_config_audit *cfg, const char *name, /* stack */ int lt_stack_framesize(struct lt_config_audit *cfg, La_regs *regs); +/* symbol */ +struct lt_symbol* lt_symbol_bind(struct lt_config_shared *cfg, + void *ptr, const char *name); +struct lt_symbol* lt_symbol_get(struct lt_config_shared *cfg, + void *ptr, const char *name); + #define PRINT(fmt, args...) \ do { \ char lpbuf[1024]; \ @@ -316,6 +332,7 @@ do { \ __LINE__, \ fmt); \ printf(lpbuf, ## args); \ + fflush(NULL); \ } while(0) #define PRINT_VERBOSE(cfg, cond, fmt, args...) \ diff --git a/src/symbol.c b/src/symbol.c new file mode 100644 index 0000000..c4e25b9 --- /dev/null +++ b/src/symbol.c @@ -0,0 +1,154 @@ +/* + Copyright (C) 2011 Jiri Olsa + + This file is part of the latrace. + + The latrace is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The latrace 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the latrace (file COPYING). If not, see + . +*/ + + +#include +#include +#include + +#include "config.h" + +static void *root = NULL; +extern struct lt_config_audit cfg; + +static int compare(const void *a, const void *b) +{ + const struct lt_symbol *sym_a = a; + const struct lt_symbol *sym_b = b; + + PRINT_VERBOSE(&cfg, 1, "a %p, b %p\n", sym_a, sym_b); + PRINT_VERBOSE(&cfg, 1, "a ptr %p, b ptr %p\n", + sym_a ? sym_a->ptr : NULL, + sym_b ? sym_b->ptr : NULL); + PRINT_VERBOSE(&cfg, 1, "a name %s, b name %s\n", + sym_a ? sym_a->name : "", + sym_b ? sym_b->name: ""); + + if (!sym_a || !sym_b) + return 1; + +/* XXX There's a glibc bug/feature causing the symbol to + * have different value in plt entry/exit.. so using name + * check instead.. so far.. ;) */ + +#define SEARCH_NAME +#undef SEARCH_PTR + +#ifdef SEARCH_NAME + return strcmp(sym_a->name, sym_b->name); +#endif + +#if SEARCH_PTR + PRINT_VERBOSE(&cfg, 1, "%s(%p) %s(%p)\n", + sym_a->name, sym_a->ptr, + sym_b->name, sym_b->ptr); + + if (sym_a->ptr > sym_b->ptr) + return 1; + + if (sym_a->ptr < sym_b->ptr) + return -1; + + return 0; +#endif +} + +static int symbol_init(struct lt_config_shared *cfg, + struct lt_symbol *sym, const char *name) +{ + struct lt_args_sym* a; + + if (lt_sh(cfg, args_enabled)) { + a = lt_args_sym_get(cfg, name); + if (!a) + return -1; + } + + sym->args = a; + + PRINT_VERBOSE(cfg, 1, "ok name %s, ptr %p, sym %p\n", + name, sym->ptr, sym); + return 0; +} + +struct lt_symbol* lt_symbol_bind(struct lt_config_shared *cfg, + void *ptr, const char *name) +{ + static struct lt_symbol *sym = NULL; + struct lt_symbol *s = NULL; + void *val; + + if (!sym) { + sym = malloc(sizeof(*sym)); + if (!sym) + return NULL; + } + + bzero(sym, sizeof(*sym)); + sym->ptr = ptr; + sym->name = name; + + PRINT_VERBOSE(cfg, 1, "checking %s(%p)\n", name, ptr); + + val = tsearch((void *) sym, &root, compare); + if (!val) + return NULL; + + s = (*(void**) val); + + /* symbol already in */ + if (s != sym) { + PRINT_VERBOSE(cfg, 1, "found %s, ptr %p, sym %p\n", + name, sym->ptr, sym); + return s; + } + + PRINT_VERBOSE(cfg, 1, "added %s, ptr %p, sym %p\n", + name, sym->ptr, sym); + + /* not interesting symbol */ + if (symbol_init(cfg, sym, name)) + return NULL; + + /* symbol properly added */ + sym = NULL; + return s; +} + +struct lt_symbol* lt_symbol_get(struct lt_config_shared *cfg, + void *ptr, const char *name) +{ + void *val; + struct lt_symbol *s; + struct lt_symbol sym = { + .ptr = ptr, + .name = name, + }; + + val = tfind(&sym, &root, compare); + + if (!val) + s = NULL; + else + s = *(struct lt_symbol**) val; + + PRINT_VERBOSE(cfg, 1, "found %p '%s'\n", s, s ? s->name : ""); + return s; +} -- cgit