Commit | Line | Data |
---|---|---|
1ccea77e | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
442f04c3 JP |
2 | /* |
3 | * elf.c - ELF access library | |
4 | * | |
5 | * Adapted from kpatch (https://github.com/dynup/kpatch): | |
6 | * Copyright (C) 2013-2015 Josh Poimboeuf <jpoimboe@redhat.com> | |
7 | * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com> | |
442f04c3 JP |
8 | */ |
9 | ||
10 | #include <sys/types.h> | |
11 | #include <sys/stat.h> | |
25cf0d8a | 12 | #include <sys/mman.h> |
442f04c3 JP |
13 | #include <fcntl.h> |
14 | #include <stdio.h> | |
15 | #include <stdlib.h> | |
16 | #include <string.h> | |
17 | #include <unistd.h> | |
385d11b1 | 18 | #include <errno.h> |
5da6aea3 | 19 | #include <linux/interval_tree_generic.h> |
7786032e | 20 | #include <objtool/builtin.h> |
442f04c3 | 21 | |
7786032e VG |
22 | #include <objtool/elf.h> |
23 | #include <objtool/warn.h> | |
442f04c3 | 24 | |
22566c16 AS |
25 | #define MAX_NAME_LEN 128 |
26 | ||
ae358196 PZ |
27 | static inline u32 str_hash(const char *str) |
28 | { | |
29 | return jhash(str, strlen(str), 0); | |
30 | } | |
31 | ||
25cf0d8a PZ |
32 | #define __elf_table(name) (elf->name##_hash) |
33 | #define __elf_bits(name) (elf->name##_bits) | |
34f7c96d | 34 | |
25cf0d8a PZ |
35 | #define elf_hash_add(name, node, key) \ |
36 | hlist_add_head(node, &__elf_table(name)[hash_min(key, __elf_bits(name))]) | |
34f7c96d | 37 | |
25cf0d8a PZ |
38 | #define elf_hash_for_each_possible(name, obj, member, key) \ |
39 | hlist_for_each_entry(obj, &__elf_table(name)[hash_min(key, __elf_bits(name))], member) | |
34f7c96d | 40 | |
25cf0d8a PZ |
41 | #define elf_alloc_hash(name, size) \ |
42 | ({ \ | |
43 | __elf_bits(name) = max(10, ilog2(size)); \ | |
44 | __elf_table(name) = mmap(NULL, sizeof(struct hlist_head) << __elf_bits(name), \ | |
45 | PROT_READ|PROT_WRITE, \ | |
46 | MAP_PRIVATE|MAP_ANON, -1, 0); \ | |
47 | if (__elf_table(name) == (void *)-1L) { \ | |
48 | WARN("mmap fail " #name); \ | |
49 | __elf_table(name) = NULL; \ | |
50 | } \ | |
51 | __elf_table(name); \ | |
52 | }) | |
34f7c96d | 53 | |
5da6aea3 | 54 | static inline unsigned long __sym_start(struct symbol *s) |
2a362ecc | 55 | { |
5da6aea3 | 56 | return s->offset; |
2a362ecc PZ |
57 | } |
58 | ||
5da6aea3 | 59 | static inline unsigned long __sym_last(struct symbol *s) |
2a362ecc | 60 | { |
5da6aea3 PZ |
61 | return s->offset + s->len - 1; |
62 | } | |
2a362ecc | 63 | |
5da6aea3 PZ |
64 | INTERVAL_TREE_DEFINE(struct symbol, node, unsigned long, __subtree_last, |
65 | __sym_start, __sym_last, static, __sym) | |
2a362ecc | 66 | |
5da6aea3 PZ |
67 | #define __sym_for_each(_iter, _tree, _start, _end) \ |
68 | for (_iter = __sym_iter_first((_tree), (_start), (_end)); \ | |
69 | _iter; _iter = __sym_iter_next(_iter, (_start), (_end))) | |
2a362ecc | 70 | |
4adb2368 PZ |
71 | struct symbol_hole { |
72 | unsigned long key; | |
73 | const struct symbol *sym; | |
74 | }; | |
75 | ||
76 | /* | |
77 | * Find !section symbol where @offset is after it. | |
78 | */ | |
79 | static int symbol_hole_by_offset(const void *key, const struct rb_node *node) | |
80 | { | |
81 | const struct symbol *s = rb_entry(node, struct symbol, node); | |
82 | struct symbol_hole *sh = (void *)key; | |
83 | ||
84 | if (sh->key < s->offset) | |
85 | return -1; | |
86 | ||
87 | if (sh->key >= s->offset + s->len) { | |
88 | if (s->type != STT_SECTION) | |
89 | sh->sym = s; | |
90 | return 1; | |
91 | } | |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
894e48ca | 96 | struct section *find_section_by_name(const struct elf *elf, const char *name) |
442f04c3 JP |
97 | { |
98 | struct section *sec; | |
99 | ||
25cf0d8a | 100 | elf_hash_for_each_possible(section_name, sec, name_hash, str_hash(name)) { |
442f04c3 JP |
101 | if (!strcmp(sec->name, name)) |
102 | return sec; | |
25cf0d8a | 103 | } |
442f04c3 JP |
104 | |
105 | return NULL; | |
106 | } | |
107 | ||
108 | static struct section *find_section_by_index(struct elf *elf, | |
109 | unsigned int idx) | |
110 | { | |
111 | struct section *sec; | |
112 | ||
25cf0d8a | 113 | elf_hash_for_each_possible(section, sec, hash, idx) { |
442f04c3 JP |
114 | if (sec->idx == idx) |
115 | return sec; | |
25cf0d8a | 116 | } |
442f04c3 JP |
117 | |
118 | return NULL; | |
119 | } | |
120 | ||
121 | static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) | |
122 | { | |
442f04c3 JP |
123 | struct symbol *sym; |
124 | ||
25cf0d8a | 125 | elf_hash_for_each_possible(symbol, sym, hash, idx) { |
65fb11a7 PZ |
126 | if (sym->idx == idx) |
127 | return sym; | |
25cf0d8a | 128 | } |
442f04c3 JP |
129 | |
130 | return NULL; | |
131 | } | |
132 | ||
133 | struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) | |
134 | { | |
5da6aea3 PZ |
135 | struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; |
136 | struct symbol *iter; | |
2a362ecc | 137 | |
5da6aea3 PZ |
138 | __sym_for_each(iter, tree, offset, offset) { |
139 | if (iter->offset == offset && iter->type != STT_SECTION) | |
140 | return iter; | |
2a362ecc | 141 | } |
7acfe531 JP |
142 | |
143 | return NULL; | |
144 | } | |
145 | ||
146 | struct symbol *find_func_by_offset(struct section *sec, unsigned long offset) | |
147 | { | |
5da6aea3 PZ |
148 | struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; |
149 | struct symbol *iter; | |
7acfe531 | 150 | |
5da6aea3 PZ |
151 | __sym_for_each(iter, tree, offset, offset) { |
152 | if (iter->offset == offset && iter->type == STT_FUNC) | |
153 | return iter; | |
2a362ecc | 154 | } |
442f04c3 JP |
155 | |
156 | return NULL; | |
157 | } | |
158 | ||
b490f453 | 159 | struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset) |
13810435 | 160 | { |
5da6aea3 PZ |
161 | struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; |
162 | struct symbol *iter; | |
2a362ecc | 163 | |
5da6aea3 PZ |
164 | __sym_for_each(iter, tree, offset, offset) { |
165 | if (iter->type != STT_SECTION) | |
166 | return iter; | |
2a362ecc | 167 | } |
13810435 JP |
168 | |
169 | return NULL; | |
170 | } | |
171 | ||
4adb2368 PZ |
172 | /* |
173 | * Returns size of hole starting at @offset. | |
174 | */ | |
175 | int find_symbol_hole_containing(const struct section *sec, unsigned long offset) | |
176 | { | |
177 | struct symbol_hole hole = { | |
178 | .key = offset, | |
179 | .sym = NULL, | |
180 | }; | |
181 | struct rb_node *n; | |
182 | struct symbol *s; | |
183 | ||
184 | /* | |
185 | * Find the rightmost symbol for which @offset is after it. | |
186 | */ | |
5da6aea3 | 187 | n = rb_find(&hole, &sec->symbol_tree.rb_root, symbol_hole_by_offset); |
4adb2368 PZ |
188 | |
189 | /* found a symbol that contains @offset */ | |
190 | if (n) | |
191 | return 0; /* not a hole */ | |
192 | ||
193 | /* didn't find a symbol for which @offset is after it */ | |
194 | if (!hole.sym) | |
195 | return 0; /* not a hole */ | |
196 | ||
197 | /* @offset >= sym->offset + sym->len, find symbol after it */ | |
198 | n = rb_next(&hole.sym->node); | |
199 | if (!n) | |
200 | return -1; /* until end of address space */ | |
201 | ||
202 | /* hole until start of next symbol */ | |
203 | s = rb_entry(n, struct symbol, node); | |
204 | return s->offset - offset; | |
205 | } | |
206 | ||
53d20720 | 207 | struct symbol *find_func_containing(struct section *sec, unsigned long offset) |
2a362ecc | 208 | { |
5da6aea3 PZ |
209 | struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; |
210 | struct symbol *iter; | |
2a362ecc | 211 | |
5da6aea3 PZ |
212 | __sym_for_each(iter, tree, offset, offset) { |
213 | if (iter->type == STT_FUNC) | |
214 | return iter; | |
2a362ecc PZ |
215 | } |
216 | ||
217 | return NULL; | |
218 | } | |
219 | ||
894e48ca | 220 | struct symbol *find_symbol_by_name(const struct elf *elf, const char *name) |
5c51f4ae JP |
221 | { |
222 | struct symbol *sym; | |
223 | ||
25cf0d8a | 224 | elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) { |
cdb3d057 PZ |
225 | if (!strcmp(sym->name, name)) |
226 | return sym; | |
25cf0d8a | 227 | } |
5c51f4ae JP |
228 | |
229 | return NULL; | |
230 | } | |
231 | ||
f1974222 | 232 | struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, |
8b5fa6bc | 233 | unsigned long offset, unsigned int len) |
442f04c3 | 234 | { |
f1974222 | 235 | struct reloc *reloc, *r = NULL; |
a5bd6236 | 236 | struct section *rsec; |
042ba73f | 237 | unsigned long o; |
442f04c3 | 238 | |
a5bd6236 JP |
239 | rsec = sec->rsec; |
240 | if (!rsec) | |
442f04c3 JP |
241 | return NULL; |
242 | ||
74b873e4 | 243 | for_offset_range(o, offset, offset + len) { |
25cf0d8a | 244 | elf_hash_for_each_possible(reloc, reloc, hash, |
a5bd6236 JP |
245 | sec_offset_hash(rsec, o)) { |
246 | if (reloc->sec != rsec) | |
74b873e4 PZ |
247 | continue; |
248 | ||
e4cbb9b8 JP |
249 | if (reloc_offset(reloc) >= offset && |
250 | reloc_offset(reloc) < offset + len) { | |
251 | if (!r || reloc_offset(reloc) < reloc_offset(r)) | |
f1974222 | 252 | r = reloc; |
74b873e4 | 253 | } |
8b5fa6bc | 254 | } |
74b873e4 PZ |
255 | if (r) |
256 | return r; | |
8b5fa6bc | 257 | } |
442f04c3 JP |
258 | |
259 | return NULL; | |
260 | } | |
261 | ||
f1974222 | 262 | struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset) |
442f04c3 | 263 | { |
f1974222 | 264 | return find_reloc_by_dest_range(elf, sec, offset, 1); |
442f04c3 JP |
265 | } |
266 | ||
442f04c3 JP |
267 | static int read_sections(struct elf *elf) |
268 | { | |
269 | Elf_Scn *s = NULL; | |
270 | struct section *sec; | |
271 | size_t shstrndx, sections_nr; | |
272 | int i; | |
273 | ||
274 | if (elf_getshdrnum(elf->elf, §ions_nr)) { | |
baa41469 | 275 | WARN_ELF("elf_getshdrnum"); |
442f04c3 JP |
276 | return -1; |
277 | } | |
278 | ||
279 | if (elf_getshdrstrndx(elf->elf, &shstrndx)) { | |
baa41469 | 280 | WARN_ELF("elf_getshdrstrndx"); |
442f04c3 JP |
281 | return -1; |
282 | } | |
283 | ||
25cf0d8a PZ |
284 | if (!elf_alloc_hash(section, sections_nr) || |
285 | !elf_alloc_hash(section_name, sections_nr)) | |
286 | return -1; | |
287 | ||
8045b8f0 TW |
288 | elf->section_data = calloc(sections_nr, sizeof(*sec)); |
289 | if (!elf->section_data) { | |
290 | perror("calloc"); | |
291 | return -1; | |
292 | } | |
442f04c3 | 293 | for (i = 0; i < sections_nr; i++) { |
8045b8f0 | 294 | sec = &elf->section_data[i]; |
442f04c3 | 295 | |
a196e171 | 296 | INIT_LIST_HEAD(&sec->symbol_list); |
442f04c3 | 297 | |
442f04c3 JP |
298 | s = elf_getscn(elf->elf, i); |
299 | if (!s) { | |
baa41469 | 300 | WARN_ELF("elf_getscn"); |
442f04c3 JP |
301 | return -1; |
302 | } | |
303 | ||
304 | sec->idx = elf_ndxscn(s); | |
305 | ||
306 | if (!gelf_getshdr(s, &sec->sh)) { | |
baa41469 | 307 | WARN_ELF("gelf_getshdr"); |
442f04c3 JP |
308 | return -1; |
309 | } | |
310 | ||
311 | sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name); | |
312 | if (!sec->name) { | |
baa41469 | 313 | WARN_ELF("elf_strptr"); |
442f04c3 JP |
314 | return -1; |
315 | } | |
316 | ||
df968c93 PV |
317 | if (sec->sh.sh_size != 0) { |
318 | sec->data = elf_getdata(s, NULL); | |
319 | if (!sec->data) { | |
320 | WARN_ELF("elf_getdata"); | |
321 | return -1; | |
322 | } | |
323 | if (sec->data->d_off != 0 || | |
324 | sec->data->d_size != sec->sh.sh_size) { | |
325 | WARN("unexpected data attributes for %s", | |
326 | sec->name); | |
327 | return -1; | |
328 | } | |
442f04c3 | 329 | } |
53038996 PZ |
330 | |
331 | list_add_tail(&sec->list, &elf->sections); | |
25cf0d8a PZ |
332 | elf_hash_add(section, &sec->hash, sec->idx); |
333 | elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); | |
eb0481bb JP |
334 | |
335 | if (is_reloc_sec(sec)) | |
ebcef730 | 336 | elf->num_relocs += sec_num_entries(sec); |
442f04c3 JP |
337 | } |
338 | ||
2daf7fab | 339 | if (opts.stats) { |
1e11f3fd | 340 | printf("nr_sections: %lu\n", (unsigned long)sections_nr); |
25cf0d8a PZ |
341 | printf("section_bits: %d\n", elf->section_bits); |
342 | } | |
1e11f3fd | 343 | |
442f04c3 JP |
344 | /* sanity check, one more call to elf_nextscn() should return NULL */ |
345 | if (elf_nextscn(elf->elf, s)) { | |
346 | WARN("section entry mismatch"); | |
347 | return -1; | |
348 | } | |
349 | ||
350 | return 0; | |
351 | } | |
352 | ||
9a7827b7 PZ |
353 | static void elf_add_symbol(struct elf *elf, struct symbol *sym) |
354 | { | |
355 | struct list_head *entry; | |
356 | struct rb_node *pnode; | |
5da6aea3 | 357 | struct symbol *iter; |
9a7827b7 | 358 | |
19526717 | 359 | INIT_LIST_HEAD(&sym->reloc_list); |
ead165fa PZ |
360 | INIT_LIST_HEAD(&sym->pv_target); |
361 | sym->alias = sym; | |
362 | ||
9a7827b7 PZ |
363 | sym->type = GELF_ST_TYPE(sym->sym.st_info); |
364 | sym->bind = GELF_ST_BIND(sym->sym.st_info); | |
365 | ||
753da417 JP |
366 | if (sym->type == STT_FILE) |
367 | elf->num_files++; | |
368 | ||
9a7827b7 PZ |
369 | sym->offset = sym->sym.st_value; |
370 | sym->len = sym->sym.st_size; | |
371 | ||
5da6aea3 PZ |
372 | __sym_for_each(iter, &sym->sec->symbol_tree, sym->offset, sym->offset) { |
373 | if (iter->offset == sym->offset && iter->type == sym->type) | |
374 | iter->alias = sym; | |
375 | } | |
376 | ||
377 | __sym_insert(sym, &sym->sec->symbol_tree); | |
9a7827b7 PZ |
378 | pnode = rb_prev(&sym->node); |
379 | if (pnode) | |
380 | entry = &rb_entry(pnode, struct symbol, node)->list; | |
381 | else | |
382 | entry = &sym->sec->symbol_list; | |
383 | list_add(&sym->list, entry); | |
25cf0d8a PZ |
384 | elf_hash_add(symbol, &sym->hash, sym->idx); |
385 | elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name)); | |
9a7827b7 PZ |
386 | |
387 | /* | |
388 | * Don't store empty STT_NOTYPE symbols in the rbtree. They | |
389 | * can exist within a function, confusing the sorting. | |
390 | */ | |
391 | if (!sym->len) | |
5da6aea3 | 392 | __sym_remove(sym, &sym->sec->symbol_tree); |
9a7827b7 PZ |
393 | } |
394 | ||
442f04c3 JP |
395 | static int read_symbols(struct elf *elf) |
396 | { | |
28fe1d7b | 397 | struct section *symtab, *symtab_shndx, *sec; |
2a362ecc | 398 | struct symbol *sym, *pfunc; |
442f04c3 | 399 | int symbols_nr, i; |
13810435 | 400 | char *coldstr; |
28fe1d7b ST |
401 | Elf_Data *shndx_data = NULL; |
402 | Elf32_Word shndx; | |
442f04c3 JP |
403 | |
404 | symtab = find_section_by_name(elf, ".symtab"); | |
25cf0d8a PZ |
405 | if (symtab) { |
406 | symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); | |
407 | if (symtab_shndx) | |
408 | shndx_data = symtab_shndx->data; | |
409 | ||
ebcef730 | 410 | symbols_nr = sec_num_entries(symtab); |
25cf0d8a | 411 | } else { |
1d489151 JP |
412 | /* |
413 | * A missing symbol table is actually possible if it's an empty | |
25cf0d8a PZ |
414 | * .o file. This can happen for thunk_64.o. Make sure to at |
415 | * least allocate the symbol hash tables so we can do symbol | |
416 | * lookups without crashing. | |
1d489151 | 417 | */ |
25cf0d8a | 418 | symbols_nr = 0; |
442f04c3 JP |
419 | } |
420 | ||
25cf0d8a PZ |
421 | if (!elf_alloc_hash(symbol, symbols_nr) || |
422 | !elf_alloc_hash(symbol_name, symbols_nr)) | |
423 | return -1; | |
442f04c3 | 424 | |
8045b8f0 TW |
425 | elf->symbol_data = calloc(symbols_nr, sizeof(*sym)); |
426 | if (!elf->symbol_data) { | |
427 | perror("calloc"); | |
428 | return -1; | |
429 | } | |
442f04c3 | 430 | for (i = 0; i < symbols_nr; i++) { |
8045b8f0 | 431 | sym = &elf->symbol_data[i]; |
442f04c3 JP |
432 | |
433 | sym->idx = i; | |
434 | ||
28fe1d7b ST |
435 | if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym, |
436 | &shndx)) { | |
437 | WARN_ELF("gelf_getsymshndx"); | |
442f04c3 JP |
438 | goto err; |
439 | } | |
440 | ||
441 | sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, | |
442 | sym->sym.st_name); | |
443 | if (!sym->name) { | |
baa41469 | 444 | WARN_ELF("elf_strptr"); |
442f04c3 JP |
445 | goto err; |
446 | } | |
447 | ||
28fe1d7b ST |
448 | if ((sym->sym.st_shndx > SHN_UNDEF && |
449 | sym->sym.st_shndx < SHN_LORESERVE) || | |
450 | (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) { | |
451 | if (sym->sym.st_shndx != SHN_XINDEX) | |
452 | shndx = sym->sym.st_shndx; | |
453 | ||
454 | sym->sec = find_section_by_index(elf, shndx); | |
442f04c3 JP |
455 | if (!sym->sec) { |
456 | WARN("couldn't find section for symbol %s", | |
457 | sym->name); | |
458 | goto err; | |
459 | } | |
9a7827b7 | 460 | if (GELF_ST_TYPE(sym->sym.st_info) == STT_SECTION) { |
442f04c3 JP |
461 | sym->name = sym->sec->name; |
462 | sym->sec->sym = sym; | |
463 | } | |
464 | } else | |
465 | sym->sec = find_section_by_index(elf, 0); | |
466 | ||
9a7827b7 | 467 | elf_add_symbol(elf, sym); |
442f04c3 JP |
468 | } |
469 | ||
2daf7fab | 470 | if (opts.stats) { |
1e11f3fd | 471 | printf("nr_symbols: %lu\n", (unsigned long)symbols_nr); |
25cf0d8a PZ |
472 | printf("symbol_bits: %d\n", elf->symbol_bits); |
473 | } | |
1e11f3fd | 474 | |
13810435 JP |
475 | /* Create parent/child links for any cold subfunctions */ |
476 | list_for_each_entry(sec, &elf->sections, list) { | |
9290e772 | 477 | sec_for_each_sym(sec, sym) { |
22566c16 AS |
478 | char pname[MAX_NAME_LEN + 1]; |
479 | size_t pnamelen; | |
13810435 JP |
480 | if (sym->type != STT_FUNC) |
481 | continue; | |
e000acc1 KCA |
482 | |
483 | if (sym->pfunc == NULL) | |
484 | sym->pfunc = sym; | |
485 | ||
486 | if (sym->cfunc == NULL) | |
487 | sym->cfunc = sym; | |
488 | ||
bcb6fb5d | 489 | coldstr = strstr(sym->name, ".cold"); |
08b393d0 JP |
490 | if (!coldstr) |
491 | continue; | |
492 | ||
22566c16 AS |
493 | pnamelen = coldstr - sym->name; |
494 | if (pnamelen > MAX_NAME_LEN) { | |
495 | WARN("%s(): parent function name exceeds maximum length of %d characters", | |
496 | sym->name, MAX_NAME_LEN); | |
497 | return -1; | |
498 | } | |
499 | ||
500 | strncpy(pname, sym->name, pnamelen); | |
501 | pname[pnamelen] = '\0'; | |
502 | pfunc = find_symbol_by_name(elf, pname); | |
08b393d0 JP |
503 | |
504 | if (!pfunc) { | |
505 | WARN("%s(): can't find parent function", | |
506 | sym->name); | |
0b9301fb | 507 | return -1; |
08b393d0 JP |
508 | } |
509 | ||
510 | sym->pfunc = pfunc; | |
511 | pfunc->cfunc = sym; | |
512 | ||
513 | /* | |
514 | * Unfortunately, -fnoreorder-functions puts the child | |
515 | * inside the parent. Remove the overlap so we can | |
516 | * have sane assumptions. | |
517 | * | |
518 | * Note that pfunc->len now no longer matches | |
519 | * pfunc->sym.st_size. | |
520 | */ | |
521 | if (sym->sec == pfunc->sec && | |
522 | sym->offset >= pfunc->offset && | |
523 | sym->offset + sym->len == pfunc->offset + pfunc->len) { | |
524 | pfunc->len -= sym->len; | |
13810435 JP |
525 | } |
526 | } | |
527 | } | |
528 | ||
442f04c3 JP |
529 | return 0; |
530 | ||
531 | err: | |
532 | free(sym); | |
533 | return -1; | |
534 | } | |
535 | ||
4abff6d4 | 536 | /* |
fcf93355 | 537 | * @sym's idx has changed. Update the relocs which reference it. |
4abff6d4 | 538 | */ |
fcf93355 | 539 | static int elf_update_sym_relocs(struct elf *elf, struct symbol *sym) |
ef47cc01 | 540 | { |
19526717 | 541 | struct reloc *reloc; |
4abff6d4 | 542 | |
fcf93355 | 543 | list_for_each_entry(reloc, &sym->reloc_list, sym_reloc_entry) { |
fcee899d | 544 | reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc_type(reloc)); |
fcf93355 JP |
545 | if (elf_write_reloc(elf, reloc)) |
546 | return -1; | |
547 | } | |
548 | ||
549 | return 0; | |
4abff6d4 PZ |
550 | } |
551 | ||
552 | /* | |
ead165fa PZ |
553 | * The libelf API is terrible; gelf_update_sym*() takes a data block relative |
554 | * index value, *NOT* the symbol index. As such, iterate the data blocks and | |
555 | * adjust index until it fits. | |
556 | * | |
557 | * If no data block is found, allow adding a new data block provided the index | |
558 | * is only one past the end. | |
4abff6d4 | 559 | */ |
ead165fa PZ |
560 | static int elf_update_symbol(struct elf *elf, struct section *symtab, |
561 | struct section *symtab_shndx, struct symbol *sym) | |
4abff6d4 | 562 | { |
ead165fa PZ |
563 | Elf32_Word shndx = sym->sec ? sym->sec->idx : SHN_UNDEF; |
564 | Elf_Data *symtab_data = NULL, *shndx_data = NULL; | |
565 | Elf64_Xword entsize = symtab->sh.sh_entsize; | |
566 | int max_idx, idx = sym->idx; | |
567 | Elf_Scn *s, *t = NULL; | |
5141d3a0 ST |
568 | bool is_special_shndx = sym->sym.st_shndx >= SHN_LORESERVE && |
569 | sym->sym.st_shndx != SHN_XINDEX; | |
570 | ||
571 | if (is_special_shndx) | |
572 | shndx = sym->sym.st_shndx; | |
4abff6d4 PZ |
573 | |
574 | s = elf_getscn(elf->elf, symtab->idx); | |
575 | if (!s) { | |
576 | WARN_ELF("elf_getscn"); | |
577 | return -1; | |
578 | } | |
579 | ||
ead165fa PZ |
580 | if (symtab_shndx) { |
581 | t = elf_getscn(elf->elf, symtab_shndx->idx); | |
582 | if (!t) { | |
583 | WARN_ELF("elf_getscn"); | |
584 | return -1; | |
585 | } | |
4abff6d4 PZ |
586 | } |
587 | ||
ead165fa PZ |
588 | for (;;) { |
589 | /* get next data descriptor for the relevant sections */ | |
590 | symtab_data = elf_getdata(s, symtab_data); | |
591 | if (t) | |
592 | shndx_data = elf_getdata(t, shndx_data); | |
4abff6d4 | 593 | |
ead165fa PZ |
594 | /* end-of-list */ |
595 | if (!symtab_data) { | |
13f60e80 PZ |
596 | /* |
597 | * Over-allocate to avoid O(n^2) symbol creation | |
598 | * behaviour. The down side is that libelf doesn't | |
599 | * like this; see elf_truncate_section() for the fixup. | |
600 | */ | |
601 | int num = max(1U, sym->idx/3); | |
ead165fa | 602 | void *buf; |
4abff6d4 | 603 | |
ead165fa PZ |
604 | if (idx) { |
605 | /* we don't do holes in symbol tables */ | |
606 | WARN("index out of range"); | |
607 | return -1; | |
608 | } | |
4abff6d4 | 609 | |
ead165fa PZ |
610 | /* if @idx == 0, it's the next contiguous entry, create it */ |
611 | symtab_data = elf_newdata(s); | |
612 | if (t) | |
613 | shndx_data = elf_newdata(t); | |
614 | ||
13f60e80 | 615 | buf = calloc(num, entsize); |
ead165fa PZ |
616 | if (!buf) { |
617 | WARN("malloc"); | |
618 | return -1; | |
619 | } | |
620 | ||
621 | symtab_data->d_buf = buf; | |
13f60e80 | 622 | symtab_data->d_size = num * entsize; |
ead165fa PZ |
623 | symtab_data->d_align = 1; |
624 | symtab_data->d_type = ELF_T_SYM; | |
625 | ||
ff408273 | 626 | mark_sec_changed(elf, symtab, true); |
13f60e80 | 627 | symtab->truncate = true; |
ead165fa PZ |
628 | |
629 | if (t) { | |
13f60e80 PZ |
630 | buf = calloc(num, sizeof(Elf32_Word)); |
631 | if (!buf) { | |
632 | WARN("malloc"); | |
633 | return -1; | |
634 | } | |
635 | ||
636 | shndx_data->d_buf = buf; | |
637 | shndx_data->d_size = num * sizeof(Elf32_Word); | |
ead165fa PZ |
638 | shndx_data->d_align = sizeof(Elf32_Word); |
639 | shndx_data->d_type = ELF_T_WORD; | |
640 | ||
ff408273 | 641 | mark_sec_changed(elf, symtab_shndx, true); |
13f60e80 | 642 | symtab_shndx->truncate = true; |
ead165fa PZ |
643 | } |
644 | ||
645 | break; | |
646 | } | |
647 | ||
648 | /* empty blocks should not happen */ | |
649 | if (!symtab_data->d_size) { | |
650 | WARN("zero size data"); | |
4abff6d4 | 651 | return -1; |
ef47cc01 PZ |
652 | } |
653 | ||
ead165fa PZ |
654 | /* is this the right block? */ |
655 | max_idx = symtab_data->d_size / entsize; | |
656 | if (idx < max_idx) | |
657 | break; | |
658 | ||
659 | /* adjust index and try again */ | |
660 | idx -= max_idx; | |
661 | } | |
662 | ||
663 | /* something went side-ways */ | |
664 | if (idx < 0) { | |
665 | WARN("negative index"); | |
666 | return -1; | |
667 | } | |
668 | ||
669 | /* setup extended section index magic and write the symbol */ | |
5141d3a0 | 670 | if ((shndx >= SHN_UNDEF && shndx < SHN_LORESERVE) || is_special_shndx) { |
ead165fa PZ |
671 | sym->sym.st_shndx = shndx; |
672 | if (!shndx_data) | |
673 | shndx = 0; | |
674 | } else { | |
675 | sym->sym.st_shndx = SHN_XINDEX; | |
4abff6d4 | 676 | if (!shndx_data) { |
ead165fa | 677 | WARN("no .symtab_shndx"); |
ef47cc01 PZ |
678 | return -1; |
679 | } | |
ead165fa | 680 | } |
ef47cc01 | 681 | |
ead165fa PZ |
682 | if (!gelf_update_symshndx(symtab_data, shndx_data, idx, &sym->sym, shndx)) { |
683 | WARN_ELF("gelf_update_symshndx"); | |
684 | return -1; | |
4abff6d4 PZ |
685 | } |
686 | ||
ead165fa | 687 | return 0; |
4abff6d4 PZ |
688 | } |
689 | ||
690 | static struct symbol * | |
4c91be8e | 691 | __elf_create_symbol(struct elf *elf, struct symbol *sym) |
4abff6d4 PZ |
692 | { |
693 | struct section *symtab, *symtab_shndx; | |
ead165fa | 694 | Elf32_Word first_non_local, new_idx; |
4c91be8e | 695 | struct symbol *old; |
4abff6d4 PZ |
696 | |
697 | symtab = find_section_by_name(elf, ".symtab"); | |
698 | if (symtab) { | |
699 | symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); | |
4abff6d4 PZ |
700 | } else { |
701 | WARN("no .symtab"); | |
702 | return NULL; | |
703 | } | |
704 | ||
ebcef730 | 705 | new_idx = sec_num_entries(symtab); |
4abff6d4 | 706 | |
4c91be8e PZ |
707 | if (GELF_ST_BIND(sym->sym.st_info) != STB_LOCAL) |
708 | goto non_local; | |
ead165fa PZ |
709 | |
710 | /* | |
711 | * Move the first global symbol, as per sh_info, into a new, higher | |
712 | * symbol index. This fees up a spot for a new local symbol. | |
713 | */ | |
714 | first_non_local = symtab->sh.sh_info; | |
ead165fa PZ |
715 | old = find_symbol_by_index(elf, first_non_local); |
716 | if (old) { | |
717 | old->idx = new_idx; | |
718 | ||
719 | hlist_del(&old->hash); | |
720 | elf_hash_add(symbol, &old->hash, old->idx); | |
721 | ||
ead165fa PZ |
722 | if (elf_update_symbol(elf, symtab, symtab_shndx, old)) { |
723 | WARN("elf_update_symbol move"); | |
4abff6d4 PZ |
724 | return NULL; |
725 | } | |
ead165fa | 726 | |
fcf93355 JP |
727 | if (elf_update_sym_relocs(elf, old)) |
728 | return NULL; | |
729 | ||
ead165fa | 730 | new_idx = first_non_local; |
4abff6d4 PZ |
731 | } |
732 | ||
4c91be8e PZ |
733 | /* |
734 | * Either way, we will add a LOCAL symbol. | |
735 | */ | |
736 | symtab->sh.sh_info += 1; | |
737 | ||
738 | non_local: | |
ead165fa PZ |
739 | sym->idx = new_idx; |
740 | if (elf_update_symbol(elf, symtab, symtab_shndx, sym)) { | |
741 | WARN("elf_update_symbol"); | |
4abff6d4 PZ |
742 | return NULL; |
743 | } | |
744 | ||
13f60e80 | 745 | symtab->sh.sh_size += symtab->sh.sh_entsize; |
ff408273 | 746 | mark_sec_changed(elf, symtab, true); |
13f60e80 PZ |
747 | |
748 | if (symtab_shndx) { | |
749 | symtab_shndx->sh.sh_size += sizeof(Elf32_Word); | |
ff408273 | 750 | mark_sec_changed(elf, symtab_shndx, true); |
13f60e80 PZ |
751 | } |
752 | ||
4c91be8e PZ |
753 | return sym; |
754 | } | |
755 | ||
756 | static struct symbol * | |
757 | elf_create_section_symbol(struct elf *elf, struct section *sec) | |
758 | { | |
759 | struct symbol *sym = calloc(1, sizeof(*sym)); | |
760 | ||
761 | if (!sym) { | |
762 | perror("malloc"); | |
763 | return NULL; | |
764 | } | |
ead165fa | 765 | |
4c91be8e PZ |
766 | sym->name = sec->name; |
767 | sym->sec = sec; | |
768 | ||
769 | // st_name 0 | |
770 | sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION); | |
771 | // st_other 0 | |
772 | // st_value 0 | |
773 | // st_size 0 | |
774 | ||
775 | sym = __elf_create_symbol(elf, sym); | |
776 | if (sym) | |
777 | elf_add_symbol(elf, sym); | |
4abff6d4 PZ |
778 | |
779 | return sym; | |
780 | } | |
781 | ||
9f2899fe PZ |
782 | static int elf_add_string(struct elf *elf, struct section *strtab, char *str); |
783 | ||
784 | struct symbol * | |
785 | elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size) | |
786 | { | |
787 | struct symbol *sym = calloc(1, sizeof(*sym)); | |
788 | size_t namelen = strlen(orig->name) + sizeof("__pfx_"); | |
789 | char *name = malloc(namelen); | |
790 | ||
791 | if (!sym || !name) { | |
792 | perror("malloc"); | |
793 | return NULL; | |
794 | } | |
795 | ||
796 | snprintf(name, namelen, "__pfx_%s", orig->name); | |
797 | ||
798 | sym->name = name; | |
799 | sym->sec = orig->sec; | |
800 | ||
801 | sym->sym.st_name = elf_add_string(elf, NULL, name); | |
802 | sym->sym.st_info = orig->sym.st_info; | |
803 | sym->sym.st_value = orig->sym.st_value - size; | |
804 | sym->sym.st_size = size; | |
805 | ||
806 | sym = __elf_create_symbol(elf, sym); | |
807 | if (sym) | |
808 | elf_add_symbol(elf, sym); | |
809 | ||
810 | return sym; | |
811 | } | |
812 | ||
6342a20e JP |
813 | static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec, |
814 | unsigned int reloc_idx, | |
815 | unsigned long offset, struct symbol *sym, | |
816 | s64 addend, unsigned int type) | |
817 | { | |
e0a9349b | 818 | struct reloc *reloc, empty = { 0 }; |
6342a20e | 819 | |
ebcef730 JP |
820 | if (reloc_idx >= sec_num_entries(rsec)) { |
821 | WARN("%s: bad reloc_idx %u for %s with %d relocs", | |
822 | __func__, reloc_idx, rsec->name, sec_num_entries(rsec)); | |
6342a20e JP |
823 | return NULL; |
824 | } | |
825 | ||
ebcef730 | 826 | reloc = &rsec->relocs[reloc_idx]; |
e0a9349b JP |
827 | |
828 | if (memcmp(reloc, &empty, sizeof(empty))) { | |
829 | WARN("%s: %s: reloc %d already initialized!", | |
830 | __func__, rsec->name, reloc_idx); | |
6342a20e JP |
831 | return NULL; |
832 | } | |
6342a20e | 833 | |
6342a20e | 834 | reloc->sec = rsec; |
6342a20e | 835 | reloc->sym = sym; |
6342a20e | 836 | |
e4cbb9b8 | 837 | reloc->rel.r_offset = offset; |
fcee899d | 838 | reloc->rel.r_info = GELF_R_INFO(sym->idx, type); |
0696b6e3 | 839 | reloc->rela.r_addend = addend; |
e4cbb9b8 | 840 | |
fcf93355 JP |
841 | if (elf_write_reloc(elf, reloc)) |
842 | return NULL; | |
843 | ||
6342a20e | 844 | list_add_tail(&reloc->sym_reloc_entry, &sym->reloc_list); |
6342a20e JP |
845 | elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); |
846 | ||
6342a20e JP |
847 | return reloc; |
848 | } | |
849 | ||
850 | struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec, | |
851 | unsigned long offset, | |
852 | unsigned int reloc_idx, | |
853 | struct section *insn_sec, | |
854 | unsigned long insn_off) | |
4abff6d4 PZ |
855 | { |
856 | struct symbol *sym = insn_sec->sym; | |
857 | int addend = insn_off; | |
858 | ||
6342a20e JP |
859 | if (!(insn_sec->sh.sh_flags & SHF_EXECINSTR)) { |
860 | WARN("bad call to %s() for data symbol %s", | |
861 | __func__, sym->name); | |
862 | return NULL; | |
863 | } | |
864 | ||
4abff6d4 PZ |
865 | if (!sym) { |
866 | /* | |
867 | * Due to how weak functions work, we must use section based | |
868 | * relocations. Symbol based relocations would result in the | |
869 | * weak and non-weak function annotations being overlaid on the | |
870 | * non-weak function after linking. | |
871 | */ | |
872 | sym = elf_create_section_symbol(elf, insn_sec); | |
873 | if (!sym) | |
6342a20e | 874 | return NULL; |
4abff6d4 PZ |
875 | |
876 | insn_sec->sym = sym; | |
ef47cc01 PZ |
877 | } |
878 | ||
6342a20e JP |
879 | return elf_init_reloc(elf, sec->rsec, reloc_idx, offset, sym, addend, |
880 | elf_text_rela_type(elf)); | |
881 | } | |
882 | ||
883 | struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec, | |
884 | unsigned long offset, | |
885 | unsigned int reloc_idx, | |
886 | struct symbol *sym, | |
887 | s64 addend) | |
888 | { | |
889 | if (sym->sec && (sec->sh.sh_flags & SHF_EXECINSTR)) { | |
890 | WARN("bad call to %s() for text symbol %s", | |
891 | __func__, sym->name); | |
892 | return NULL; | |
893 | } | |
894 | ||
895 | return elf_init_reloc(elf, sec->rsec, reloc_idx, offset, sym, addend, | |
896 | elf_data_rela_type(elf)); | |
34f7c96d PZ |
897 | } |
898 | ||
53257a97 | 899 | static int read_reloc(struct section *rsec, int i, struct reloc *reloc) |
fb414783 | 900 | { |
53257a97 JP |
901 | bool rela = rsec->sh.sh_type == SHT_RELA; |
902 | void *retp; | |
fb414783 | 903 | |
53257a97 JP |
904 | if (rela) |
905 | retp = gelf_getrela(rsec->data, i, &reloc->rela); | |
906 | else | |
907 | retp = gelf_getrel(rsec->data, i, &reloc->rel); | |
908 | ||
909 | if (!retp) { | |
fb414783 MH |
910 | WARN_ELF("gelf_getrela"); |
911 | return -1; | |
912 | } | |
53257a97 | 913 | |
fb414783 MH |
914 | return 0; |
915 | } | |
916 | ||
f1974222 | 917 | static int read_relocs(struct elf *elf) |
442f04c3 | 918 | { |
eb0481bb | 919 | unsigned long nr_reloc, max_reloc = 0; |
a5bd6236 | 920 | struct section *rsec; |
f1974222 | 921 | struct reloc *reloc; |
442f04c3 | 922 | unsigned int symndx; |
19526717 PZ |
923 | struct symbol *sym; |
924 | int i; | |
442f04c3 | 925 | |
eb0481bb | 926 | if (!elf_alloc_hash(reloc, elf->num_relocs)) |
25cf0d8a PZ |
927 | return -1; |
928 | ||
a5bd6236 | 929 | list_for_each_entry(rsec, &elf->sections, list) { |
eb0481bb | 930 | if (!is_reloc_sec(rsec)) |
442f04c3 JP |
931 | continue; |
932 | ||
a5bd6236 JP |
933 | rsec->base = find_section_by_index(elf, rsec->sh.sh_info); |
934 | if (!rsec->base) { | |
f1974222 | 935 | WARN("can't find base section for reloc section %s", |
a5bd6236 | 936 | rsec->name); |
442f04c3 JP |
937 | return -1; |
938 | } | |
939 | ||
a5bd6236 | 940 | rsec->base->rsec = rsec; |
442f04c3 | 941 | |
f1974222 | 942 | nr_reloc = 0; |
ebcef730 JP |
943 | rsec->relocs = calloc(sec_num_entries(rsec), sizeof(*reloc)); |
944 | if (!rsec->relocs) { | |
8045b8f0 TW |
945 | perror("calloc"); |
946 | return -1; | |
947 | } | |
ebcef730 JP |
948 | for (i = 0; i < sec_num_entries(rsec); i++) { |
949 | reloc = &rsec->relocs[i]; | |
53257a97 JP |
950 | |
951 | if (read_reloc(rsec, i, reloc)) | |
952 | return -1; | |
442f04c3 | 953 | |
a5bd6236 | 954 | reloc->sec = rsec; |
53257a97 | 955 | symndx = GELF_R_SYM(reloc->rel.r_info); |
19526717 | 956 | reloc->sym = sym = find_symbol_by_index(elf, symndx); |
f1974222 MH |
957 | if (!reloc->sym) { |
958 | WARN("can't find reloc entry symbol %d for %s", | |
a5bd6236 | 959 | symndx, rsec->name); |
442f04c3 JP |
960 | return -1; |
961 | } | |
042ba73f | 962 | |
19526717 | 963 | list_add_tail(&reloc->sym_reloc_entry, &sym->reloc_list); |
25cf0d8a | 964 | elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); |
3a647607 | 965 | |
f1974222 | 966 | nr_reloc++; |
442f04c3 | 967 | } |
f1974222 | 968 | max_reloc = max(max_reloc, nr_reloc); |
1e11f3fd PZ |
969 | } |
970 | ||
2daf7fab | 971 | if (opts.stats) { |
f1974222 | 972 | printf("max_reloc: %lu\n", max_reloc); |
eb0481bb | 973 | printf("num_relocs: %lu\n", elf->num_relocs); |
25cf0d8a | 974 | printf("reloc_bits: %d\n", elf->reloc_bits); |
442f04c3 JP |
975 | } |
976 | ||
977 | return 0; | |
978 | } | |
979 | ||
bc359ff2 | 980 | struct elf *elf_open_read(const char *name, int flags) |
442f04c3 JP |
981 | { |
982 | struct elf *elf; | |
627fce14 | 983 | Elf_Cmd cmd; |
442f04c3 JP |
984 | |
985 | elf_version(EV_CURRENT); | |
986 | ||
987 | elf = malloc(sizeof(*elf)); | |
988 | if (!elf) { | |
989 | perror("malloc"); | |
990 | return NULL; | |
991 | } | |
34f7c96d | 992 | memset(elf, 0, offsetof(struct elf, sections)); |
442f04c3 JP |
993 | |
994 | INIT_LIST_HEAD(&elf->sections); | |
995 | ||
627fce14 | 996 | elf->fd = open(name, flags); |
442f04c3 | 997 | if (elf->fd == -1) { |
385d11b1 JP |
998 | fprintf(stderr, "objtool: Can't open '%s': %s\n", |
999 | name, strerror(errno)); | |
442f04c3 JP |
1000 | goto err; |
1001 | } | |
1002 | ||
627fce14 JP |
1003 | if ((flags & O_ACCMODE) == O_RDONLY) |
1004 | cmd = ELF_C_READ_MMAP; | |
1005 | else if ((flags & O_ACCMODE) == O_RDWR) | |
1006 | cmd = ELF_C_RDWR; | |
1007 | else /* O_WRONLY */ | |
1008 | cmd = ELF_C_WRITE; | |
1009 | ||
1010 | elf->elf = elf_begin(elf->fd, cmd, NULL); | |
442f04c3 | 1011 | if (!elf->elf) { |
baa41469 | 1012 | WARN_ELF("elf_begin"); |
442f04c3 JP |
1013 | goto err; |
1014 | } | |
1015 | ||
1016 | if (!gelf_getehdr(elf->elf, &elf->ehdr)) { | |
baa41469 | 1017 | WARN_ELF("gelf_getehdr"); |
442f04c3 JP |
1018 | goto err; |
1019 | } | |
1020 | ||
1021 | if (read_sections(elf)) | |
1022 | goto err; | |
1023 | ||
1024 | if (read_symbols(elf)) | |
1025 | goto err; | |
1026 | ||
f1974222 | 1027 | if (read_relocs(elf)) |
442f04c3 JP |
1028 | goto err; |
1029 | ||
1030 | return elf; | |
1031 | ||
1032 | err: | |
1033 | elf_close(elf); | |
1034 | return NULL; | |
1035 | } | |
1036 | ||
417a4dc9 PZ |
1037 | static int elf_add_string(struct elf *elf, struct section *strtab, char *str) |
1038 | { | |
1039 | Elf_Data *data; | |
1040 | Elf_Scn *s; | |
1041 | int len; | |
1042 | ||
1043 | if (!strtab) | |
1044 | strtab = find_section_by_name(elf, ".strtab"); | |
1045 | if (!strtab) { | |
1046 | WARN("can't find .strtab section"); | |
1047 | return -1; | |
1048 | } | |
1049 | ||
1050 | s = elf_getscn(elf->elf, strtab->idx); | |
1051 | if (!s) { | |
1052 | WARN_ELF("elf_getscn"); | |
1053 | return -1; | |
1054 | } | |
1055 | ||
1056 | data = elf_newdata(s); | |
1057 | if (!data) { | |
1058 | WARN_ELF("elf_newdata"); | |
1059 | return -1; | |
1060 | } | |
1061 | ||
1062 | data->d_buf = str; | |
1063 | data->d_size = strlen(str) + 1; | |
1064 | data->d_align = 1; | |
1065 | ||
fe255fe6 JL |
1066 | len = strtab->sh.sh_size; |
1067 | strtab->sh.sh_size += data->d_size; | |
ff408273 JP |
1068 | |
1069 | mark_sec_changed(elf, strtab, true); | |
417a4dc9 PZ |
1070 | |
1071 | return len; | |
1072 | } | |
1073 | ||
627fce14 | 1074 | struct section *elf_create_section(struct elf *elf, const char *name, |
6342a20e | 1075 | size_t entsize, unsigned int nr) |
627fce14 JP |
1076 | { |
1077 | struct section *sec, *shstrtab; | |
1078 | size_t size = entsize * nr; | |
3c3ea503 | 1079 | Elf_Scn *s; |
627fce14 JP |
1080 | |
1081 | sec = malloc(sizeof(*sec)); | |
1082 | if (!sec) { | |
1083 | perror("malloc"); | |
1084 | return NULL; | |
1085 | } | |
1086 | memset(sec, 0, sizeof(*sec)); | |
1087 | ||
1088 | INIT_LIST_HEAD(&sec->symbol_list); | |
627fce14 | 1089 | |
627fce14 JP |
1090 | s = elf_newscn(elf->elf); |
1091 | if (!s) { | |
1092 | WARN_ELF("elf_newscn"); | |
1093 | return NULL; | |
1094 | } | |
1095 | ||
1096 | sec->name = strdup(name); | |
1097 | if (!sec->name) { | |
1098 | perror("strdup"); | |
1099 | return NULL; | |
1100 | } | |
1101 | ||
1102 | sec->idx = elf_ndxscn(s); | |
627fce14 JP |
1103 | |
1104 | sec->data = elf_newdata(s); | |
1105 | if (!sec->data) { | |
1106 | WARN_ELF("elf_newdata"); | |
1107 | return NULL; | |
1108 | } | |
1109 | ||
1110 | sec->data->d_size = size; | |
1111 | sec->data->d_align = 1; | |
1112 | ||
1113 | if (size) { | |
1114 | sec->data->d_buf = malloc(size); | |
1115 | if (!sec->data->d_buf) { | |
1116 | perror("malloc"); | |
1117 | return NULL; | |
1118 | } | |
1119 | memset(sec->data->d_buf, 0, size); | |
1120 | } | |
1121 | ||
1122 | if (!gelf_getshdr(s, &sec->sh)) { | |
1123 | WARN_ELF("gelf_getshdr"); | |
1124 | return NULL; | |
1125 | } | |
1126 | ||
1127 | sec->sh.sh_size = size; | |
1128 | sec->sh.sh_entsize = entsize; | |
1129 | sec->sh.sh_type = SHT_PROGBITS; | |
1130 | sec->sh.sh_addralign = 1; | |
2707579d | 1131 | sec->sh.sh_flags = SHF_ALLOC; |
627fce14 | 1132 | |
6d77d3b4 | 1133 | /* Add section name to .shstrtab (or .strtab for Clang) */ |
627fce14 | 1134 | shstrtab = find_section_by_name(elf, ".shstrtab"); |
6d77d3b4 SS |
1135 | if (!shstrtab) |
1136 | shstrtab = find_section_by_name(elf, ".strtab"); | |
627fce14 | 1137 | if (!shstrtab) { |
6d77d3b4 | 1138 | WARN("can't find .shstrtab or .strtab section"); |
627fce14 JP |
1139 | return NULL; |
1140 | } | |
417a4dc9 PZ |
1141 | sec->sh.sh_name = elf_add_string(elf, shstrtab, sec->name); |
1142 | if (sec->sh.sh_name == -1) | |
627fce14 | 1143 | return NULL; |
627fce14 | 1144 | |
53038996 | 1145 | list_add_tail(&sec->list, &elf->sections); |
25cf0d8a PZ |
1146 | elf_hash_add(section, &sec->hash, sec->idx); |
1147 | elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); | |
53038996 | 1148 | |
ff408273 | 1149 | mark_sec_changed(elf, sec, true); |
2b10be23 | 1150 | |
627fce14 JP |
1151 | return sec; |
1152 | } | |
1153 | ||
53257a97 | 1154 | static struct section *elf_create_rela_section(struct elf *elf, |
6342a20e JP |
1155 | struct section *sec, |
1156 | unsigned int reloc_nr) | |
fb414783 | 1157 | { |
a5bd6236 | 1158 | struct section *rsec; |
53257a97 | 1159 | char *rsec_name; |
fb414783 | 1160 | |
53257a97 JP |
1161 | rsec_name = malloc(strlen(sec->name) + strlen(".rela") + 1); |
1162 | if (!rsec_name) { | |
fb414783 MH |
1163 | perror("malloc"); |
1164 | return NULL; | |
1165 | } | |
53257a97 JP |
1166 | strcpy(rsec_name, ".rela"); |
1167 | strcat(rsec_name, sec->name); | |
fb414783 | 1168 | |
6342a20e | 1169 | rsec = elf_create_section(elf, rsec_name, elf_rela_size(elf), reloc_nr); |
53257a97 | 1170 | free(rsec_name); |
a5bd6236 | 1171 | if (!rsec) |
fb414783 MH |
1172 | return NULL; |
1173 | ||
6342a20e | 1174 | rsec->data->d_type = ELF_T_RELA; |
a5bd6236 | 1175 | rsec->sh.sh_type = SHT_RELA; |
53257a97 | 1176 | rsec->sh.sh_addralign = elf_addr_size(elf); |
a5bd6236 | 1177 | rsec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; |
53257a97 | 1178 | rsec->sh.sh_info = sec->idx; |
a5bd6236 | 1179 | rsec->sh.sh_flags = SHF_INFO_LINK; |
627fce14 | 1180 | |
ebcef730 JP |
1181 | rsec->relocs = calloc(sec_num_entries(rsec), sizeof(struct reloc)); |
1182 | if (!rsec->relocs) { | |
e0a9349b JP |
1183 | perror("calloc"); |
1184 | return NULL; | |
1185 | } | |
1186 | ||
6342a20e JP |
1187 | sec->rsec = rsec; |
1188 | rsec->base = sec; | |
1189 | ||
a5bd6236 | 1190 | return rsec; |
627fce14 JP |
1191 | } |
1192 | ||
6342a20e JP |
1193 | struct section *elf_create_section_pair(struct elf *elf, const char *name, |
1194 | size_t entsize, unsigned int nr, | |
1195 | unsigned int reloc_nr) | |
1196 | { | |
1197 | struct section *sec; | |
1198 | ||
1199 | sec = elf_create_section(elf, name, entsize, nr); | |
1200 | if (!sec) | |
1201 | return NULL; | |
1202 | ||
1203 | if (!elf_create_rela_section(elf, sec, reloc_nr)) | |
1204 | return NULL; | |
1205 | ||
1206 | return sec; | |
1207 | } | |
1208 | ||
fdabdd0b PZ |
1209 | int elf_write_insn(struct elf *elf, struct section *sec, |
1210 | unsigned long offset, unsigned int len, | |
1211 | const char *insn) | |
1212 | { | |
1213 | Elf_Data *data = sec->data; | |
1214 | ||
1215 | if (data->d_type != ELF_T_BYTE || data->d_off) { | |
1216 | WARN("write to unexpected data for section: %s", sec->name); | |
1217 | return -1; | |
1218 | } | |
1219 | ||
1220 | memcpy(data->d_buf + offset, insn, len); | |
fdabdd0b | 1221 | |
ff408273 | 1222 | mark_sec_changed(elf, sec, true); |
fdabdd0b PZ |
1223 | |
1224 | return 0; | |
1225 | } | |
1226 | ||
d832c005 | 1227 | int elf_write_reloc(struct elf *elf, struct reloc *reloc) |
fdabdd0b | 1228 | { |
a5bd6236 | 1229 | struct section *rsec = reloc->sec; |
53257a97 | 1230 | int ret; |
fdabdd0b | 1231 | |
0696b6e3 | 1232 | if (rsec->sh.sh_type == SHT_RELA) |
be9a4c11 | 1233 | ret = gelf_update_rela(rsec->data, reloc_idx(reloc), &reloc->rela); |
0696b6e3 | 1234 | else |
be9a4c11 | 1235 | ret = gelf_update_rel(rsec->data, reloc_idx(reloc), &reloc->rel); |
d832c005 | 1236 | |
53257a97 JP |
1237 | if (!ret) { |
1238 | WARN_ELF("gelf_update_rela"); | |
1239 | return -1; | |
fdabdd0b PZ |
1240 | } |
1241 | ||
ff408273 | 1242 | mark_sec_changed(elf, rsec, true); |
fdabdd0b PZ |
1243 | |
1244 | return 0; | |
1245 | } | |
1246 | ||
13f60e80 PZ |
1247 | /* |
1248 | * When Elf_Scn::sh_size is smaller than the combined Elf_Data::d_size | |
1249 | * do you: | |
1250 | * | |
1251 | * A) adhere to the section header and truncate the data, or | |
1252 | * B) ignore the section header and write out all the data you've got? | |
1253 | * | |
1254 | * Yes, libelf sucks and we need to manually truncate if we over-allocate data. | |
1255 | */ | |
1256 | static int elf_truncate_section(struct elf *elf, struct section *sec) | |
1257 | { | |
1258 | u64 size = sec->sh.sh_size; | |
1259 | bool truncated = false; | |
1260 | Elf_Data *data = NULL; | |
1261 | Elf_Scn *s; | |
1262 | ||
1263 | s = elf_getscn(elf->elf, sec->idx); | |
1264 | if (!s) { | |
1265 | WARN_ELF("elf_getscn"); | |
1266 | return -1; | |
1267 | } | |
1268 | ||
1269 | for (;;) { | |
1270 | /* get next data descriptor for the relevant section */ | |
1271 | data = elf_getdata(s, data); | |
1272 | ||
1273 | if (!data) { | |
1274 | if (size) { | |
1275 | WARN("end of section data but non-zero size left\n"); | |
1276 | return -1; | |
1277 | } | |
1278 | return 0; | |
1279 | } | |
1280 | ||
1281 | if (truncated) { | |
1282 | /* when we remove symbols */ | |
1283 | WARN("truncated; but more data\n"); | |
1284 | return -1; | |
1285 | } | |
1286 | ||
1287 | if (!data->d_size) { | |
1288 | WARN("zero size data"); | |
1289 | return -1; | |
1290 | } | |
1291 | ||
1292 | if (data->d_size > size) { | |
1293 | truncated = true; | |
1294 | data->d_size = size; | |
1295 | } | |
1296 | ||
1297 | size -= data->d_size; | |
1298 | } | |
1299 | } | |
1300 | ||
2b10be23 | 1301 | int elf_write(struct elf *elf) |
627fce14 JP |
1302 | { |
1303 | struct section *sec; | |
1304 | Elf_Scn *s; | |
1305 | ||
2daf7fab | 1306 | if (opts.dryrun) |
f2d3a250 PZ |
1307 | return 0; |
1308 | ||
3a647607 | 1309 | /* Update changed relocation sections and section headers: */ |
627fce14 | 1310 | list_for_each_entry(sec, &elf->sections, list) { |
13f60e80 PZ |
1311 | if (sec->truncate) |
1312 | elf_truncate_section(elf, sec); | |
1313 | ||
ff408273 | 1314 | if (sec_changed(sec)) { |
627fce14 JP |
1315 | s = elf_getscn(elf->elf, sec->idx); |
1316 | if (!s) { | |
1317 | WARN_ELF("elf_getscn"); | |
1318 | return -1; | |
1319 | } | |
ff408273 JP |
1320 | |
1321 | /* Note this also flags the section dirty */ | |
97dab2ae | 1322 | if (!gelf_update_shdr(s, &sec->sh)) { |
627fce14 JP |
1323 | WARN_ELF("gelf_update_shdr"); |
1324 | return -1; | |
1325 | } | |
2b10be23 | 1326 | |
ff408273 | 1327 | mark_sec_changed(elf, sec, false); |
627fce14 JP |
1328 | } |
1329 | } | |
1330 | ||
97dab2ae JP |
1331 | /* Make sure the new section header entries get updated properly. */ |
1332 | elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY); | |
1333 | ||
1334 | /* Write all changes to the file. */ | |
627fce14 JP |
1335 | if (elf_update(elf->elf, ELF_C_WRITE) < 0) { |
1336 | WARN_ELF("elf_update"); | |
1337 | return -1; | |
1338 | } | |
1339 | ||
2b10be23 PZ |
1340 | elf->changed = false; |
1341 | ||
627fce14 JP |
1342 | return 0; |
1343 | } | |
1344 | ||
442f04c3 JP |
1345 | void elf_close(struct elf *elf) |
1346 | { | |
baa41469 JP |
1347 | if (elf->elf) |
1348 | elf_end(elf->elf); | |
1349 | ||
1350 | if (elf->fd > 0) | |
1351 | close(elf->fd); | |
1352 | ||
5201a9bc JP |
1353 | /* |
1354 | * NOTE: All remaining allocations are leaked on purpose. Objtool is | |
1355 | * about to exit anyway. | |
1356 | */ | |
442f04c3 | 1357 | } |