1/* $NetBSD: symtab.c,v 1.10 2023/08/23 12:24:59 rin 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__RCSID("$NetBSD: symtab.c,v 1.10 2023/08/23 12:24:59 rin Exp $"); 33 34#include <stdlib.h> 35#include <stdio.h> 36#include <string.h> 37#include <stdint.h> 38#include <stdbool.h> 39#include <err.h> 40#include <dlfcn.h> 41 42#include <libelf.h> 43#include <gelf.h> 44#ifndef ELF_ST_BIND 45#define ELF_ST_BIND(x) ((x) >> 4) 46#endif 47#ifndef ELF_ST_TYPE 48#define ELF_ST_TYPE(x) (((unsigned int)x) & 0xf) 49#endif 50 51#include "symbol.h" 52#include "symtab.h" 53 54#ifdef SYMTAB_DEBUG 55#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt "\n", __func__, __VA_ARGS__) 56#else 57#define DPRINTF(fmt, ...) 58#endif 59 60struct symbol { 61 char *st_name; 62 uintptr_t st_value; 63 uintptr_t st_info; 64}; 65 66struct symtab { 67 size_t nsymbols; 68 struct symbol *symbols; 69 bool ispie; 70}; 71 72static int 73address_compare(const void *a, const void *b) 74{ 75 const struct symbol *sa = a; 76 const struct symbol *sb = b; 77 return (int)(intmax_t)(sa->st_value - sb->st_value); 78} 79 80void 81symtab_destroy(symtab_t *s) 82{ 83 if (s == NULL) 84 return; 85 for (size_t i = 0; i < s->nsymbols; i++) 86 free(s->symbols[i].st_name); 87 free(s->symbols); 88 free(s); 89} 90 91symtab_t * 92symtab_create(int fd, int bind, int type) 93{ 94 Elf *elf; 95 symtab_t *st; 96 Elf_Scn *scn = NULL; 97 GElf_Ehdr ehdr; 98 99 if (elf_version(EV_CURRENT) == EV_NONE) { 100 warnx("Elf Library is out of date."); 101 return NULL; 102 } 103 104 elf = elf_begin(fd, ELF_C_READ, NULL); 105 if (elf == NULL) { 106 warnx("Error opening elf file: %s", elf_errmsg(elf_errno())); 107 return NULL; 108 } 109 st = calloc(1, sizeof(*st)); 110 if (st == NULL) { 111 warnx("Error allocating symbol table"); 112 elf_end(elf); 113 return NULL; 114 } 115 if (gelf_getehdr(elf, &ehdr) == NULL) { 116 warnx("Error getting ELF Ehdr"); 117 elf_end(elf); 118 return NULL; 119 } 120 121 st->ispie = ehdr.e_type == ET_DYN; 122 123 while ((scn = elf_nextscn(elf, scn)) != NULL) { 124 GElf_Shdr shdr; 125 Elf_Data *edata; 126 size_t ns; 127 struct symbol *s; 128 129 gelf_getshdr(scn, &shdr); 130 if(shdr.sh_type != SHT_SYMTAB) 131 continue; 132 133 edata = elf_getdata(scn, NULL); 134 ns = shdr.sh_size / shdr.sh_entsize; 135 s = calloc(ns, sizeof(*s)); 136 if (s == NULL) { 137 warn("Cannot allocate %zu symbols", ns); 138 goto out; 139 } 140 st->symbols = s; 141 142 for (size_t i = 0; i < ns; i++) { 143 GElf_Sym sym; 144 gelf_getsym(edata, (int)i, &sym); 145 146 DPRINTF("%s@%#jx=%d,%d", 147 elf_strptr(elf, shdr.sh_link, sym.st_name), 148 (uintmax_t)sym.st_value, ELF_ST_BIND(sym.st_info), 149 ELF_ST_TYPE(sym.st_info)); 150 151 if (bind != -1 && 152 (unsigned)bind != ELF_ST_BIND(sym.st_info)) 153 continue; 154 155 if (type != -1 && 156 (unsigned)type != ELF_ST_TYPE(sym.st_info)) 157 continue; 158 159 s->st_value = sym.st_value; 160 s->st_info = sym.st_info; 161 s->st_name = strdup( 162 elf_strptr(elf, shdr.sh_link, sym.st_name)); 163 if (s->st_name == NULL) { 164 warn("Cannot allocate symbol"); 165 goto out; 166 } 167 s++; 168 } 169 st->nsymbols = s - st->symbols; 170 if (st->nsymbols == 0) { 171 warnx("No symbols found"); 172 goto out; 173 } 174 qsort(st->symbols, st->nsymbols, sizeof(*st->symbols), 175 address_compare); 176 elf_end(elf); 177 return st; 178 } 179out: 180 symtab_destroy(st); 181 elf_end(elf); 182 return NULL; 183} 184 185 186int 187symtab_find(const symtab_t *st, const void *p, Dl_info *dli) 188{ 189 struct symbol *s = st->symbols; 190 size_t ns = st->nsymbols; 191 size_t hi = ns; 192 size_t lo = 0; 193 size_t mid = ns / 2; 194 uintptr_t fbase = st->ispie ? (uintptr_t)dli->dli_fbase : 0; 195 uintptr_t dd, sd, me = (uintptr_t)p - fbase; 196 uintptr_t sa = SYMBOL_CANONICALIZE(dli->dli_saddr); 197 uintptr_t ad = sa - fbase; 198 199 DPRINTF("[fbase=%#jx, saddr=%p, sa=%#jx, me=%#jx ad=%#jx]", 200 (uintmax_t)fbase, dli->dli_saddr, (uintmax_t)sa, 201 (uintmax_t)me, (uintmax_t)ad); 202 203 for (;;) { 204 if (s[mid].st_value < me) 205 lo = mid; 206 else if (s[mid].st_value > me) 207 hi = mid; 208 else 209 break; 210 if (hi - lo == 1) { 211 mid = lo; 212 break; 213 } 214 mid = (hi + lo) / 2; 215 } 216 dd = me - ad; 217 sd = me - s[mid].st_value; 218 if (dd > sd) { 219 dli->dli_saddr = (void *)s[mid].st_value; 220 dli->dli_sname = s[mid].st_name; 221 DPRINTF("me=%#jx -> [%#jx, %s]", (uintmax_t)me, (uintmax_t)sd, 222 dli->dli_sname); 223 } else { 224 DPRINTF("%#jx -> [%#jx, ***]", (uintmax_t)me, (uintmax_t)sd); 225 } 226 227 return 1; 228} 229