aboutsummaryrefslogtreecommitdiff
path: root/contrib/libexecinfo/symtab.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/libexecinfo/symtab.c')
-rw-r--r--contrib/libexecinfo/symtab.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/contrib/libexecinfo/symtab.c b/contrib/libexecinfo/symtab.c
new file mode 100644
index 000000000000..92d92dc20bc5
--- /dev/null
+++ b/contrib/libexecinfo/symtab.c
@@ -0,0 +1,193 @@
+/* $NetBSD: symtab.c,v 1.2 2013/08/29 15:01:57 christos Exp $ */
+
+/*-
+ * Copyright (c) 2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: symtab.c,v 1.2 2013/08/29 15:01:57 christos Exp $");
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <err.h>
+#include <dlfcn.h>
+
+#include <libelf.h>
+#include <gelf.h>
+#ifndef ELF_ST_BIND
+#define ELF_ST_BIND(x) ((x) >> 4)
+#endif
+#ifndef ELF_ST_TYPE
+#define ELF_ST_TYPE(x) (((unsigned int)x) & 0xf)
+#endif
+
+
+#include "symtab.h"
+
+struct symbol {
+ char *st_name;
+ uintptr_t st_value;
+ uintptr_t st_info;
+};
+
+struct symtab {
+ size_t nsymbols;
+ struct symbol *symbols;
+};
+
+static int
+address_compare(const void *a, const void *b)
+{
+ const struct symbol *sa = a;
+ const struct symbol *sb = b;
+ return (int)(intmax_t)(sa->st_value - sb->st_value);
+}
+
+void
+symtab_destroy(symtab_t *s)
+{
+ if (s == NULL)
+ return;
+ for (size_t i = 0; i < s->nsymbols; i++)
+ free(s->symbols[i].st_name);
+ free(s->symbols);
+ free(s);
+}
+
+symtab_t *
+symtab_create(int fd, int bind, int type)
+{
+ Elf *elf;
+ symtab_t *st;
+ Elf_Scn *scn = NULL;
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ warnx("Elf Library is out of date.");
+ return NULL;
+ }
+
+ elf = elf_begin(fd, ELF_C_READ, NULL);
+ if (elf == NULL) {
+ warnx("Error opening elf file: %s", elf_errmsg(elf_errno()));
+ return NULL;
+ }
+ st = calloc(1, sizeof(*st));
+ if (st == NULL) {
+ warnx("Error allocating symbol table");
+ elf_end(elf);
+ return NULL;
+ }
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ GElf_Shdr shdr;
+ Elf_Data *edata;
+ size_t ns;
+ struct symbol *s;
+
+ gelf_getshdr(scn, &shdr);
+ if(shdr.sh_type != SHT_SYMTAB)
+ continue;
+
+ edata = elf_getdata(scn, NULL);
+ ns = shdr.sh_size / shdr.sh_entsize;
+ s = calloc(ns, sizeof(*s));
+ if (s == NULL) {
+ warn("Cannot allocate %zu symbols", ns);
+ goto out;
+ }
+ st->symbols = s;
+
+ for (size_t i = 0; i < ns; i++) {
+ GElf_Sym sym;
+ gelf_getsym(edata, (int)i, &sym);
+
+ if (bind != -1 &&
+ (unsigned)bind != ELF_ST_BIND(sym.st_info))
+ continue;
+
+ if (type != -1 &&
+ (unsigned)type != ELF_ST_TYPE(sym.st_info))
+ continue;
+
+ s->st_value = sym.st_value;
+ s->st_info = sym.st_info;
+ s->st_name = strdup(
+ elf_strptr(elf, shdr.sh_link, sym.st_name));
+ if (s->st_name == NULL)
+ goto out;
+ s++;
+ }
+ st->nsymbols = s - st->symbols;
+ if (st->nsymbols == 0) {
+ warnx("No symbols found");
+ goto out;
+ }
+ qsort(st->symbols, st->nsymbols, sizeof(*st->symbols),
+ address_compare);
+ elf_end(elf);
+ return st;
+ }
+out:
+ symtab_destroy(st);
+ elf_end(elf);
+ return NULL;
+}
+
+
+int
+symtab_find(const symtab_t *st, const void *p, Dl_info *dli)
+{
+ struct symbol *s = st->symbols;
+ size_t ns = st->nsymbols;
+ size_t hi = ns;
+ size_t lo = 0;
+ size_t mid = ns / 2;
+ uintptr_t dd, sd, me = (uintptr_t)p;
+
+ for (;;) {
+ if (s[mid].st_value < me)
+ lo = mid;
+ else if (s[mid].st_value > me)
+ hi = mid;
+ else
+ break;
+ if (hi - lo == 1) {
+ mid = lo;
+ break;
+ }
+ mid = (hi + lo) / 2;
+ }
+ dd = me - (uintptr_t)dli->dli_saddr;
+ sd = me - s[mid].st_value;
+ if (dd > sd) {
+ dli->dli_saddr = (void *)s[mid].st_value;
+ dli->dli_sname = s[mid].st_name;
+ }
+ return 1;
+}