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> |
7786032e | 19 | #include <objtool/builtin.h> |
442f04c3 | 20 | |
7786032e VG |
21 | #include <objtool/elf.h> |
22 | #include <objtool/warn.h> | |
442f04c3 | 23 | |
22566c16 AS |
24 | #define MAX_NAME_LEN 128 |
25 | ||
ae358196 PZ |
26 | static inline u32 str_hash(const char *str) |
27 | { | |
28 | return jhash(str, strlen(str), 0); | |
29 | } | |
30 | ||
25cf0d8a PZ |
31 | #define __elf_table(name) (elf->name##_hash) |
32 | #define __elf_bits(name) (elf->name##_bits) | |
34f7c96d | 33 | |
25cf0d8a PZ |
34 | #define elf_hash_add(name, node, key) \ |
35 | hlist_add_head(node, &__elf_table(name)[hash_min(key, __elf_bits(name))]) | |
34f7c96d | 36 | |
25cf0d8a PZ |
37 | #define elf_hash_for_each_possible(name, obj, member, key) \ |
38 | hlist_for_each_entry(obj, &__elf_table(name)[hash_min(key, __elf_bits(name))], member) | |
34f7c96d | 39 | |
25cf0d8a PZ |
40 | #define elf_alloc_hash(name, size) \ |
41 | ({ \ | |
42 | __elf_bits(name) = max(10, ilog2(size)); \ | |
43 | __elf_table(name) = mmap(NULL, sizeof(struct hlist_head) << __elf_bits(name), \ | |
44 | PROT_READ|PROT_WRITE, \ | |
45 | MAP_PRIVATE|MAP_ANON, -1, 0); \ | |
46 | if (__elf_table(name) == (void *)-1L) { \ | |
47 | WARN("mmap fail " #name); \ | |
48 | __elf_table(name) = NULL; \ | |
49 | } \ | |
50 | __elf_table(name); \ | |
51 | }) | |
34f7c96d | 52 | |
2d24dd57 | 53 | static bool symbol_to_offset(struct rb_node *a, const struct rb_node *b) |
2a362ecc PZ |
54 | { |
55 | struct symbol *sa = rb_entry(a, struct symbol, node); | |
56 | struct symbol *sb = rb_entry(b, struct symbol, node); | |
57 | ||
58 | if (sa->offset < sb->offset) | |
2d24dd57 | 59 | return true; |
2a362ecc | 60 | if (sa->offset > sb->offset) |
2d24dd57 | 61 | return false; |
2a362ecc PZ |
62 | |
63 | if (sa->len < sb->len) | |
2d24dd57 | 64 | return true; |
2a362ecc | 65 | if (sa->len > sb->len) |
2d24dd57 | 66 | return false; |
2a362ecc PZ |
67 | |
68 | sa->alias = sb; | |
69 | ||
2d24dd57 | 70 | return false; |
2a362ecc PZ |
71 | } |
72 | ||
73 | static int symbol_by_offset(const void *key, const struct rb_node *node) | |
74 | { | |
75 | const struct symbol *s = rb_entry(node, struct symbol, node); | |
76 | const unsigned long *o = key; | |
77 | ||
78 | if (*o < s->offset) | |
79 | return -1; | |
5377cae9 | 80 | if (*o >= s->offset + s->len) |
2a362ecc PZ |
81 | return 1; |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
4adb2368 PZ |
86 | struct symbol_hole { |
87 | unsigned long key; | |
88 | const struct symbol *sym; | |
89 | }; | |
90 | ||
91 | /* | |
92 | * Find !section symbol where @offset is after it. | |
93 | */ | |
94 | static int symbol_hole_by_offset(const void *key, const struct rb_node *node) | |
95 | { | |
96 | const struct symbol *s = rb_entry(node, struct symbol, node); | |
97 | struct symbol_hole *sh = (void *)key; | |
98 | ||
99 | if (sh->key < s->offset) | |
100 | return -1; | |
101 | ||
102 | if (sh->key >= s->offset + s->len) { | |
103 | if (s->type != STT_SECTION) | |
104 | sh->sym = s; | |
105 | return 1; | |
106 | } | |
107 | ||
108 | return 0; | |
109 | } | |
110 | ||
894e48ca | 111 | struct section *find_section_by_name(const struct elf *elf, const char *name) |
442f04c3 JP |
112 | { |
113 | struct section *sec; | |
114 | ||
25cf0d8a | 115 | elf_hash_for_each_possible(section_name, sec, name_hash, str_hash(name)) { |
442f04c3 JP |
116 | if (!strcmp(sec->name, name)) |
117 | return sec; | |
25cf0d8a | 118 | } |
442f04c3 JP |
119 | |
120 | return NULL; | |
121 | } | |
122 | ||
123 | static struct section *find_section_by_index(struct elf *elf, | |
124 | unsigned int idx) | |
125 | { | |
126 | struct section *sec; | |
127 | ||
25cf0d8a | 128 | elf_hash_for_each_possible(section, sec, hash, idx) { |
442f04c3 JP |
129 | if (sec->idx == idx) |
130 | return sec; | |
25cf0d8a | 131 | } |
442f04c3 JP |
132 | |
133 | return NULL; | |
134 | } | |
135 | ||
136 | static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) | |
137 | { | |
442f04c3 JP |
138 | struct symbol *sym; |
139 | ||
25cf0d8a | 140 | elf_hash_for_each_possible(symbol, sym, hash, idx) { |
65fb11a7 PZ |
141 | if (sym->idx == idx) |
142 | return sym; | |
25cf0d8a | 143 | } |
442f04c3 JP |
144 | |
145 | return NULL; | |
146 | } | |
147 | ||
148 | struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) | |
149 | { | |
2a362ecc | 150 | struct rb_node *node; |
442f04c3 | 151 | |
2d24dd57 | 152 | rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { |
2a362ecc PZ |
153 | struct symbol *s = rb_entry(node, struct symbol, node); |
154 | ||
155 | if (s->offset == offset && s->type != STT_SECTION) | |
156 | return s; | |
157 | } | |
7acfe531 JP |
158 | |
159 | return NULL; | |
160 | } | |
161 | ||
162 | struct symbol *find_func_by_offset(struct section *sec, unsigned long offset) | |
163 | { | |
2a362ecc | 164 | struct rb_node *node; |
7acfe531 | 165 | |
2d24dd57 | 166 | rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { |
2a362ecc PZ |
167 | struct symbol *s = rb_entry(node, struct symbol, node); |
168 | ||
169 | if (s->offset == offset && s->type == STT_FUNC) | |
170 | return s; | |
171 | } | |
442f04c3 JP |
172 | |
173 | return NULL; | |
174 | } | |
175 | ||
b490f453 | 176 | struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset) |
13810435 | 177 | { |
2a362ecc | 178 | struct rb_node *node; |
13810435 | 179 | |
2d24dd57 | 180 | rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { |
2a362ecc PZ |
181 | struct symbol *s = rb_entry(node, struct symbol, node); |
182 | ||
183 | if (s->type != STT_SECTION) | |
184 | return s; | |
185 | } | |
13810435 JP |
186 | |
187 | return NULL; | |
188 | } | |
189 | ||
4adb2368 PZ |
190 | /* |
191 | * Returns size of hole starting at @offset. | |
192 | */ | |
193 | int find_symbol_hole_containing(const struct section *sec, unsigned long offset) | |
194 | { | |
195 | struct symbol_hole hole = { | |
196 | .key = offset, | |
197 | .sym = NULL, | |
198 | }; | |
199 | struct rb_node *n; | |
200 | struct symbol *s; | |
201 | ||
202 | /* | |
203 | * Find the rightmost symbol for which @offset is after it. | |
204 | */ | |
205 | n = rb_find(&hole, &sec->symbol_tree, symbol_hole_by_offset); | |
206 | ||
207 | /* found a symbol that contains @offset */ | |
208 | if (n) | |
209 | return 0; /* not a hole */ | |
210 | ||
211 | /* didn't find a symbol for which @offset is after it */ | |
212 | if (!hole.sym) | |
213 | return 0; /* not a hole */ | |
214 | ||
215 | /* @offset >= sym->offset + sym->len, find symbol after it */ | |
216 | n = rb_next(&hole.sym->node); | |
217 | if (!n) | |
218 | return -1; /* until end of address space */ | |
219 | ||
220 | /* hole until start of next symbol */ | |
221 | s = rb_entry(n, struct symbol, node); | |
222 | return s->offset - offset; | |
223 | } | |
224 | ||
53d20720 | 225 | struct symbol *find_func_containing(struct section *sec, unsigned long offset) |
2a362ecc PZ |
226 | { |
227 | struct rb_node *node; | |
228 | ||
2d24dd57 | 229 | rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { |
2a362ecc PZ |
230 | struct symbol *s = rb_entry(node, struct symbol, node); |
231 | ||
232 | if (s->type == STT_FUNC) | |
233 | return s; | |
234 | } | |
235 | ||
236 | return NULL; | |
237 | } | |
238 | ||
894e48ca | 239 | struct symbol *find_symbol_by_name(const struct elf *elf, const char *name) |
5c51f4ae JP |
240 | { |
241 | struct symbol *sym; | |
242 | ||
25cf0d8a | 243 | elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) { |
cdb3d057 PZ |
244 | if (!strcmp(sym->name, name)) |
245 | return sym; | |
25cf0d8a | 246 | } |
5c51f4ae JP |
247 | |
248 | return NULL; | |
249 | } | |
250 | ||
f1974222 | 251 | struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, |
8b5fa6bc | 252 | unsigned long offset, unsigned int len) |
442f04c3 | 253 | { |
f1974222 | 254 | struct reloc *reloc, *r = NULL; |
042ba73f | 255 | unsigned long o; |
442f04c3 | 256 | |
f1974222 | 257 | if (!sec->reloc) |
442f04c3 JP |
258 | return NULL; |
259 | ||
f1974222 | 260 | sec = sec->reloc; |
8b5fa6bc | 261 | |
74b873e4 | 262 | for_offset_range(o, offset, offset + len) { |
25cf0d8a PZ |
263 | elf_hash_for_each_possible(reloc, reloc, hash, |
264 | sec_offset_hash(sec, o)) { | |
f1974222 | 265 | if (reloc->sec != sec) |
74b873e4 PZ |
266 | continue; |
267 | ||
f1974222 MH |
268 | if (reloc->offset >= offset && reloc->offset < offset + len) { |
269 | if (!r || reloc->offset < r->offset) | |
270 | r = reloc; | |
74b873e4 | 271 | } |
8b5fa6bc | 272 | } |
74b873e4 PZ |
273 | if (r) |
274 | return r; | |
8b5fa6bc | 275 | } |
442f04c3 JP |
276 | |
277 | return NULL; | |
278 | } | |
279 | ||
f1974222 | 280 | struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset) |
442f04c3 | 281 | { |
f1974222 | 282 | return find_reloc_by_dest_range(elf, sec, offset, 1); |
442f04c3 JP |
283 | } |
284 | ||
442f04c3 JP |
285 | static int read_sections(struct elf *elf) |
286 | { | |
287 | Elf_Scn *s = NULL; | |
288 | struct section *sec; | |
289 | size_t shstrndx, sections_nr; | |
290 | int i; | |
291 | ||
292 | if (elf_getshdrnum(elf->elf, §ions_nr)) { | |
baa41469 | 293 | WARN_ELF("elf_getshdrnum"); |
442f04c3 JP |
294 | return -1; |
295 | } | |
296 | ||
297 | if (elf_getshdrstrndx(elf->elf, &shstrndx)) { | |
baa41469 | 298 | WARN_ELF("elf_getshdrstrndx"); |
442f04c3 JP |
299 | return -1; |
300 | } | |
301 | ||
25cf0d8a PZ |
302 | if (!elf_alloc_hash(section, sections_nr) || |
303 | !elf_alloc_hash(section_name, sections_nr)) | |
304 | return -1; | |
305 | ||
442f04c3 JP |
306 | for (i = 0; i < sections_nr; i++) { |
307 | sec = malloc(sizeof(*sec)); | |
308 | if (!sec) { | |
309 | perror("malloc"); | |
310 | return -1; | |
311 | } | |
312 | memset(sec, 0, sizeof(*sec)); | |
313 | ||
a196e171 | 314 | INIT_LIST_HEAD(&sec->symbol_list); |
f1974222 | 315 | INIT_LIST_HEAD(&sec->reloc_list); |
442f04c3 | 316 | |
442f04c3 JP |
317 | s = elf_getscn(elf->elf, i); |
318 | if (!s) { | |
baa41469 | 319 | WARN_ELF("elf_getscn"); |
442f04c3 JP |
320 | return -1; |
321 | } | |
322 | ||
323 | sec->idx = elf_ndxscn(s); | |
324 | ||
325 | if (!gelf_getshdr(s, &sec->sh)) { | |
baa41469 | 326 | WARN_ELF("gelf_getshdr"); |
442f04c3 JP |
327 | return -1; |
328 | } | |
329 | ||
330 | sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name); | |
331 | if (!sec->name) { | |
baa41469 | 332 | WARN_ELF("elf_strptr"); |
442f04c3 JP |
333 | return -1; |
334 | } | |
335 | ||
df968c93 PV |
336 | if (sec->sh.sh_size != 0) { |
337 | sec->data = elf_getdata(s, NULL); | |
338 | if (!sec->data) { | |
339 | WARN_ELF("elf_getdata"); | |
340 | return -1; | |
341 | } | |
342 | if (sec->data->d_off != 0 || | |
343 | sec->data->d_size != sec->sh.sh_size) { | |
344 | WARN("unexpected data attributes for %s", | |
345 | sec->name); | |
346 | return -1; | |
347 | } | |
442f04c3 | 348 | } |
53038996 | 349 | |
d33b9035 | 350 | if (sec->sh.sh_flags & SHF_EXECINSTR) |
fe255fe6 | 351 | elf->text_size += sec->sh.sh_size; |
d33b9035 | 352 | |
53038996 | 353 | list_add_tail(&sec->list, &elf->sections); |
25cf0d8a PZ |
354 | elf_hash_add(section, &sec->hash, sec->idx); |
355 | elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); | |
442f04c3 JP |
356 | } |
357 | ||
25cf0d8a | 358 | if (stats) { |
1e11f3fd | 359 | printf("nr_sections: %lu\n", (unsigned long)sections_nr); |
25cf0d8a PZ |
360 | printf("section_bits: %d\n", elf->section_bits); |
361 | } | |
1e11f3fd | 362 | |
442f04c3 JP |
363 | /* sanity check, one more call to elf_nextscn() should return NULL */ |
364 | if (elf_nextscn(elf->elf, s)) { | |
365 | WARN("section entry mismatch"); | |
366 | return -1; | |
367 | } | |
368 | ||
369 | return 0; | |
370 | } | |
371 | ||
9a7827b7 PZ |
372 | static void elf_add_symbol(struct elf *elf, struct symbol *sym) |
373 | { | |
374 | struct list_head *entry; | |
375 | struct rb_node *pnode; | |
376 | ||
377 | sym->type = GELF_ST_TYPE(sym->sym.st_info); | |
378 | sym->bind = GELF_ST_BIND(sym->sym.st_info); | |
379 | ||
380 | sym->offset = sym->sym.st_value; | |
381 | sym->len = sym->sym.st_size; | |
382 | ||
383 | rb_add(&sym->node, &sym->sec->symbol_tree, symbol_to_offset); | |
384 | pnode = rb_prev(&sym->node); | |
385 | if (pnode) | |
386 | entry = &rb_entry(pnode, struct symbol, node)->list; | |
387 | else | |
388 | entry = &sym->sec->symbol_list; | |
389 | list_add(&sym->list, entry); | |
25cf0d8a PZ |
390 | elf_hash_add(symbol, &sym->hash, sym->idx); |
391 | elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name)); | |
9a7827b7 PZ |
392 | |
393 | /* | |
394 | * Don't store empty STT_NOTYPE symbols in the rbtree. They | |
395 | * can exist within a function, confusing the sorting. | |
396 | */ | |
397 | if (!sym->len) | |
398 | rb_erase(&sym->node, &sym->sec->symbol_tree); | |
399 | } | |
400 | ||
442f04c3 JP |
401 | static int read_symbols(struct elf *elf) |
402 | { | |
28fe1d7b | 403 | struct section *symtab, *symtab_shndx, *sec; |
2a362ecc | 404 | struct symbol *sym, *pfunc; |
442f04c3 | 405 | int symbols_nr, i; |
13810435 | 406 | char *coldstr; |
28fe1d7b ST |
407 | Elf_Data *shndx_data = NULL; |
408 | Elf32_Word shndx; | |
442f04c3 JP |
409 | |
410 | symtab = find_section_by_name(elf, ".symtab"); | |
25cf0d8a PZ |
411 | if (symtab) { |
412 | symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); | |
413 | if (symtab_shndx) | |
414 | shndx_data = symtab_shndx->data; | |
415 | ||
416 | symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize; | |
417 | } else { | |
1d489151 JP |
418 | /* |
419 | * A missing symbol table is actually possible if it's an empty | |
25cf0d8a PZ |
420 | * .o file. This can happen for thunk_64.o. Make sure to at |
421 | * least allocate the symbol hash tables so we can do symbol | |
422 | * lookups without crashing. | |
1d489151 | 423 | */ |
25cf0d8a | 424 | symbols_nr = 0; |
442f04c3 JP |
425 | } |
426 | ||
25cf0d8a PZ |
427 | if (!elf_alloc_hash(symbol, symbols_nr) || |
428 | !elf_alloc_hash(symbol_name, symbols_nr)) | |
429 | return -1; | |
442f04c3 JP |
430 | |
431 | for (i = 0; i < symbols_nr; i++) { | |
432 | sym = malloc(sizeof(*sym)); | |
433 | if (!sym) { | |
434 | perror("malloc"); | |
435 | return -1; | |
436 | } | |
437 | memset(sym, 0, sizeof(*sym)); | |
988f0168 | 438 | INIT_LIST_HEAD(&sym->pv_target); |
2a362ecc | 439 | sym->alias = sym; |
442f04c3 JP |
440 | |
441 | sym->idx = i; | |
442 | ||
28fe1d7b ST |
443 | if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym, |
444 | &shndx)) { | |
445 | WARN_ELF("gelf_getsymshndx"); | |
442f04c3 JP |
446 | goto err; |
447 | } | |
448 | ||
449 | sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, | |
450 | sym->sym.st_name); | |
451 | if (!sym->name) { | |
baa41469 | 452 | WARN_ELF("elf_strptr"); |
442f04c3 JP |
453 | goto err; |
454 | } | |
455 | ||
28fe1d7b ST |
456 | if ((sym->sym.st_shndx > SHN_UNDEF && |
457 | sym->sym.st_shndx < SHN_LORESERVE) || | |
458 | (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) { | |
459 | if (sym->sym.st_shndx != SHN_XINDEX) | |
460 | shndx = sym->sym.st_shndx; | |
461 | ||
462 | sym->sec = find_section_by_index(elf, shndx); | |
442f04c3 JP |
463 | if (!sym->sec) { |
464 | WARN("couldn't find section for symbol %s", | |
465 | sym->name); | |
466 | goto err; | |
467 | } | |
9a7827b7 | 468 | if (GELF_ST_TYPE(sym->sym.st_info) == STT_SECTION) { |
442f04c3 JP |
469 | sym->name = sym->sec->name; |
470 | sym->sec->sym = sym; | |
471 | } | |
472 | } else | |
473 | sym->sec = find_section_by_index(elf, 0); | |
474 | ||
9a7827b7 | 475 | elf_add_symbol(elf, sym); |
442f04c3 JP |
476 | } |
477 | ||
25cf0d8a | 478 | if (stats) { |
1e11f3fd | 479 | printf("nr_symbols: %lu\n", (unsigned long)symbols_nr); |
25cf0d8a PZ |
480 | printf("symbol_bits: %d\n", elf->symbol_bits); |
481 | } | |
1e11f3fd | 482 | |
13810435 JP |
483 | /* Create parent/child links for any cold subfunctions */ |
484 | list_for_each_entry(sec, &elf->sections, list) { | |
485 | list_for_each_entry(sym, &sec->symbol_list, list) { | |
22566c16 AS |
486 | char pname[MAX_NAME_LEN + 1]; |
487 | size_t pnamelen; | |
13810435 JP |
488 | if (sym->type != STT_FUNC) |
489 | continue; | |
e000acc1 KCA |
490 | |
491 | if (sym->pfunc == NULL) | |
492 | sym->pfunc = sym; | |
493 | ||
494 | if (sym->cfunc == NULL) | |
495 | sym->cfunc = sym; | |
496 | ||
bcb6fb5d | 497 | coldstr = strstr(sym->name, ".cold"); |
08b393d0 JP |
498 | if (!coldstr) |
499 | continue; | |
500 | ||
22566c16 AS |
501 | pnamelen = coldstr - sym->name; |
502 | if (pnamelen > MAX_NAME_LEN) { | |
503 | WARN("%s(): parent function name exceeds maximum length of %d characters", | |
504 | sym->name, MAX_NAME_LEN); | |
505 | return -1; | |
506 | } | |
507 | ||
508 | strncpy(pname, sym->name, pnamelen); | |
509 | pname[pnamelen] = '\0'; | |
510 | pfunc = find_symbol_by_name(elf, pname); | |
08b393d0 JP |
511 | |
512 | if (!pfunc) { | |
513 | WARN("%s(): can't find parent function", | |
514 | sym->name); | |
0b9301fb | 515 | return -1; |
08b393d0 JP |
516 | } |
517 | ||
518 | sym->pfunc = pfunc; | |
519 | pfunc->cfunc = sym; | |
520 | ||
521 | /* | |
522 | * Unfortunately, -fnoreorder-functions puts the child | |
523 | * inside the parent. Remove the overlap so we can | |
524 | * have sane assumptions. | |
525 | * | |
526 | * Note that pfunc->len now no longer matches | |
527 | * pfunc->sym.st_size. | |
528 | */ | |
529 | if (sym->sec == pfunc->sec && | |
530 | sym->offset >= pfunc->offset && | |
531 | sym->offset + sym->len == pfunc->offset + pfunc->len) { | |
532 | pfunc->len -= sym->len; | |
13810435 JP |
533 | } |
534 | } | |
535 | } | |
536 | ||
442f04c3 JP |
537 | return 0; |
538 | ||
539 | err: | |
540 | free(sym); | |
541 | return -1; | |
542 | } | |
543 | ||
d0c5c4cc PZ |
544 | static struct section *elf_create_reloc_section(struct elf *elf, |
545 | struct section *base, | |
546 | int reltype); | |
547 | ||
ef47cc01 | 548 | int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset, |
c087c6e7 | 549 | unsigned int type, struct symbol *sym, long addend) |
34f7c96d | 550 | { |
ef47cc01 PZ |
551 | struct reloc *reloc; |
552 | ||
d0c5c4cc PZ |
553 | if (!sec->reloc && !elf_create_reloc_section(elf, sec, SHT_RELA)) |
554 | return -1; | |
555 | ||
ef47cc01 PZ |
556 | reloc = malloc(sizeof(*reloc)); |
557 | if (!reloc) { | |
558 | perror("malloc"); | |
559 | return -1; | |
560 | } | |
561 | memset(reloc, 0, sizeof(*reloc)); | |
562 | ||
563 | reloc->sec = sec->reloc; | |
564 | reloc->offset = offset; | |
565 | reloc->type = type; | |
566 | reloc->sym = sym; | |
567 | reloc->addend = addend; | |
34f7c96d | 568 | |
ef47cc01 | 569 | list_add_tail(&reloc->list, &sec->reloc->reloc_list); |
25cf0d8a | 570 | elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); |
3a647607 | 571 | |
86e1e054 | 572 | sec->reloc->sh.sh_size += sec->reloc->sh.sh_entsize; |
ef47cc01 PZ |
573 | sec->reloc->changed = true; |
574 | ||
575 | return 0; | |
576 | } | |
577 | ||
578 | int elf_add_reloc_to_insn(struct elf *elf, struct section *sec, | |
579 | unsigned long offset, unsigned int type, | |
580 | struct section *insn_sec, unsigned long insn_off) | |
581 | { | |
582 | struct symbol *sym; | |
583 | int addend; | |
584 | ||
585 | if (insn_sec->sym) { | |
586 | sym = insn_sec->sym; | |
587 | addend = insn_off; | |
588 | ||
589 | } else { | |
590 | /* | |
591 | * The Clang assembler strips section symbols, so we have to | |
592 | * reference the function symbol instead: | |
593 | */ | |
594 | sym = find_symbol_containing(insn_sec, insn_off); | |
595 | if (!sym) { | |
596 | /* | |
597 | * Hack alert. This happens when we need to reference | |
598 | * the NOP pad insn immediately after the function. | |
599 | */ | |
600 | sym = find_symbol_containing(insn_sec, insn_off - 1); | |
601 | } | |
602 | ||
603 | if (!sym) { | |
604 | WARN("can't find symbol containing %s+0x%lx", insn_sec->name, insn_off); | |
605 | return -1; | |
606 | } | |
607 | ||
608 | addend = insn_off - sym->offset; | |
609 | } | |
610 | ||
611 | return elf_add_reloc(elf, sec, offset, type, sym, addend); | |
34f7c96d PZ |
612 | } |
613 | ||
fb414783 MH |
614 | static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) |
615 | { | |
616 | if (!gelf_getrel(sec->data, i, &reloc->rel)) { | |
617 | WARN_ELF("gelf_getrel"); | |
618 | return -1; | |
619 | } | |
620 | reloc->type = GELF_R_TYPE(reloc->rel.r_info); | |
621 | reloc->addend = 0; | |
622 | reloc->offset = reloc->rel.r_offset; | |
623 | *symndx = GELF_R_SYM(reloc->rel.r_info); | |
624 | return 0; | |
625 | } | |
626 | ||
627 | static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) | |
628 | { | |
629 | if (!gelf_getrela(sec->data, i, &reloc->rela)) { | |
630 | WARN_ELF("gelf_getrela"); | |
631 | return -1; | |
632 | } | |
633 | reloc->type = GELF_R_TYPE(reloc->rela.r_info); | |
634 | reloc->addend = reloc->rela.r_addend; | |
635 | reloc->offset = reloc->rela.r_offset; | |
636 | *symndx = GELF_R_SYM(reloc->rela.r_info); | |
637 | return 0; | |
638 | } | |
639 | ||
f1974222 | 640 | static int read_relocs(struct elf *elf) |
442f04c3 JP |
641 | { |
642 | struct section *sec; | |
f1974222 | 643 | struct reloc *reloc; |
442f04c3 JP |
644 | int i; |
645 | unsigned int symndx; | |
f1974222 | 646 | unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0; |
442f04c3 | 647 | |
d33b9035 | 648 | if (!elf_alloc_hash(reloc, elf->text_size / 16)) |
25cf0d8a PZ |
649 | return -1; |
650 | ||
442f04c3 | 651 | list_for_each_entry(sec, &elf->sections, list) { |
fb414783 MH |
652 | if ((sec->sh.sh_type != SHT_RELA) && |
653 | (sec->sh.sh_type != SHT_REL)) | |
442f04c3 JP |
654 | continue; |
655 | ||
1e968bf5 | 656 | sec->base = find_section_by_index(elf, sec->sh.sh_info); |
442f04c3 | 657 | if (!sec->base) { |
f1974222 | 658 | WARN("can't find base section for reloc section %s", |
442f04c3 JP |
659 | sec->name); |
660 | return -1; | |
661 | } | |
662 | ||
f1974222 | 663 | sec->base->reloc = sec; |
442f04c3 | 664 | |
f1974222 | 665 | nr_reloc = 0; |
442f04c3 | 666 | for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { |
f1974222 MH |
667 | reloc = malloc(sizeof(*reloc)); |
668 | if (!reloc) { | |
442f04c3 JP |
669 | perror("malloc"); |
670 | return -1; | |
671 | } | |
f1974222 | 672 | memset(reloc, 0, sizeof(*reloc)); |
fb414783 MH |
673 | switch (sec->sh.sh_type) { |
674 | case SHT_REL: | |
675 | if (read_rel_reloc(sec, i, reloc, &symndx)) | |
676 | return -1; | |
677 | break; | |
678 | case SHT_RELA: | |
679 | if (read_rela_reloc(sec, i, reloc, &symndx)) | |
680 | return -1; | |
681 | break; | |
682 | default: return -1; | |
442f04c3 JP |
683 | } |
684 | ||
f1974222 | 685 | reloc->sec = sec; |
d832c005 PZ |
686 | reloc->idx = i; |
687 | reloc->sym = find_symbol_by_index(elf, symndx); | |
f1974222 MH |
688 | if (!reloc->sym) { |
689 | WARN("can't find reloc entry symbol %d for %s", | |
442f04c3 JP |
690 | symndx, sec->name); |
691 | return -1; | |
692 | } | |
042ba73f | 693 | |
3a647607 | 694 | list_add_tail(&reloc->list, &sec->reloc_list); |
25cf0d8a | 695 | elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); |
3a647607 | 696 | |
f1974222 | 697 | nr_reloc++; |
442f04c3 | 698 | } |
f1974222 MH |
699 | max_reloc = max(max_reloc, nr_reloc); |
700 | tot_reloc += nr_reloc; | |
1e11f3fd PZ |
701 | } |
702 | ||
703 | if (stats) { | |
f1974222 MH |
704 | printf("max_reloc: %lu\n", max_reloc); |
705 | printf("tot_reloc: %lu\n", tot_reloc); | |
25cf0d8a | 706 | printf("reloc_bits: %d\n", elf->reloc_bits); |
442f04c3 JP |
707 | } |
708 | ||
709 | return 0; | |
710 | } | |
711 | ||
bc359ff2 | 712 | struct elf *elf_open_read(const char *name, int flags) |
442f04c3 JP |
713 | { |
714 | struct elf *elf; | |
627fce14 | 715 | Elf_Cmd cmd; |
442f04c3 JP |
716 | |
717 | elf_version(EV_CURRENT); | |
718 | ||
719 | elf = malloc(sizeof(*elf)); | |
720 | if (!elf) { | |
721 | perror("malloc"); | |
722 | return NULL; | |
723 | } | |
34f7c96d | 724 | memset(elf, 0, offsetof(struct elf, sections)); |
442f04c3 JP |
725 | |
726 | INIT_LIST_HEAD(&elf->sections); | |
727 | ||
627fce14 | 728 | elf->fd = open(name, flags); |
442f04c3 | 729 | if (elf->fd == -1) { |
385d11b1 JP |
730 | fprintf(stderr, "objtool: Can't open '%s': %s\n", |
731 | name, strerror(errno)); | |
442f04c3 JP |
732 | goto err; |
733 | } | |
734 | ||
627fce14 JP |
735 | if ((flags & O_ACCMODE) == O_RDONLY) |
736 | cmd = ELF_C_READ_MMAP; | |
737 | else if ((flags & O_ACCMODE) == O_RDWR) | |
738 | cmd = ELF_C_RDWR; | |
739 | else /* O_WRONLY */ | |
740 | cmd = ELF_C_WRITE; | |
741 | ||
742 | elf->elf = elf_begin(elf->fd, cmd, NULL); | |
442f04c3 | 743 | if (!elf->elf) { |
baa41469 | 744 | WARN_ELF("elf_begin"); |
442f04c3 JP |
745 | goto err; |
746 | } | |
747 | ||
748 | if (!gelf_getehdr(elf->elf, &elf->ehdr)) { | |
baa41469 | 749 | WARN_ELF("gelf_getehdr"); |
442f04c3 JP |
750 | goto err; |
751 | } | |
752 | ||
753 | if (read_sections(elf)) | |
754 | goto err; | |
755 | ||
756 | if (read_symbols(elf)) | |
757 | goto err; | |
758 | ||
f1974222 | 759 | if (read_relocs(elf)) |
442f04c3 JP |
760 | goto err; |
761 | ||
762 | return elf; | |
763 | ||
764 | err: | |
765 | elf_close(elf); | |
766 | return NULL; | |
767 | } | |
768 | ||
417a4dc9 PZ |
769 | static int elf_add_string(struct elf *elf, struct section *strtab, char *str) |
770 | { | |
771 | Elf_Data *data; | |
772 | Elf_Scn *s; | |
773 | int len; | |
774 | ||
775 | if (!strtab) | |
776 | strtab = find_section_by_name(elf, ".strtab"); | |
777 | if (!strtab) { | |
778 | WARN("can't find .strtab section"); | |
779 | return -1; | |
780 | } | |
781 | ||
782 | s = elf_getscn(elf->elf, strtab->idx); | |
783 | if (!s) { | |
784 | WARN_ELF("elf_getscn"); | |
785 | return -1; | |
786 | } | |
787 | ||
788 | data = elf_newdata(s); | |
789 | if (!data) { | |
790 | WARN_ELF("elf_newdata"); | |
791 | return -1; | |
792 | } | |
793 | ||
794 | data->d_buf = str; | |
795 | data->d_size = strlen(str) + 1; | |
796 | data->d_align = 1; | |
797 | ||
fe255fe6 JL |
798 | len = strtab->sh.sh_size; |
799 | strtab->sh.sh_size += data->d_size; | |
417a4dc9 PZ |
800 | strtab->changed = true; |
801 | ||
802 | return len; | |
803 | } | |
804 | ||
627fce14 | 805 | struct section *elf_create_section(struct elf *elf, const char *name, |
1e7e4788 | 806 | unsigned int sh_flags, size_t entsize, int nr) |
627fce14 JP |
807 | { |
808 | struct section *sec, *shstrtab; | |
809 | size_t size = entsize * nr; | |
3c3ea503 | 810 | Elf_Scn *s; |
627fce14 JP |
811 | |
812 | sec = malloc(sizeof(*sec)); | |
813 | if (!sec) { | |
814 | perror("malloc"); | |
815 | return NULL; | |
816 | } | |
817 | memset(sec, 0, sizeof(*sec)); | |
818 | ||
819 | INIT_LIST_HEAD(&sec->symbol_list); | |
f1974222 | 820 | INIT_LIST_HEAD(&sec->reloc_list); |
627fce14 | 821 | |
627fce14 JP |
822 | s = elf_newscn(elf->elf); |
823 | if (!s) { | |
824 | WARN_ELF("elf_newscn"); | |
825 | return NULL; | |
826 | } | |
827 | ||
828 | sec->name = strdup(name); | |
829 | if (!sec->name) { | |
830 | perror("strdup"); | |
831 | return NULL; | |
832 | } | |
833 | ||
834 | sec->idx = elf_ndxscn(s); | |
627fce14 JP |
835 | sec->changed = true; |
836 | ||
837 | sec->data = elf_newdata(s); | |
838 | if (!sec->data) { | |
839 | WARN_ELF("elf_newdata"); | |
840 | return NULL; | |
841 | } | |
842 | ||
843 | sec->data->d_size = size; | |
844 | sec->data->d_align = 1; | |
845 | ||
846 | if (size) { | |
847 | sec->data->d_buf = malloc(size); | |
848 | if (!sec->data->d_buf) { | |
849 | perror("malloc"); | |
850 | return NULL; | |
851 | } | |
852 | memset(sec->data->d_buf, 0, size); | |
853 | } | |
854 | ||
855 | if (!gelf_getshdr(s, &sec->sh)) { | |
856 | WARN_ELF("gelf_getshdr"); | |
857 | return NULL; | |
858 | } | |
859 | ||
860 | sec->sh.sh_size = size; | |
861 | sec->sh.sh_entsize = entsize; | |
862 | sec->sh.sh_type = SHT_PROGBITS; | |
863 | sec->sh.sh_addralign = 1; | |
1e7e4788 | 864 | sec->sh.sh_flags = SHF_ALLOC | sh_flags; |
627fce14 | 865 | |
6d77d3b4 | 866 | /* Add section name to .shstrtab (or .strtab for Clang) */ |
627fce14 | 867 | shstrtab = find_section_by_name(elf, ".shstrtab"); |
6d77d3b4 SS |
868 | if (!shstrtab) |
869 | shstrtab = find_section_by_name(elf, ".strtab"); | |
627fce14 | 870 | if (!shstrtab) { |
6d77d3b4 | 871 | WARN("can't find .shstrtab or .strtab section"); |
627fce14 JP |
872 | return NULL; |
873 | } | |
417a4dc9 PZ |
874 | sec->sh.sh_name = elf_add_string(elf, shstrtab, sec->name); |
875 | if (sec->sh.sh_name == -1) | |
627fce14 | 876 | return NULL; |
627fce14 | 877 | |
53038996 | 878 | list_add_tail(&sec->list, &elf->sections); |
25cf0d8a PZ |
879 | elf_hash_add(section, &sec->hash, sec->idx); |
880 | elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); | |
53038996 | 881 | |
2b10be23 PZ |
882 | elf->changed = true; |
883 | ||
627fce14 JP |
884 | return sec; |
885 | } | |
886 | ||
fb414783 MH |
887 | static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base) |
888 | { | |
889 | char *relocname; | |
890 | struct section *sec; | |
891 | ||
892 | relocname = malloc(strlen(base->name) + strlen(".rel") + 1); | |
893 | if (!relocname) { | |
894 | perror("malloc"); | |
895 | return NULL; | |
896 | } | |
897 | strcpy(relocname, ".rel"); | |
898 | strcat(relocname, base->name); | |
899 | ||
1e7e4788 | 900 | sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rel), 0); |
fb414783 MH |
901 | free(relocname); |
902 | if (!sec) | |
903 | return NULL; | |
904 | ||
905 | base->reloc = sec; | |
906 | sec->base = base; | |
907 | ||
908 | sec->sh.sh_type = SHT_REL; | |
909 | sec->sh.sh_addralign = 8; | |
910 | sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; | |
911 | sec->sh.sh_info = base->idx; | |
912 | sec->sh.sh_flags = SHF_INFO_LINK; | |
913 | ||
914 | return sec; | |
915 | } | |
916 | ||
917 | static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base) | |
627fce14 | 918 | { |
f1974222 | 919 | char *relocname; |
627fce14 JP |
920 | struct section *sec; |
921 | ||
f1974222 MH |
922 | relocname = malloc(strlen(base->name) + strlen(".rela") + 1); |
923 | if (!relocname) { | |
627fce14 JP |
924 | perror("malloc"); |
925 | return NULL; | |
926 | } | |
f1974222 MH |
927 | strcpy(relocname, ".rela"); |
928 | strcat(relocname, base->name); | |
627fce14 | 929 | |
1e7e4788 | 930 | sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rela), 0); |
f1974222 | 931 | free(relocname); |
627fce14 JP |
932 | if (!sec) |
933 | return NULL; | |
934 | ||
f1974222 | 935 | base->reloc = sec; |
627fce14 JP |
936 | sec->base = base; |
937 | ||
938 | sec->sh.sh_type = SHT_RELA; | |
939 | sec->sh.sh_addralign = 8; | |
940 | sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; | |
941 | sec->sh.sh_info = base->idx; | |
942 | sec->sh.sh_flags = SHF_INFO_LINK; | |
943 | ||
944 | return sec; | |
945 | } | |
946 | ||
d0c5c4cc | 947 | static struct section *elf_create_reloc_section(struct elf *elf, |
fb414783 MH |
948 | struct section *base, |
949 | int reltype) | |
950 | { | |
951 | switch (reltype) { | |
952 | case SHT_REL: return elf_create_rel_reloc_section(elf, base); | |
953 | case SHT_RELA: return elf_create_rela_reloc_section(elf, base); | |
954 | default: return NULL; | |
955 | } | |
956 | } | |
957 | ||
86e1e054 | 958 | static int elf_rebuild_rel_reloc_section(struct section *sec) |
627fce14 | 959 | { |
f1974222 | 960 | struct reloc *reloc; |
86e1e054 | 961 | int idx = 0; |
a1a664ec | 962 | void *buf; |
627fce14 | 963 | |
fb414783 | 964 | /* Allocate a buffer for relocations */ |
86e1e054 | 965 | buf = malloc(sec->sh.sh_size); |
a1a664ec | 966 | if (!buf) { |
fb414783 MH |
967 | perror("malloc"); |
968 | return -1; | |
969 | } | |
970 | ||
a1a664ec | 971 | sec->data->d_buf = buf; |
86e1e054 | 972 | sec->data->d_size = sec->sh.sh_size; |
a1a664ec | 973 | sec->data->d_type = ELF_T_REL; |
fb414783 | 974 | |
fb414783 MH |
975 | idx = 0; |
976 | list_for_each_entry(reloc, &sec->reloc_list, list) { | |
a1a664ec MS |
977 | reloc->rel.r_offset = reloc->offset; |
978 | reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); | |
b46179d6 MF |
979 | if (!gelf_update_rel(sec->data, idx, &reloc->rel)) { |
980 | WARN_ELF("gelf_update_rel"); | |
981 | return -1; | |
982 | } | |
fb414783 MH |
983 | idx++; |
984 | } | |
985 | ||
986 | return 0; | |
987 | } | |
627fce14 | 988 | |
86e1e054 | 989 | static int elf_rebuild_rela_reloc_section(struct section *sec) |
fb414783 MH |
990 | { |
991 | struct reloc *reloc; | |
86e1e054 | 992 | int idx = 0; |
a1a664ec | 993 | void *buf; |
fb414783 MH |
994 | |
995 | /* Allocate a buffer for relocations with addends */ | |
86e1e054 | 996 | buf = malloc(sec->sh.sh_size); |
a1a664ec | 997 | if (!buf) { |
627fce14 JP |
998 | perror("malloc"); |
999 | return -1; | |
1000 | } | |
1001 | ||
a1a664ec | 1002 | sec->data->d_buf = buf; |
86e1e054 | 1003 | sec->data->d_size = sec->sh.sh_size; |
a1a664ec | 1004 | sec->data->d_type = ELF_T_RELA; |
627fce14 | 1005 | |
627fce14 | 1006 | idx = 0; |
f1974222 | 1007 | list_for_each_entry(reloc, &sec->reloc_list, list) { |
a1a664ec MS |
1008 | reloc->rela.r_offset = reloc->offset; |
1009 | reloc->rela.r_addend = reloc->addend; | |
1010 | reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); | |
b46179d6 MF |
1011 | if (!gelf_update_rela(sec->data, idx, &reloc->rela)) { |
1012 | WARN_ELF("gelf_update_rela"); | |
1013 | return -1; | |
1014 | } | |
627fce14 JP |
1015 | idx++; |
1016 | } | |
1017 | ||
1018 | return 0; | |
1019 | } | |
1020 | ||
3a647607 | 1021 | static int elf_rebuild_reloc_section(struct elf *elf, struct section *sec) |
fb414783 | 1022 | { |
fb414783 | 1023 | switch (sec->sh.sh_type) { |
86e1e054 MF |
1024 | case SHT_REL: return elf_rebuild_rel_reloc_section(sec); |
1025 | case SHT_RELA: return elf_rebuild_rela_reloc_section(sec); | |
fb414783 MH |
1026 | default: return -1; |
1027 | } | |
1028 | } | |
1029 | ||
fdabdd0b PZ |
1030 | int elf_write_insn(struct elf *elf, struct section *sec, |
1031 | unsigned long offset, unsigned int len, | |
1032 | const char *insn) | |
1033 | { | |
1034 | Elf_Data *data = sec->data; | |
1035 | ||
1036 | if (data->d_type != ELF_T_BYTE || data->d_off) { | |
1037 | WARN("write to unexpected data for section: %s", sec->name); | |
1038 | return -1; | |
1039 | } | |
1040 | ||
1041 | memcpy(data->d_buf + offset, insn, len); | |
1042 | elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY); | |
1043 | ||
1044 | elf->changed = true; | |
1045 | ||
1046 | return 0; | |
1047 | } | |
1048 | ||
d832c005 | 1049 | int elf_write_reloc(struct elf *elf, struct reloc *reloc) |
fdabdd0b | 1050 | { |
d832c005 | 1051 | struct section *sec = reloc->sec; |
fdabdd0b | 1052 | |
d832c005 PZ |
1053 | if (sec->sh.sh_type == SHT_REL) { |
1054 | reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); | |
1055 | reloc->rel.r_offset = reloc->offset; | |
fdabdd0b | 1056 | |
d832c005 PZ |
1057 | if (!gelf_update_rel(sec->data, reloc->idx, &reloc->rel)) { |
1058 | WARN_ELF("gelf_update_rel"); | |
1059 | return -1; | |
1060 | } | |
1061 | } else { | |
1062 | reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); | |
1063 | reloc->rela.r_addend = reloc->addend; | |
1064 | reloc->rela.r_offset = reloc->offset; | |
1065 | ||
1066 | if (!gelf_update_rela(sec->data, reloc->idx, &reloc->rela)) { | |
1067 | WARN_ELF("gelf_update_rela"); | |
1068 | return -1; | |
1069 | } | |
fdabdd0b PZ |
1070 | } |
1071 | ||
1072 | elf->changed = true; | |
1073 | ||
1074 | return 0; | |
1075 | } | |
1076 | ||
2b10be23 | 1077 | int elf_write(struct elf *elf) |
627fce14 JP |
1078 | { |
1079 | struct section *sec; | |
1080 | Elf_Scn *s; | |
1081 | ||
f2d3a250 PZ |
1082 | if (dryrun) |
1083 | return 0; | |
1084 | ||
3a647607 | 1085 | /* Update changed relocation sections and section headers: */ |
627fce14 JP |
1086 | list_for_each_entry(sec, &elf->sections, list) { |
1087 | if (sec->changed) { | |
1088 | s = elf_getscn(elf->elf, sec->idx); | |
1089 | if (!s) { | |
1090 | WARN_ELF("elf_getscn"); | |
1091 | return -1; | |
1092 | } | |
97dab2ae | 1093 | if (!gelf_update_shdr(s, &sec->sh)) { |
627fce14 JP |
1094 | WARN_ELF("gelf_update_shdr"); |
1095 | return -1; | |
1096 | } | |
2b10be23 | 1097 | |
86e1e054 MF |
1098 | if (sec->base && |
1099 | elf_rebuild_reloc_section(elf, sec)) { | |
1100 | WARN("elf_rebuild_reloc_section"); | |
1101 | return -1; | |
1102 | } | |
1103 | ||
2b10be23 | 1104 | sec->changed = false; |
3a647607 | 1105 | elf->changed = true; |
627fce14 JP |
1106 | } |
1107 | } | |
1108 | ||
97dab2ae JP |
1109 | /* Make sure the new section header entries get updated properly. */ |
1110 | elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY); | |
1111 | ||
1112 | /* Write all changes to the file. */ | |
627fce14 JP |
1113 | if (elf_update(elf->elf, ELF_C_WRITE) < 0) { |
1114 | WARN_ELF("elf_update"); | |
1115 | return -1; | |
1116 | } | |
1117 | ||
2b10be23 PZ |
1118 | elf->changed = false; |
1119 | ||
627fce14 JP |
1120 | return 0; |
1121 | } | |
1122 | ||
442f04c3 JP |
1123 | void elf_close(struct elf *elf) |
1124 | { | |
1125 | struct section *sec, *tmpsec; | |
1126 | struct symbol *sym, *tmpsym; | |
f1974222 | 1127 | struct reloc *reloc, *tmpreloc; |
442f04c3 | 1128 | |
baa41469 JP |
1129 | if (elf->elf) |
1130 | elf_end(elf->elf); | |
1131 | ||
1132 | if (elf->fd > 0) | |
1133 | close(elf->fd); | |
1134 | ||
442f04c3 | 1135 | list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { |
a196e171 | 1136 | list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { |
442f04c3 | 1137 | list_del(&sym->list); |
042ba73f | 1138 | hash_del(&sym->hash); |
442f04c3 JP |
1139 | free(sym); |
1140 | } | |
f1974222 MH |
1141 | list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) { |
1142 | list_del(&reloc->list); | |
1143 | hash_del(&reloc->hash); | |
1144 | free(reloc); | |
442f04c3 JP |
1145 | } |
1146 | list_del(&sec->list); | |
1147 | free(sec); | |
1148 | } | |
baa41469 | 1149 | |
442f04c3 JP |
1150 | free(elf); |
1151 | } |