[ktap] [PATCH] add new symbol module to search symbols in DSO (for uprobes)

  • From: Azat Khuzhin <a3at.mail@xxxxxxxxx>
  • To: ktap@xxxxxxxxxxxxx
  • Date: Wed, 6 Nov 2013 13:51:02 +0400

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.

Have NO_LIBELF option for makefile, for cross-builds.

Signed-off-by: Azat Khuzhin <a3at.mail@xxxxxxxxx>
---
v2: make it more pretty
v3: make it faster (thanks to Yicheng)
v4: add NO_LIBELF option into Makefile, and more common parsing
v5: fix leaking of "tail"
v6: coding styles fixes

 Makefile             |  21 ++++++++-
 include/ktap_types.h |   2 +
 userspace/eventdef.c |  54 ++++++++++++++++++++++-
 userspace/symbol.c   | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++
 userspace/symbol.h   |  32 ++++++++++++++
 5 files changed, 226 insertions(+), 2 deletions(-)
 create mode 100644 userspace/symbol.c
 create mode 100644 userspace/symbol.h

diff --git a/Makefile b/Makefile
index 372b41a..86aa5ee 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,9 @@
 
+#
+# Define NO_LIBELF if you do not want libelf dependency (e.g. cross-builds)
+# (this will also disable resolve resolving symbols in DSO functionality)
+#
+
 # Do not instrument the tracer itself:
 ifdef CONFIG_FUNCTION_TRACER
 ORIG_CFLAGS := $(KBUILD_CFLAGS)
@@ -12,6 +17,8 @@ INTP = interpreter
 
 LIBDIR = $(INTP)/library
 
+KTAP_LIBS = -lpthread
+
 LIB_OBJS += $(LIBDIR)/baselib.o $(LIBDIR)/kdebug.o $(LIBDIR)/timer.o \
                $(LIBDIR)/ansilib.o
 
@@ -32,6 +39,11 @@ modules_install:
        $(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules_install
 
 KTAPC_CFLAGS = -Wall -O2
+ifdef NO_LIBELF
+       KTAPC_CFLAGS += -DNO_LIBELF
+else
+       KTAP_LIBS += -lelf
+endif
 
 UDIR = userspace
 
@@ -59,6 +71,10 @@ $(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 $<
+ifndef NO_LIBELF
+$(UDIR)/symbol.o: $(UDIR)/symbol.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+endif
 
 KTAPOBJS =
 KTAPOBJS += $(UDIR)/lex.o
@@ -73,9 +89,12 @@ KTAPOBJS += $(UDIR)/opcode.o
 KTAPOBJS += $(UDIR)/table.o
 KTAPOBJS += $(UDIR)/tstring.o
 KTAPOBJS += $(UDIR)/object.o
+ifndef NO_LIBELF
+       KTAPOBJS += $(UDIR)/symbol.o
+endif
 
 ktap: $(KTAPOBJS)
-       $(QUIET_LINK)$(CC) $(KTAPC_CFLAGS) -o $@ $(KTAPOBJS) -lpthread
+       $(QUIET_LINK)$(CC) $(KTAPC_CFLAGS) -o $@ $(KTAPOBJS) $(KTAP_LIBS)
 
 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..5ffb1fa 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,51 @@ static int parse_events_add_kprobe(char *old_event)
 
 #define UPROBE_EVENTS_PATH "/sys/kernel/debug/tracing/uprobe_events"
 
+#ifndef NO_LIBELF
+static char *parse_events_resolve_symbol(char *event)
+{
+       char *colon = strchr(event, ':');
+       vaddr_t symbol_address = strtol(colon + 1 /* skip ":" */, NULL, 0);
+
+       char *end, *tail, *binary, *symbol;
+
+       /**
+        * We already have address, no need in resolving.
+        */
+       if (symbol_address)
+               return event;
+
+       binary = strndup(event, colon - event);
+
+       end = strpbrk(event, "% ");
+       if (end) {
+               tail = strdup(end);
+               symbol = strndup(colon + 1 /* skip ":" */, end - 1 - colon);
+       } else {
+               tail = NULL;
+               symbol = strdup(colon + 1 /* skip ":" */);
+       }
+
+       symbol_address = find_symbol(binary, symbol);
+       if (symbol_address) {
+               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%s",
+                       binary, symbol_address, tail ?: "");
+       }
+
+       free(binary);
+       free(symbol);
+       if (tail)
+               free(tail);
+
+       return event;
+}
+#endif
+
 static int parse_events_add_uprobe(char *old_event)
 {
        static int event_seq = 0;
@@ -323,9 +369,15 @@ static int parse_events_add_uprobe(char *old_event)
        }
 
        event = strdup(old_event);
+#ifndef NO_LIBELF
+       event = parse_events_resolve_symbol(event);
+#endif
        r = strstr(event, "%return");
        if (r) {
                memset(r, ' ', 7);
+       }
+
+       if (r) {
                snprintf(probe_event, 128, "r:uprobes/kp%d %s",
                                event_seq, event);
        } else
@@ -541,7 +593,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..2e83281
--- /dev/null
+++ b/userspace/symbol.c
@@ -0,0 +1,119 @@
+/*
+ * 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)
+{
+       vaddr_t vaddr = 0;
+       Elf *elf;
+       int fd;
+
+       if (elf_version(EV_CURRENT) == EV_NONE)
+               return vaddr;
+
+       fd = open(exec, O_RDONLY);
+       if (fd < 0)
+               return vaddr;
+
+       elf = elf_begin(fd, ELF_C_READ, NULL);
+       if (elf) {
+               vaddr = search_symbol(elf, symbol);
+               elf_end(elf);
+       }
+
+       close(fd);
+       return vaddr;
+}
+
diff --git a/userspace/symbol.h b/userspace/symbol.h
new file mode 100644
index 0000000..e5c0908
--- /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>
+
+
+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


Other related posts: