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