summaryrefslogtreecommitdiff
path: root/src/lookup_kas.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lookup_kas.c')
-rw-r--r--src/lookup_kas.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/src/lookup_kas.c b/src/lookup_kas.c
new file mode 100644
index 0000000..09fad98
--- /dev/null
+++ b/src/lookup_kas.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2009, Neil Horman <nhorman@redhat.com>
+ *
+ * This program file 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; version 2 of the License.
+ *
+ * This program 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 this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+/*
+ * This is a translator. given an input address, this will convert it into a
+ * symbolic name using /proc/kallsyms
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/utsname.h>
+#include <bfd.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+
+#include "lookup.h"
+
+struct symbol_entry {
+ char *sym_name;
+ __u64 start;
+ __u64 end;
+ LIST_ENTRY(symbol_entry) list;
+};
+
+LIST_HEAD(sym_list, symbol_entry);
+
+/*
+ * This is our cache of symbols that we've previously looked up
+ */
+static struct sym_list sym_list_head = {NULL};
+
+
+static int lookup_kas_cache( __u64 pc, struct loc_result *location)
+{
+ struct symbol_entry *sym;
+
+ LIST_FOREACH(sym, &sym_list_head, list) {
+ if ((pc >= sym->start) &&
+ (pc <= sym->end)) {
+ location->symbol = sym->sym_name;
+ location->offset = (pc - sym->start);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void kas_add_cache(__u64 start, __u64 end, char *name)
+{
+ struct symbol_entry *sym = NULL;
+
+ sym = malloc(sizeof(struct symbol_entry));
+ if (!sym)
+ return;
+
+ sym->start = start;
+ sym->end = end;
+ sym->sym_name = name;
+
+ LIST_INSERT_HEAD(&sym_list_head, sym, list);
+ return;
+}
+
+static int lookup_kas_proc(__u64 pc, struct loc_result *location)
+{
+ FILE *pf;
+ __u64 ppc;
+ __u64 uppc, ulpc, uipc;
+ char *name, *last_name;
+
+ pf = fopen("/proc/kallsyms", "r");
+
+ if (!pf)
+ return 1;
+
+ last_name = NULL;
+ uipc = pc;
+ ulpc = 0;
+ while (!feof(pf)) {
+ /*
+ * Each line of /proc/kallsyms is formatteded as:
+ * - "%pK %c %s\n" (for kernel internal symbols), or
+ * - "%pK %c %s\t[%s]\n" (for module-provided symbols)
+ */
+ if (fscanf(pf, "%llx %*s %as [ %*[^]] ]", &ppc, &name) < 0) {
+ perror("Error Scanning File: ");
+ break;
+ }
+
+ uppc = (__u64)ppc;
+ if ((uipc >= ulpc) &&
+ (uipc < uppc)) {
+ /*
+ * The last symbol we looked at
+ * was a hit, record and return it
+ * Note that we don't free last_name
+ * here, because the cache is using it
+ */
+ kas_add_cache(ulpc, uppc-1, last_name);
+ fclose(pf);
+ free(name);
+ return lookup_kas_cache(pc, location);
+ }
+
+ /*
+ * Advance all our state holders
+ */
+ free(last_name);
+ last_name = name;
+ ulpc = uppc;
+ }
+
+ fclose(pf);
+ return 1;
+}
+
+static int lookup_kas_init(void)
+{
+ printf("Initalizing kallsyms db\n");
+
+ return 0;
+}
+
+
+static int lookup_kas_sym(void *pc, struct loc_result *location)
+{
+ __u64 pcv;
+
+ memset(&pcv, 0, sizeof(__u64));
+
+ memcpy(&pcv, &pc, sizeof(void *));
+
+ if (!lookup_kas_cache(pcv, location))
+ return 0;
+
+ return lookup_kas_proc(pcv, location);
+}
+
+struct lookup_methods kallsym_methods = {
+ lookup_kas_init,
+ lookup_kas_sym,
+};
+