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