1 /* $NetBSD: symtab.c,v 1.2 2013/08/29 15:01:57 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2012 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Christos Zoulas. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 #include <sys/cdefs.h> 32 #include <stdlib.h> 33 #include <stdio.h> 34 #include <string.h> 35 #include <stdint.h> 36 #include <err.h> 37 #include <dlfcn.h> 38 39 #include <libelf.h> 40 #include <gelf.h> 41 #ifndef ELF_ST_BIND 42 #define ELF_ST_BIND(x) ((x) >> 4) 43 #endif 44 #ifndef ELF_ST_TYPE 45 #define ELF_ST_TYPE(x) (((unsigned int)x) & 0xf) 46 #endif 47 48 49 #include "symtab.h" 50 51 struct symbol { 52 char *st_name; 53 uintptr_t st_value; 54 uintptr_t st_info; 55 }; 56 57 struct symtab { 58 size_t nsymbols; 59 struct symbol *symbols; 60 }; 61 62 static int 63 address_compare(const void *a, const void *b) 64 { 65 const struct symbol *sa = a; 66 const struct symbol *sb = b; 67 return (int)(intmax_t)(sa->st_value - sb->st_value); 68 } 69 70 void 71 symtab_destroy(symtab_t *s) 72 { 73 if (s == NULL) 74 return; 75 for (size_t i = 0; i < s->nsymbols; i++) 76 free(s->symbols[i].st_name); 77 free(s->symbols); 78 free(s); 79 } 80 81 symtab_t * 82 symtab_create(int fd, int bind, int type) 83 { 84 Elf *elf; 85 symtab_t *st; 86 Elf_Scn *scn = NULL; 87 88 if (elf_version(EV_CURRENT) == EV_NONE) { 89 warnx("Elf Library is out of date."); 90 return NULL; 91 } 92 93 elf = elf_begin(fd, ELF_C_READ, NULL); 94 if (elf == NULL) { 95 warnx("Error opening elf file: %s", elf_errmsg(elf_errno())); 96 return NULL; 97 } 98 st = calloc(1, sizeof(*st)); 99 if (st == NULL) { 100 warnx("Error allocating symbol table"); 101 elf_end(elf); 102 return NULL; 103 } 104 105 while ((scn = elf_nextscn(elf, scn)) != NULL) { 106 GElf_Shdr shdr; 107 Elf_Data *edata; 108 size_t ns; 109 struct symbol *s; 110 111 gelf_getshdr(scn, &shdr); 112 if(shdr.sh_type != SHT_SYMTAB) 113 continue; 114 115 edata = elf_getdata(scn, NULL); 116 ns = shdr.sh_size / shdr.sh_entsize; 117 s = calloc(ns, sizeof(*s)); 118 if (s == NULL) { 119 warn("Cannot allocate %zu symbols", ns); 120 goto out; 121 } 122 st->symbols = s; 123 124 for (size_t i = 0; i < ns; i++) { 125 GElf_Sym sym; 126 gelf_getsym(edata, (int)i, &sym); 127 128 if (bind != -1 && 129 (unsigned)bind != ELF_ST_BIND(sym.st_info)) 130 continue; 131 132 if (type != -1 && 133 (unsigned)type != ELF_ST_TYPE(sym.st_info)) 134 continue; 135 136 s->st_value = sym.st_value; 137 s->st_info = sym.st_info; 138 s->st_name = strdup( 139 elf_strptr(elf, shdr.sh_link, sym.st_name)); 140 if (s->st_name == NULL) 141 goto out; 142 s++; 143 } 144 st->nsymbols = s - st->symbols; 145 if (st->nsymbols == 0) { 146 warnx("No symbols found"); 147 goto out; 148 } 149 qsort(st->symbols, st->nsymbols, sizeof(*st->symbols), 150 address_compare); 151 elf_end(elf); 152 return st; 153 } 154 out: 155 symtab_destroy(st); 156 elf_end(elf); 157 return NULL; 158 } 159 160 161 int 162 symtab_find(const symtab_t *st, const void *p, Dl_info *dli) 163 { 164 struct symbol *s = st->symbols; 165 size_t ns = st->nsymbols; 166 size_t hi = ns; 167 size_t lo = 0; 168 size_t mid = ns / 2; 169 uintptr_t dd, sd, me = (uintptr_t)p; 170 171 for (;;) { 172 if (s[mid].st_value < me) 173 lo = mid; 174 else if (s[mid].st_value > me) 175 hi = mid; 176 else 177 break; 178 if (hi - lo == 1) { 179 mid = lo; 180 break; 181 } 182 mid = (hi + lo) / 2; 183 } 184 dd = me - (uintptr_t)dli->dli_saddr; 185 sd = me - s[mid].st_value; 186 if (dd > sd) { 187 dli->dli_saddr = (void *)s[mid].st_value; 188 dli->dli_sname = s[mid].st_name; 189 } 190 return 1; 191 } 192