This will allow to don't hardcoding addresses of functions in ktap scripts, that use uprobes, but instead, use normal function names. Before this patch: $ ktap -e 'trace probe:/path/to/bin:0xDEADBEAF' # instead some real address After this path: $ ktap -e 'trace probe:/path/to/bin:foo' Works also for ret probes (%return) Requires libelf. Signed-off-by: Azat Khuzhin <a3at.mail@xxxxxxxxx> --- v2: make it more pretty v3: make it faster (thanks to Yicheng) Makefile | 5 ++- include/ktap_types.h | 2 + userspace/eventdef.c | 43 ++++++++++++++++++- userspace/symbol.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++ userspace/symbol.h | 32 +++++++++++++++ 5 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 userspace/symbol.c create mode 100644 userspace/symbol.h diff --git a/Makefile b/Makefile index 372b41a..344c82a 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,8 @@ $(UDIR)/tstring.o: $(INTP)/tstring.c $(INC)/* $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< $(UDIR)/object.o: $(INTP)/object.c $(INC)/* $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< +$(UDIR)/symbol.o: $(UDIR)/symbol.c $(INC)/* + $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< KTAPOBJS = KTAPOBJS += $(UDIR)/lex.o @@ -73,9 +75,10 @@ KTAPOBJS += $(UDIR)/opcode.o KTAPOBJS += $(UDIR)/table.o KTAPOBJS += $(UDIR)/tstring.o KTAPOBJS += $(UDIR)/object.o +KTAPOBJS += $(UDIR)/symbol.o ktap: $(KTAPOBJS) - $(QUIET_LINK)$(CC) $(KTAPC_CFLAGS) -o $@ $(KTAPOBJS) -lpthread + $(QUIET_LINK)$(CC) $(KTAPC_CFLAGS) -o $@ $(KTAPOBJS) -lpthread -lelf KMISC := /lib/modules/$(KVERSION)/ktapvm/ diff --git a/include/ktap_types.h b/include/ktap_types.h index 1dae9d8..4e19f0b 100644 --- a/include/ktap_types.h +++ b/include/ktap_types.h @@ -600,5 +600,7 @@ extern ktap_global_state dummy_global_state; #define KTAP_QL(x) "'" x "'" #define KTAP_QS KTAP_QL("%s") +#define STRINGIFY(type) #type + #endif /* __KTAP_TYPES_H__ */ diff --git a/userspace/eventdef.c b/userspace/eventdef.c index 76a68ac..280202d 100644 --- a/userspace/eventdef.c +++ b/userspace/eventdef.c @@ -29,6 +29,7 @@ #include "../include/ktap_types.h" #include "../include/ktap_opcodes.h" #include "ktapc.h" +#include "symbol.h" static char tracing_events_path[] = "/sys/kernel/debug/tracing/events"; @@ -305,6 +306,42 @@ static int parse_events_add_kprobe(char *old_event) #define UPROBE_EVENTS_PATH "/sys/kernel/debug/tracing/uprobe_events" +static char* parse_events_resolve_symbol(char *event, char *r) +{ + char *colon = strchr(event, ':'); + vaddr_t symbol_address = strtol(colon + 1 /* skip ":" */, NULL, 0); + + char *binary, *symbol; + + /** + * We already have address, no need in resolving. + */ + if (symbol_address) { + return event; + } + + binary = strndup(event, colon - event); + if (r) { + symbol = strndup(colon + 1 /* skip ":" */, + r - 1 /* skip "%" */ - colon); + } else { + symbol = strdup(colon + 1); + } + + if ((symbol_address = find_symbol(binary, symbol))) { + verbose_printf("symbol %s resolved to 0x%lx\n", + event, symbol_address); + + event = realloc(event, strlen(event) + (strlen(STRINGIFY(ULONG_MAX)))); + sprintf(event, "%s:0x%lx", binary, symbol_address); + } + + free(binary); + free(symbol); + + return event; +} + static int parse_events_add_uprobe(char *old_event) { static int event_seq = 0; @@ -326,6 +363,10 @@ static int parse_events_add_uprobe(char *old_event) r = strstr(event, "%return"); if (r) { memset(r, ' ', 7); + } + event = parse_events_resolve_symbol(event, r); + + if (r) { snprintf(probe_event, 128, "r:uprobes/kp%d %s", event_seq, event); } else @@ -541,7 +582,7 @@ ktap_string *ktapc_parse_eventdef(ktap_string *eventdef) goto parse_next_eventdef; ts = ktapc_ts_new(g_idstr); - free(g_idstr); + free(g_idstr); return ts; error: diff --git a/userspace/symbol.c b/userspace/symbol.c new file mode 100644 index 0000000..23333b8 --- /dev/null +++ b/userspace/symbol.c @@ -0,0 +1,114 @@ +/* + * symbol.c + * + * This file is part of ktap by Jovi Zhangwei. + * + * Copyright (C) 2013 Azat Khuzhin <a3at.mail@xxxxxxxxx>. + * + * ktap is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * ktap is distributed in the hope 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 + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "symbol.h" + +#include <stdio.h> +#include <stdlib.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> + +#include <libelf.h> + + +/** + * @return v_addr of "LOAD" program header, that have zero offset. + */ +static vaddr_t find_load_address(Elf *elf) +{ + GElf_Phdr header; + size_t headers; + vaddr_t address = 0; + + elf_getphdrnum(elf, &headers); + while (headers-- > 0) { + gelf_getphdr(elf, headers, &header); + if (header.p_type != PT_LOAD || header.p_offset != 0) + continue; + + address = header.p_vaddr; + break; + } + + return address; +} + +static vaddr_t search_symbol(Elf *elf, const char *symbol) +{ + Elf_Data *elf_data = NULL; + Elf_Scn *scn = NULL; + GElf_Sym sym; + GElf_Shdr shdr; + + vaddr_t load_address = find_load_address(elf); + + if (!load_address) + return 0; + + while ((scn = elf_nextscn(elf, scn))) { + int i, symbols; + char *current_symbol; + + gelf_getshdr(scn, &shdr); + + if (shdr.sh_type != SHT_SYMTAB) + continue; + + elf_data = elf_getdata(scn, elf_data); + symbols = shdr.sh_size / shdr.sh_entsize; + + for (i = 0; i < symbols; i++) { + gelf_getsym(elf_data, i, &sym); + + if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) + continue; + + current_symbol = elf_strptr(elf, shdr.sh_link, sym.st_name); + if (!strcmp(current_symbol, symbol)) { + return (sym.st_value - load_address); + } + } + } + + return 0; +} + +vaddr_t find_symbol(const char *exec, const char *symbol) +{ + Elf *elf = NULL; + int fd = 0; + vaddr_t vaddr = 0; + + if (((fd = open(exec, O_RDONLY)) != -1) && + (elf_version(EV_CURRENT) != EV_NONE) && + (elf = elf_begin(fd, ELF_C_READ, NULL))) + vaddr = search_symbol(elf, symbol); + + if (elf) + elf_end(elf); + if (fd) + close(fd); + return vaddr; +} diff --git a/userspace/symbol.h b/userspace/symbol.h new file mode 100644 index 0000000..478ad2e --- /dev/null +++ b/userspace/symbol.h @@ -0,0 +1,32 @@ +/* + * symbol.h + * + * This file is part of ktap by Jovi Zhangwei. + * + * Copyright (C) 2013 Azat Khuzhin <a3at.mail@xxxxxxxxx>. + * + * ktap is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * ktap is distributed in the hope 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 + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <gelf.h> // GElf_Addr + + +typedef GElf_Addr vaddr_t; + +/** + * Find symbol in DSO + * + * @return 0 on failure, otherwise address of symbol. + */ +vaddr_t find_symbol(const char *exec, const char *symbol); -- 1.8.4.rc3