xref: /openbmc/linux/scripts/mod/symsearch.c (revision 86aa961bb4619a68077ebeba21c52e9ba0eab43d)
1*2bc92c61SJack Brennen // SPDX-License-Identifier: GPL-2.0
2*2bc92c61SJack Brennen 
3*2bc92c61SJack Brennen /*
4*2bc92c61SJack Brennen  * Helper functions for finding the symbol in an ELF which is "nearest"
5*2bc92c61SJack Brennen  * to a given address.
6*2bc92c61SJack Brennen  */
7*2bc92c61SJack Brennen 
8*2bc92c61SJack Brennen #include "modpost.h"
9*2bc92c61SJack Brennen 
10*2bc92c61SJack Brennen struct syminfo {
11*2bc92c61SJack Brennen 	unsigned int symbol_index;
12*2bc92c61SJack Brennen 	unsigned int section_index;
13*2bc92c61SJack Brennen 	Elf_Addr addr;
14*2bc92c61SJack Brennen };
15*2bc92c61SJack Brennen 
16*2bc92c61SJack Brennen /*
17*2bc92c61SJack Brennen  * Container used to hold an entire binary search table.
18*2bc92c61SJack Brennen  * Entries in table are ascending, sorted first by section_index,
19*2bc92c61SJack Brennen  * then by addr, and last by symbol_index.  The sorting by
20*2bc92c61SJack Brennen  * symbol_index is used to ensure predictable behavior when
21*2bc92c61SJack Brennen  * multiple symbols are present with the same address; all
22*2bc92c61SJack Brennen  * symbols past the first are effectively ignored, by eliding
23*2bc92c61SJack Brennen  * them in symsearch_fixup().
24*2bc92c61SJack Brennen  */
25*2bc92c61SJack Brennen struct symsearch {
26*2bc92c61SJack Brennen 	unsigned int table_size;
27*2bc92c61SJack Brennen 	struct syminfo table[];
28*2bc92c61SJack Brennen };
29*2bc92c61SJack Brennen 
syminfo_compare(const void * s1,const void * s2)30*2bc92c61SJack Brennen static int syminfo_compare(const void *s1, const void *s2)
31*2bc92c61SJack Brennen {
32*2bc92c61SJack Brennen 	const struct syminfo *sym1 = s1;
33*2bc92c61SJack Brennen 	const struct syminfo *sym2 = s2;
34*2bc92c61SJack Brennen 
35*2bc92c61SJack Brennen 	if (sym1->section_index > sym2->section_index)
36*2bc92c61SJack Brennen 		return 1;
37*2bc92c61SJack Brennen 	if (sym1->section_index < sym2->section_index)
38*2bc92c61SJack Brennen 		return -1;
39*2bc92c61SJack Brennen 	if (sym1->addr > sym2->addr)
40*2bc92c61SJack Brennen 		return 1;
41*2bc92c61SJack Brennen 	if (sym1->addr < sym2->addr)
42*2bc92c61SJack Brennen 		return -1;
43*2bc92c61SJack Brennen 	if (sym1->symbol_index > sym2->symbol_index)
44*2bc92c61SJack Brennen 		return 1;
45*2bc92c61SJack Brennen 	if (sym1->symbol_index < sym2->symbol_index)
46*2bc92c61SJack Brennen 		return -1;
47*2bc92c61SJack Brennen 	return 0;
48*2bc92c61SJack Brennen }
49*2bc92c61SJack Brennen 
symbol_count(struct elf_info * elf)50*2bc92c61SJack Brennen static unsigned int symbol_count(struct elf_info *elf)
51*2bc92c61SJack Brennen {
52*2bc92c61SJack Brennen 	unsigned int result = 0;
53*2bc92c61SJack Brennen 
54*2bc92c61SJack Brennen 	for (Elf_Sym *sym = elf->symtab_start; sym < elf->symtab_stop; sym++) {
55*2bc92c61SJack Brennen 		if (is_valid_name(elf, sym))
56*2bc92c61SJack Brennen 			result++;
57*2bc92c61SJack Brennen 	}
58*2bc92c61SJack Brennen 	return result;
59*2bc92c61SJack Brennen }
60*2bc92c61SJack Brennen 
61*2bc92c61SJack Brennen /*
62*2bc92c61SJack Brennen  * Populate the search array that we just allocated.
63*2bc92c61SJack Brennen  * Be slightly paranoid here.  The ELF file is mmap'd and could
64*2bc92c61SJack Brennen  * conceivably change between symbol_count() and symsearch_populate().
65*2bc92c61SJack Brennen  * If we notice any difference, bail out rather than potentially
66*2bc92c61SJack Brennen  * propagating errors or crashing.
67*2bc92c61SJack Brennen  */
symsearch_populate(struct elf_info * elf,struct syminfo * table,unsigned int table_size)68*2bc92c61SJack Brennen static void symsearch_populate(struct elf_info *elf,
69*2bc92c61SJack Brennen 			       struct syminfo *table,
70*2bc92c61SJack Brennen 			       unsigned int table_size)
71*2bc92c61SJack Brennen {
72*2bc92c61SJack Brennen 	bool is_arm = (elf->hdr->e_machine == EM_ARM);
73*2bc92c61SJack Brennen 
74*2bc92c61SJack Brennen 	for (Elf_Sym *sym = elf->symtab_start; sym < elf->symtab_stop; sym++) {
75*2bc92c61SJack Brennen 		if (is_valid_name(elf, sym)) {
76*2bc92c61SJack Brennen 			if (table_size-- == 0)
77*2bc92c61SJack Brennen 				fatal("%s: size mismatch\n", __func__);
78*2bc92c61SJack Brennen 			table->symbol_index = sym - elf->symtab_start;
79*2bc92c61SJack Brennen 			table->section_index = get_secindex(elf, sym);
80*2bc92c61SJack Brennen 			table->addr = sym->st_value;
81*2bc92c61SJack Brennen 
82*2bc92c61SJack Brennen 			/*
83*2bc92c61SJack Brennen 			 * For ARM Thumb instruction, the bit 0 of st_value is
84*2bc92c61SJack Brennen 			 * set if the symbol is STT_FUNC type. Mask it to get
85*2bc92c61SJack Brennen 			 * the address.
86*2bc92c61SJack Brennen 			 */
87*2bc92c61SJack Brennen 			if (is_arm && ELF_ST_TYPE(sym->st_info) == STT_FUNC)
88*2bc92c61SJack Brennen 				table->addr &= ~1;
89*2bc92c61SJack Brennen 
90*2bc92c61SJack Brennen 			table++;
91*2bc92c61SJack Brennen 		}
92*2bc92c61SJack Brennen 	}
93*2bc92c61SJack Brennen 
94*2bc92c61SJack Brennen 	if (table_size != 0)
95*2bc92c61SJack Brennen 		fatal("%s: size mismatch\n", __func__);
96*2bc92c61SJack Brennen }
97*2bc92c61SJack Brennen 
98*2bc92c61SJack Brennen /*
99*2bc92c61SJack Brennen  * Do any fixups on the table after sorting.
100*2bc92c61SJack Brennen  * For now, this just finds adjacent entries which have
101*2bc92c61SJack Brennen  * the same section_index and addr, and it propagates
102*2bc92c61SJack Brennen  * the first symbol_index over the subsequent entries,
103*2bc92c61SJack Brennen  * so that only one symbol_index is seen for any given
104*2bc92c61SJack Brennen  * section_index and addr.  This ensures that whether
105*2bc92c61SJack Brennen  * we're looking at an address from "above" or "below"
106*2bc92c61SJack Brennen  * that we see the same symbol_index.
107*2bc92c61SJack Brennen  * This does leave some duplicate entries in the table;
108*2bc92c61SJack Brennen  * in practice, these are a small fraction of the
109*2bc92c61SJack Brennen  * total number of entries, and they are harmless to
110*2bc92c61SJack Brennen  * the binary search algorithm other than a few occasional
111*2bc92c61SJack Brennen  * unnecessary comparisons.
112*2bc92c61SJack Brennen  */
symsearch_fixup(struct syminfo * table,unsigned int table_size)113*2bc92c61SJack Brennen static void symsearch_fixup(struct syminfo *table, unsigned int table_size)
114*2bc92c61SJack Brennen {
115*2bc92c61SJack Brennen 	/* Don't look at index 0, it will never change. */
116*2bc92c61SJack Brennen 	for (unsigned int i = 1; i < table_size; i++) {
117*2bc92c61SJack Brennen 		if (table[i].addr == table[i - 1].addr &&
118*2bc92c61SJack Brennen 		    table[i].section_index == table[i - 1].section_index) {
119*2bc92c61SJack Brennen 			table[i].symbol_index = table[i - 1].symbol_index;
120*2bc92c61SJack Brennen 		}
121*2bc92c61SJack Brennen 	}
122*2bc92c61SJack Brennen }
123*2bc92c61SJack Brennen 
symsearch_init(struct elf_info * elf)124*2bc92c61SJack Brennen void symsearch_init(struct elf_info *elf)
125*2bc92c61SJack Brennen {
126*2bc92c61SJack Brennen 	unsigned int table_size = symbol_count(elf);
127*2bc92c61SJack Brennen 
128*2bc92c61SJack Brennen 	elf->symsearch = NOFAIL(malloc(sizeof(struct symsearch) +
129*2bc92c61SJack Brennen 				       sizeof(struct syminfo) * table_size));
130*2bc92c61SJack Brennen 	elf->symsearch->table_size = table_size;
131*2bc92c61SJack Brennen 
132*2bc92c61SJack Brennen 	symsearch_populate(elf, elf->symsearch->table, table_size);
133*2bc92c61SJack Brennen 	qsort(elf->symsearch->table, table_size,
134*2bc92c61SJack Brennen 	      sizeof(struct syminfo), syminfo_compare);
135*2bc92c61SJack Brennen 
136*2bc92c61SJack Brennen 	symsearch_fixup(elf->symsearch->table, table_size);
137*2bc92c61SJack Brennen }
138*2bc92c61SJack Brennen 
symsearch_finish(struct elf_info * elf)139*2bc92c61SJack Brennen void symsearch_finish(struct elf_info *elf)
140*2bc92c61SJack Brennen {
141*2bc92c61SJack Brennen 	free(elf->symsearch);
142*2bc92c61SJack Brennen 	elf->symsearch = NULL;
143*2bc92c61SJack Brennen }
144*2bc92c61SJack Brennen 
145*2bc92c61SJack Brennen /*
146*2bc92c61SJack Brennen  * Find the syminfo which is in secndx and "nearest" to addr.
147*2bc92c61SJack Brennen  * allow_negative: allow returning a symbol whose address is > addr.
148*2bc92c61SJack Brennen  * min_distance: ignore symbols which are further away than this.
149*2bc92c61SJack Brennen  *
150*2bc92c61SJack Brennen  * Returns a pointer into the symbol table for success.
151*2bc92c61SJack Brennen  * Returns NULL if no legal symbol is found within the requested range.
152*2bc92c61SJack Brennen  */
symsearch_find_nearest(struct elf_info * elf,Elf_Addr addr,unsigned int secndx,bool allow_negative,Elf_Addr min_distance)153*2bc92c61SJack Brennen Elf_Sym *symsearch_find_nearest(struct elf_info *elf, Elf_Addr addr,
154*2bc92c61SJack Brennen 				unsigned int secndx, bool allow_negative,
155*2bc92c61SJack Brennen 				Elf_Addr min_distance)
156*2bc92c61SJack Brennen {
157*2bc92c61SJack Brennen 	unsigned int hi = elf->symsearch->table_size;
158*2bc92c61SJack Brennen 	unsigned int lo = 0;
159*2bc92c61SJack Brennen 	struct syminfo *table = elf->symsearch->table;
160*2bc92c61SJack Brennen 	struct syminfo target;
161*2bc92c61SJack Brennen 
162*2bc92c61SJack Brennen 	target.addr = addr;
163*2bc92c61SJack Brennen 	target.section_index = secndx;
164*2bc92c61SJack Brennen 	target.symbol_index = ~0;  /* compares greater than any actual index */
165*2bc92c61SJack Brennen 	while (hi > lo) {
166*2bc92c61SJack Brennen 		unsigned int mid = lo + (hi - lo) / 2;  /* Avoids overflow */
167*2bc92c61SJack Brennen 
168*2bc92c61SJack Brennen 		if (syminfo_compare(&table[mid], &target) > 0)
169*2bc92c61SJack Brennen 			hi = mid;
170*2bc92c61SJack Brennen 		else
171*2bc92c61SJack Brennen 			lo = mid + 1;
172*2bc92c61SJack Brennen 	}
173*2bc92c61SJack Brennen 
174*2bc92c61SJack Brennen 	/*
175*2bc92c61SJack Brennen 	 * table[hi], if it exists, is the first entry in the array which
176*2bc92c61SJack Brennen 	 * lies beyond target.  table[hi - 1], if it exists, is the last
177*2bc92c61SJack Brennen 	 * entry in the array which comes before target, including the
178*2bc92c61SJack Brennen 	 * case where it perfectly matches the section and the address.
179*2bc92c61SJack Brennen 	 *
180*2bc92c61SJack Brennen 	 * Note -- if the address we're looking up falls perfectly
181*2bc92c61SJack Brennen 	 * in the middle of two symbols, this is written to always
182*2bc92c61SJack Brennen 	 * prefer the symbol with the lower address.
183*2bc92c61SJack Brennen 	 */
184*2bc92c61SJack Brennen 	Elf_Sym *result = NULL;
185*2bc92c61SJack Brennen 
186*2bc92c61SJack Brennen 	if (allow_negative &&
187*2bc92c61SJack Brennen 	    hi < elf->symsearch->table_size &&
188*2bc92c61SJack Brennen 	    table[hi].section_index == secndx &&
189*2bc92c61SJack Brennen 	    table[hi].addr - addr <= min_distance) {
190*2bc92c61SJack Brennen 		min_distance = table[hi].addr - addr;
191*2bc92c61SJack Brennen 		result = &elf->symtab_start[table[hi].symbol_index];
192*2bc92c61SJack Brennen 	}
193*2bc92c61SJack Brennen 	if (hi > 0 &&
194*2bc92c61SJack Brennen 	    table[hi - 1].section_index == secndx &&
195*2bc92c61SJack Brennen 	    addr - table[hi - 1].addr <= min_distance) {
196*2bc92c61SJack Brennen 		result = &elf->symtab_start[table[hi - 1].symbol_index];
197*2bc92c61SJack Brennen 	}
198*2bc92c61SJack Brennen 	return result;
199*2bc92c61SJack Brennen }
200