]>
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> | |
12 | #include <fcntl.h> | |
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <string.h> | |
16 | #include <unistd.h> | |
385d11b1 | 17 | #include <errno.h> |
1e11f3fd | 18 | #include "builtin.h" |
442f04c3 JP |
19 | |
20 | #include "elf.h" | |
21 | #include "warn.h" | |
22 | ||
22566c16 AS |
23 | #define MAX_NAME_LEN 128 |
24 | ||
ae358196 PZ |
25 | static inline u32 str_hash(const char *str) |
26 | { | |
27 | return jhash(str, strlen(str), 0); | |
28 | } | |
29 | ||
34f7c96d PZ |
30 | static inline int elf_hash_bits(void) |
31 | { | |
32 | return vmlinux ? ELF_HASH_BITS : 16; | |
33 | } | |
34 | ||
35 | #define elf_hash_add(hashtable, node, key) \ | |
36 | hlist_add_head(node, &hashtable[hash_min(key, elf_hash_bits())]) | |
37 | ||
38 | static void elf_hash_init(struct hlist_head *table) | |
39 | { | |
40 | __hash_init(table, 1U << elf_hash_bits()); | |
41 | } | |
42 | ||
43 | #define elf_hash_for_each_possible(name, obj, member, key) \ | |
44 | hlist_for_each_entry(obj, &name[hash_min(key, elf_hash_bits())], member) | |
45 | ||
2a362ecc PZ |
46 | static void rb_add(struct rb_root *tree, struct rb_node *node, |
47 | int (*cmp)(struct rb_node *, const struct rb_node *)) | |
48 | { | |
49 | struct rb_node **link = &tree->rb_node; | |
50 | struct rb_node *parent = NULL; | |
51 | ||
52 | while (*link) { | |
53 | parent = *link; | |
54 | if (cmp(node, parent) < 0) | |
55 | link = &parent->rb_left; | |
56 | else | |
57 | link = &parent->rb_right; | |
58 | } | |
59 | ||
60 | rb_link_node(node, parent, link); | |
61 | rb_insert_color(node, tree); | |
62 | } | |
63 | ||
b490f453 | 64 | static struct rb_node *rb_find_first(const struct rb_root *tree, const void *key, |
2a362ecc PZ |
65 | int (*cmp)(const void *key, const struct rb_node *)) |
66 | { | |
67 | struct rb_node *node = tree->rb_node; | |
68 | struct rb_node *match = NULL; | |
69 | ||
70 | while (node) { | |
71 | int c = cmp(key, node); | |
72 | if (c <= 0) { | |
73 | if (!c) | |
74 | match = node; | |
75 | node = node->rb_left; | |
76 | } else if (c > 0) { | |
77 | node = node->rb_right; | |
78 | } | |
79 | } | |
80 | ||
81 | return match; | |
82 | } | |
83 | ||
84 | static struct rb_node *rb_next_match(struct rb_node *node, const void *key, | |
85 | int (*cmp)(const void *key, const struct rb_node *)) | |
86 | { | |
87 | node = rb_next(node); | |
88 | if (node && cmp(key, node)) | |
89 | node = NULL; | |
90 | return node; | |
91 | } | |
92 | ||
93 | #define rb_for_each(tree, node, key, cmp) \ | |
94 | for ((node) = rb_find_first((tree), (key), (cmp)); \ | |
95 | (node); (node) = rb_next_match((node), (key), (cmp))) | |
96 | ||
97 | static int symbol_to_offset(struct rb_node *a, const struct rb_node *b) | |
98 | { | |
99 | struct symbol *sa = rb_entry(a, struct symbol, node); | |
100 | struct symbol *sb = rb_entry(b, struct symbol, node); | |
101 | ||
102 | if (sa->offset < sb->offset) | |
103 | return -1; | |
104 | if (sa->offset > sb->offset) | |
105 | return 1; | |
106 | ||
107 | if (sa->len < sb->len) | |
108 | return -1; | |
109 | if (sa->len > sb->len) | |
110 | return 1; | |
111 | ||
112 | sa->alias = sb; | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
117 | static int symbol_by_offset(const void *key, const struct rb_node *node) | |
118 | { | |
119 | const struct symbol *s = rb_entry(node, struct symbol, node); | |
120 | const unsigned long *o = key; | |
121 | ||
122 | if (*o < s->offset) | |
123 | return -1; | |
5377cae9 | 124 | if (*o >= s->offset + s->len) |
2a362ecc PZ |
125 | return 1; |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
894e48ca | 130 | struct section *find_section_by_name(const struct elf *elf, const char *name) |
442f04c3 JP |
131 | { |
132 | struct section *sec; | |
133 | ||
34f7c96d | 134 | elf_hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name)) |
442f04c3 JP |
135 | if (!strcmp(sec->name, name)) |
136 | return sec; | |
137 | ||
138 | return NULL; | |
139 | } | |
140 | ||
141 | static struct section *find_section_by_index(struct elf *elf, | |
142 | unsigned int idx) | |
143 | { | |
144 | struct section *sec; | |
145 | ||
34f7c96d | 146 | elf_hash_for_each_possible(elf->section_hash, sec, hash, idx) |
442f04c3 JP |
147 | if (sec->idx == idx) |
148 | return sec; | |
149 | ||
150 | return NULL; | |
151 | } | |
152 | ||
153 | static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) | |
154 | { | |
442f04c3 JP |
155 | struct symbol *sym; |
156 | ||
34f7c96d | 157 | elf_hash_for_each_possible(elf->symbol_hash, sym, hash, idx) |
65fb11a7 PZ |
158 | if (sym->idx == idx) |
159 | return sym; | |
442f04c3 JP |
160 | |
161 | return NULL; | |
162 | } | |
163 | ||
164 | struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) | |
165 | { | |
2a362ecc | 166 | struct rb_node *node; |
442f04c3 | 167 | |
2a362ecc PZ |
168 | rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { |
169 | struct symbol *s = rb_entry(node, struct symbol, node); | |
170 | ||
171 | if (s->offset == offset && s->type != STT_SECTION) | |
172 | return s; | |
173 | } | |
7acfe531 JP |
174 | |
175 | return NULL; | |
176 | } | |
177 | ||
178 | struct symbol *find_func_by_offset(struct section *sec, unsigned long offset) | |
179 | { | |
2a362ecc | 180 | struct rb_node *node; |
7acfe531 | 181 | |
2a362ecc PZ |
182 | rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { |
183 | struct symbol *s = rb_entry(node, struct symbol, node); | |
184 | ||
185 | if (s->offset == offset && s->type == STT_FUNC) | |
186 | return s; | |
187 | } | |
442f04c3 JP |
188 | |
189 | return NULL; | |
190 | } | |
191 | ||
b490f453 | 192 | struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset) |
13810435 | 193 | { |
2a362ecc | 194 | struct rb_node *node; |
13810435 | 195 | |
2a362ecc PZ |
196 | rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { |
197 | struct symbol *s = rb_entry(node, struct symbol, node); | |
198 | ||
199 | if (s->type != STT_SECTION) | |
200 | return s; | |
201 | } | |
13810435 JP |
202 | |
203 | return NULL; | |
204 | } | |
205 | ||
53d20720 | 206 | struct symbol *find_func_containing(struct section *sec, unsigned long offset) |
2a362ecc PZ |
207 | { |
208 | struct rb_node *node; | |
209 | ||
210 | rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { | |
211 | struct symbol *s = rb_entry(node, struct symbol, node); | |
212 | ||
213 | if (s->type == STT_FUNC) | |
214 | return s; | |
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 | ||
34f7c96d | 224 | elf_hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name)) |
cdb3d057 PZ |
225 | if (!strcmp(sym->name, name)) |
226 | return sym; | |
5c51f4ae JP |
227 | |
228 | return NULL; | |
229 | } | |
230 | ||
894e48ca | 231 | struct rela *find_rela_by_dest_range(const struct elf *elf, struct section *sec, |
8b5fa6bc | 232 | unsigned long offset, unsigned int len) |
442f04c3 | 233 | { |
74b873e4 | 234 | struct rela *rela, *r = NULL; |
042ba73f | 235 | unsigned long o; |
442f04c3 JP |
236 | |
237 | if (!sec->rela) | |
238 | return NULL; | |
239 | ||
8b5fa6bc PZ |
240 | sec = sec->rela; |
241 | ||
74b873e4 | 242 | for_offset_range(o, offset, offset + len) { |
34f7c96d | 243 | elf_hash_for_each_possible(elf->rela_hash, rela, hash, |
8b5fa6bc | 244 | sec_offset_hash(sec, o)) { |
74b873e4 PZ |
245 | if (rela->sec != sec) |
246 | continue; | |
247 | ||
248 | if (rela->offset >= offset && rela->offset < offset + len) { | |
249 | if (!r || rela->offset < r->offset) | |
250 | r = rela; | |
251 | } | |
8b5fa6bc | 252 | } |
74b873e4 PZ |
253 | if (r) |
254 | return r; | |
8b5fa6bc | 255 | } |
442f04c3 JP |
256 | |
257 | return NULL; | |
258 | } | |
259 | ||
894e48ca | 260 | struct rela *find_rela_by_dest(const struct elf *elf, struct section *sec, unsigned long offset) |
442f04c3 | 261 | { |
8b5fa6bc | 262 | return find_rela_by_dest_range(elf, sec, offset, 1); |
442f04c3 JP |
263 | } |
264 | ||
442f04c3 JP |
265 | static int read_sections(struct elf *elf) |
266 | { | |
267 | Elf_Scn *s = NULL; | |
268 | struct section *sec; | |
269 | size_t shstrndx, sections_nr; | |
270 | int i; | |
271 | ||
272 | if (elf_getshdrnum(elf->elf, §ions_nr)) { | |
baa41469 | 273 | WARN_ELF("elf_getshdrnum"); |
442f04c3 JP |
274 | return -1; |
275 | } | |
276 | ||
277 | if (elf_getshdrstrndx(elf->elf, &shstrndx)) { | |
baa41469 | 278 | WARN_ELF("elf_getshdrstrndx"); |
442f04c3 JP |
279 | return -1; |
280 | } | |
281 | ||
282 | for (i = 0; i < sections_nr; i++) { | |
283 | sec = malloc(sizeof(*sec)); | |
284 | if (!sec) { | |
285 | perror("malloc"); | |
286 | return -1; | |
287 | } | |
288 | memset(sec, 0, sizeof(*sec)); | |
289 | ||
a196e171 JP |
290 | INIT_LIST_HEAD(&sec->symbol_list); |
291 | INIT_LIST_HEAD(&sec->rela_list); | |
442f04c3 | 292 | |
442f04c3 JP |
293 | s = elf_getscn(elf->elf, i); |
294 | if (!s) { | |
baa41469 | 295 | WARN_ELF("elf_getscn"); |
442f04c3 JP |
296 | return -1; |
297 | } | |
298 | ||
299 | sec->idx = elf_ndxscn(s); | |
300 | ||
301 | if (!gelf_getshdr(s, &sec->sh)) { | |
baa41469 | 302 | WARN_ELF("gelf_getshdr"); |
442f04c3 JP |
303 | return -1; |
304 | } | |
305 | ||
306 | sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name); | |
307 | if (!sec->name) { | |
baa41469 | 308 | WARN_ELF("elf_strptr"); |
442f04c3 JP |
309 | return -1; |
310 | } | |
311 | ||
df968c93 PV |
312 | if (sec->sh.sh_size != 0) { |
313 | sec->data = elf_getdata(s, NULL); | |
314 | if (!sec->data) { | |
315 | WARN_ELF("elf_getdata"); | |
316 | return -1; | |
317 | } | |
318 | if (sec->data->d_off != 0 || | |
319 | sec->data->d_size != sec->sh.sh_size) { | |
320 | WARN("unexpected data attributes for %s", | |
321 | sec->name); | |
322 | return -1; | |
323 | } | |
442f04c3 | 324 | } |
df968c93 | 325 | sec->len = sec->sh.sh_size; |
53038996 PZ |
326 | |
327 | list_add_tail(&sec->list, &elf->sections); | |
34f7c96d PZ |
328 | elf_hash_add(elf->section_hash, &sec->hash, sec->idx); |
329 | elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name)); | |
442f04c3 JP |
330 | } |
331 | ||
1e11f3fd PZ |
332 | if (stats) |
333 | printf("nr_sections: %lu\n", (unsigned long)sections_nr); | |
334 | ||
442f04c3 JP |
335 | /* sanity check, one more call to elf_nextscn() should return NULL */ |
336 | if (elf_nextscn(elf->elf, s)) { | |
337 | WARN("section entry mismatch"); | |
338 | return -1; | |
339 | } | |
340 | ||
341 | return 0; | |
342 | } | |
343 | ||
344 | static int read_symbols(struct elf *elf) | |
345 | { | |
28fe1d7b | 346 | struct section *symtab, *symtab_shndx, *sec; |
2a362ecc PZ |
347 | struct symbol *sym, *pfunc; |
348 | struct list_head *entry; | |
349 | struct rb_node *pnode; | |
442f04c3 | 350 | int symbols_nr, i; |
13810435 | 351 | char *coldstr; |
28fe1d7b ST |
352 | Elf_Data *shndx_data = NULL; |
353 | Elf32_Word shndx; | |
442f04c3 JP |
354 | |
355 | symtab = find_section_by_name(elf, ".symtab"); | |
356 | if (!symtab) { | |
357 | WARN("missing symbol table"); | |
358 | return -1; | |
359 | } | |
360 | ||
28fe1d7b ST |
361 | symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); |
362 | if (symtab_shndx) | |
363 | shndx_data = symtab_shndx->data; | |
364 | ||
442f04c3 JP |
365 | symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize; |
366 | ||
367 | for (i = 0; i < symbols_nr; i++) { | |
368 | sym = malloc(sizeof(*sym)); | |
369 | if (!sym) { | |
370 | perror("malloc"); | |
371 | return -1; | |
372 | } | |
373 | memset(sym, 0, sizeof(*sym)); | |
2a362ecc | 374 | sym->alias = sym; |
442f04c3 JP |
375 | |
376 | sym->idx = i; | |
377 | ||
28fe1d7b ST |
378 | if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym, |
379 | &shndx)) { | |
380 | WARN_ELF("gelf_getsymshndx"); | |
442f04c3 JP |
381 | goto err; |
382 | } | |
383 | ||
384 | sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, | |
385 | sym->sym.st_name); | |
386 | if (!sym->name) { | |
baa41469 | 387 | WARN_ELF("elf_strptr"); |
442f04c3 JP |
388 | goto err; |
389 | } | |
390 | ||
391 | sym->type = GELF_ST_TYPE(sym->sym.st_info); | |
392 | sym->bind = GELF_ST_BIND(sym->sym.st_info); | |
393 | ||
28fe1d7b ST |
394 | if ((sym->sym.st_shndx > SHN_UNDEF && |
395 | sym->sym.st_shndx < SHN_LORESERVE) || | |
396 | (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) { | |
397 | if (sym->sym.st_shndx != SHN_XINDEX) | |
398 | shndx = sym->sym.st_shndx; | |
399 | ||
400 | sym->sec = find_section_by_index(elf, shndx); | |
442f04c3 JP |
401 | if (!sym->sec) { |
402 | WARN("couldn't find section for symbol %s", | |
403 | sym->name); | |
404 | goto err; | |
405 | } | |
406 | if (sym->type == STT_SECTION) { | |
407 | sym->name = sym->sec->name; | |
408 | sym->sec->sym = sym; | |
409 | } | |
410 | } else | |
411 | sym->sec = find_section_by_index(elf, 0); | |
412 | ||
413 | sym->offset = sym->sym.st_value; | |
414 | sym->len = sym->sym.st_size; | |
415 | ||
2a362ecc PZ |
416 | rb_add(&sym->sec->symbol_tree, &sym->node, symbol_to_offset); |
417 | pnode = rb_prev(&sym->node); | |
418 | if (pnode) | |
419 | entry = &rb_entry(pnode, struct symbol, node)->list; | |
420 | else | |
421 | entry = &sym->sec->symbol_list; | |
442f04c3 | 422 | list_add(&sym->list, entry); |
34f7c96d PZ |
423 | elf_hash_add(elf->symbol_hash, &sym->hash, sym->idx); |
424 | elf_hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name)); | |
442f04c3 JP |
425 | } |
426 | ||
1e11f3fd PZ |
427 | if (stats) |
428 | printf("nr_symbols: %lu\n", (unsigned long)symbols_nr); | |
429 | ||
13810435 JP |
430 | /* Create parent/child links for any cold subfunctions */ |
431 | list_for_each_entry(sec, &elf->sections, list) { | |
432 | list_for_each_entry(sym, &sec->symbol_list, list) { | |
22566c16 AS |
433 | char pname[MAX_NAME_LEN + 1]; |
434 | size_t pnamelen; | |
13810435 JP |
435 | if (sym->type != STT_FUNC) |
436 | continue; | |
437 | sym->pfunc = sym->cfunc = sym; | |
bcb6fb5d | 438 | coldstr = strstr(sym->name, ".cold"); |
08b393d0 JP |
439 | if (!coldstr) |
440 | continue; | |
441 | ||
22566c16 AS |
442 | pnamelen = coldstr - sym->name; |
443 | if (pnamelen > MAX_NAME_LEN) { | |
444 | WARN("%s(): parent function name exceeds maximum length of %d characters", | |
445 | sym->name, MAX_NAME_LEN); | |
446 | return -1; | |
447 | } | |
448 | ||
449 | strncpy(pname, sym->name, pnamelen); | |
450 | pname[pnamelen] = '\0'; | |
451 | pfunc = find_symbol_by_name(elf, pname); | |
08b393d0 JP |
452 | |
453 | if (!pfunc) { | |
454 | WARN("%s(): can't find parent function", | |
455 | sym->name); | |
0b9301fb | 456 | return -1; |
08b393d0 JP |
457 | } |
458 | ||
459 | sym->pfunc = pfunc; | |
460 | pfunc->cfunc = sym; | |
461 | ||
462 | /* | |
463 | * Unfortunately, -fnoreorder-functions puts the child | |
464 | * inside the parent. Remove the overlap so we can | |
465 | * have sane assumptions. | |
466 | * | |
467 | * Note that pfunc->len now no longer matches | |
468 | * pfunc->sym.st_size. | |
469 | */ | |
470 | if (sym->sec == pfunc->sec && | |
471 | sym->offset >= pfunc->offset && | |
472 | sym->offset + sym->len == pfunc->offset + pfunc->len) { | |
473 | pfunc->len -= sym->len; | |
13810435 JP |
474 | } |
475 | } | |
476 | } | |
477 | ||
442f04c3 JP |
478 | return 0; |
479 | ||
480 | err: | |
481 | free(sym); | |
482 | return -1; | |
483 | } | |
484 | ||
34f7c96d PZ |
485 | void elf_add_rela(struct elf *elf, struct rela *rela) |
486 | { | |
487 | struct section *sec = rela->sec; | |
488 | ||
489 | list_add_tail(&rela->list, &sec->rela_list); | |
490 | elf_hash_add(elf->rela_hash, &rela->hash, rela_hash(rela)); | |
491 | } | |
492 | ||
442f04c3 JP |
493 | static int read_relas(struct elf *elf) |
494 | { | |
495 | struct section *sec; | |
496 | struct rela *rela; | |
497 | int i; | |
498 | unsigned int symndx; | |
1e11f3fd | 499 | unsigned long nr_rela, max_rela = 0, tot_rela = 0; |
442f04c3 JP |
500 | |
501 | list_for_each_entry(sec, &elf->sections, list) { | |
502 | if (sec->sh.sh_type != SHT_RELA) | |
503 | continue; | |
504 | ||
505 | sec->base = find_section_by_name(elf, sec->name + 5); | |
506 | if (!sec->base) { | |
507 | WARN("can't find base section for rela section %s", | |
508 | sec->name); | |
509 | return -1; | |
510 | } | |
511 | ||
512 | sec->base->rela = sec; | |
513 | ||
1e11f3fd | 514 | nr_rela = 0; |
442f04c3 JP |
515 | for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { |
516 | rela = malloc(sizeof(*rela)); | |
517 | if (!rela) { | |
518 | perror("malloc"); | |
519 | return -1; | |
520 | } | |
521 | memset(rela, 0, sizeof(*rela)); | |
522 | ||
baa41469 JP |
523 | if (!gelf_getrela(sec->data, i, &rela->rela)) { |
524 | WARN_ELF("gelf_getrela"); | |
442f04c3 JP |
525 | return -1; |
526 | } | |
527 | ||
528 | rela->type = GELF_R_TYPE(rela->rela.r_info); | |
529 | rela->addend = rela->rela.r_addend; | |
530 | rela->offset = rela->rela.r_offset; | |
531 | symndx = GELF_R_SYM(rela->rela.r_info); | |
532 | rela->sym = find_symbol_by_index(elf, symndx); | |
e7c2bc37 | 533 | rela->sec = sec; |
442f04c3 JP |
534 | if (!rela->sym) { |
535 | WARN("can't find rela entry symbol %d for %s", | |
536 | symndx, sec->name); | |
537 | return -1; | |
538 | } | |
042ba73f | 539 | |
34f7c96d | 540 | elf_add_rela(elf, rela); |
1e11f3fd | 541 | nr_rela++; |
442f04c3 | 542 | } |
1e11f3fd PZ |
543 | max_rela = max(max_rela, nr_rela); |
544 | tot_rela += nr_rela; | |
545 | } | |
546 | ||
547 | if (stats) { | |
548 | printf("max_rela: %lu\n", max_rela); | |
549 | printf("tot_rela: %lu\n", tot_rela); | |
442f04c3 JP |
550 | } |
551 | ||
552 | return 0; | |
553 | } | |
554 | ||
bc359ff2 | 555 | struct elf *elf_open_read(const char *name, int flags) |
442f04c3 JP |
556 | { |
557 | struct elf *elf; | |
627fce14 | 558 | Elf_Cmd cmd; |
442f04c3 JP |
559 | |
560 | elf_version(EV_CURRENT); | |
561 | ||
562 | elf = malloc(sizeof(*elf)); | |
563 | if (!elf) { | |
564 | perror("malloc"); | |
565 | return NULL; | |
566 | } | |
34f7c96d | 567 | memset(elf, 0, offsetof(struct elf, sections)); |
442f04c3 JP |
568 | |
569 | INIT_LIST_HEAD(&elf->sections); | |
570 | ||
34f7c96d PZ |
571 | elf_hash_init(elf->symbol_hash); |
572 | elf_hash_init(elf->symbol_name_hash); | |
573 | elf_hash_init(elf->section_hash); | |
574 | elf_hash_init(elf->section_name_hash); | |
575 | elf_hash_init(elf->rela_hash); | |
576 | ||
627fce14 | 577 | elf->fd = open(name, flags); |
442f04c3 | 578 | if (elf->fd == -1) { |
385d11b1 JP |
579 | fprintf(stderr, "objtool: Can't open '%s': %s\n", |
580 | name, strerror(errno)); | |
442f04c3 JP |
581 | goto err; |
582 | } | |
583 | ||
627fce14 JP |
584 | if ((flags & O_ACCMODE) == O_RDONLY) |
585 | cmd = ELF_C_READ_MMAP; | |
586 | else if ((flags & O_ACCMODE) == O_RDWR) | |
587 | cmd = ELF_C_RDWR; | |
588 | else /* O_WRONLY */ | |
589 | cmd = ELF_C_WRITE; | |
590 | ||
591 | elf->elf = elf_begin(elf->fd, cmd, NULL); | |
442f04c3 | 592 | if (!elf->elf) { |
baa41469 | 593 | WARN_ELF("elf_begin"); |
442f04c3 JP |
594 | goto err; |
595 | } | |
596 | ||
597 | if (!gelf_getehdr(elf->elf, &elf->ehdr)) { | |
baa41469 | 598 | WARN_ELF("gelf_getehdr"); |
442f04c3 JP |
599 | goto err; |
600 | } | |
601 | ||
602 | if (read_sections(elf)) | |
603 | goto err; | |
604 | ||
605 | if (read_symbols(elf)) | |
606 | goto err; | |
607 | ||
608 | if (read_relas(elf)) | |
609 | goto err; | |
610 | ||
611 | return elf; | |
612 | ||
613 | err: | |
614 | elf_close(elf); | |
615 | return NULL; | |
616 | } | |
617 | ||
627fce14 JP |
618 | struct section *elf_create_section(struct elf *elf, const char *name, |
619 | size_t entsize, int nr) | |
620 | { | |
621 | struct section *sec, *shstrtab; | |
622 | size_t size = entsize * nr; | |
3c3ea503 | 623 | Elf_Scn *s; |
627fce14 JP |
624 | Elf_Data *data; |
625 | ||
626 | sec = malloc(sizeof(*sec)); | |
627 | if (!sec) { | |
628 | perror("malloc"); | |
629 | return NULL; | |
630 | } | |
631 | memset(sec, 0, sizeof(*sec)); | |
632 | ||
633 | INIT_LIST_HEAD(&sec->symbol_list); | |
634 | INIT_LIST_HEAD(&sec->rela_list); | |
627fce14 | 635 | |
627fce14 JP |
636 | s = elf_newscn(elf->elf); |
637 | if (!s) { | |
638 | WARN_ELF("elf_newscn"); | |
639 | return NULL; | |
640 | } | |
641 | ||
642 | sec->name = strdup(name); | |
643 | if (!sec->name) { | |
644 | perror("strdup"); | |
645 | return NULL; | |
646 | } | |
647 | ||
648 | sec->idx = elf_ndxscn(s); | |
649 | sec->len = size; | |
650 | sec->changed = true; | |
651 | ||
652 | sec->data = elf_newdata(s); | |
653 | if (!sec->data) { | |
654 | WARN_ELF("elf_newdata"); | |
655 | return NULL; | |
656 | } | |
657 | ||
658 | sec->data->d_size = size; | |
659 | sec->data->d_align = 1; | |
660 | ||
661 | if (size) { | |
662 | sec->data->d_buf = malloc(size); | |
663 | if (!sec->data->d_buf) { | |
664 | perror("malloc"); | |
665 | return NULL; | |
666 | } | |
667 | memset(sec->data->d_buf, 0, size); | |
668 | } | |
669 | ||
670 | if (!gelf_getshdr(s, &sec->sh)) { | |
671 | WARN_ELF("gelf_getshdr"); | |
672 | return NULL; | |
673 | } | |
674 | ||
675 | sec->sh.sh_size = size; | |
676 | sec->sh.sh_entsize = entsize; | |
677 | sec->sh.sh_type = SHT_PROGBITS; | |
678 | sec->sh.sh_addralign = 1; | |
679 | sec->sh.sh_flags = SHF_ALLOC; | |
680 | ||
681 | ||
6d77d3b4 | 682 | /* Add section name to .shstrtab (or .strtab for Clang) */ |
627fce14 | 683 | shstrtab = find_section_by_name(elf, ".shstrtab"); |
6d77d3b4 SS |
684 | if (!shstrtab) |
685 | shstrtab = find_section_by_name(elf, ".strtab"); | |
627fce14 | 686 | if (!shstrtab) { |
6d77d3b4 | 687 | WARN("can't find .shstrtab or .strtab section"); |
627fce14 JP |
688 | return NULL; |
689 | } | |
690 | ||
691 | s = elf_getscn(elf->elf, shstrtab->idx); | |
692 | if (!s) { | |
693 | WARN_ELF("elf_getscn"); | |
694 | return NULL; | |
695 | } | |
696 | ||
697 | data = elf_newdata(s); | |
698 | if (!data) { | |
699 | WARN_ELF("elf_newdata"); | |
700 | return NULL; | |
701 | } | |
702 | ||
703 | data->d_buf = sec->name; | |
704 | data->d_size = strlen(name) + 1; | |
705 | data->d_align = 1; | |
706 | ||
707 | sec->sh.sh_name = shstrtab->len; | |
708 | ||
709 | shstrtab->len += strlen(name) + 1; | |
710 | shstrtab->changed = true; | |
711 | ||
53038996 | 712 | list_add_tail(&sec->list, &elf->sections); |
34f7c96d PZ |
713 | elf_hash_add(elf->section_hash, &sec->hash, sec->idx); |
714 | elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name)); | |
53038996 | 715 | |
627fce14 JP |
716 | return sec; |
717 | } | |
718 | ||
719 | struct section *elf_create_rela_section(struct elf *elf, struct section *base) | |
720 | { | |
721 | char *relaname; | |
722 | struct section *sec; | |
723 | ||
724 | relaname = malloc(strlen(base->name) + strlen(".rela") + 1); | |
725 | if (!relaname) { | |
726 | perror("malloc"); | |
727 | return NULL; | |
728 | } | |
729 | strcpy(relaname, ".rela"); | |
730 | strcat(relaname, base->name); | |
731 | ||
732 | sec = elf_create_section(elf, relaname, sizeof(GElf_Rela), 0); | |
0998b7a0 | 733 | free(relaname); |
627fce14 JP |
734 | if (!sec) |
735 | return NULL; | |
736 | ||
737 | base->rela = sec; | |
738 | sec->base = base; | |
739 | ||
740 | sec->sh.sh_type = SHT_RELA; | |
741 | sec->sh.sh_addralign = 8; | |
742 | sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; | |
743 | sec->sh.sh_info = base->idx; | |
744 | sec->sh.sh_flags = SHF_INFO_LINK; | |
745 | ||
746 | return sec; | |
747 | } | |
748 | ||
749 | int elf_rebuild_rela_section(struct section *sec) | |
750 | { | |
751 | struct rela *rela; | |
752 | int nr, idx = 0, size; | |
753 | GElf_Rela *relas; | |
754 | ||
755 | nr = 0; | |
756 | list_for_each_entry(rela, &sec->rela_list, list) | |
757 | nr++; | |
758 | ||
759 | size = nr * sizeof(*relas); | |
760 | relas = malloc(size); | |
761 | if (!relas) { | |
762 | perror("malloc"); | |
763 | return -1; | |
764 | } | |
765 | ||
766 | sec->data->d_buf = relas; | |
767 | sec->data->d_size = size; | |
768 | ||
769 | sec->sh.sh_size = size; | |
770 | ||
771 | idx = 0; | |
772 | list_for_each_entry(rela, &sec->rela_list, list) { | |
773 | relas[idx].r_offset = rela->offset; | |
774 | relas[idx].r_addend = rela->addend; | |
775 | relas[idx].r_info = GELF_R_INFO(rela->sym->idx, rela->type); | |
776 | idx++; | |
777 | } | |
778 | ||
779 | return 0; | |
780 | } | |
781 | ||
894e48ca | 782 | int elf_write(const struct elf *elf) |
627fce14 JP |
783 | { |
784 | struct section *sec; | |
785 | Elf_Scn *s; | |
786 | ||
97dab2ae | 787 | /* Update section headers for changed sections: */ |
627fce14 JP |
788 | list_for_each_entry(sec, &elf->sections, list) { |
789 | if (sec->changed) { | |
790 | s = elf_getscn(elf->elf, sec->idx); | |
791 | if (!s) { | |
792 | WARN_ELF("elf_getscn"); | |
793 | return -1; | |
794 | } | |
97dab2ae | 795 | if (!gelf_update_shdr(s, &sec->sh)) { |
627fce14 JP |
796 | WARN_ELF("gelf_update_shdr"); |
797 | return -1; | |
798 | } | |
799 | } | |
800 | } | |
801 | ||
97dab2ae JP |
802 | /* Make sure the new section header entries get updated properly. */ |
803 | elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY); | |
804 | ||
805 | /* Write all changes to the file. */ | |
627fce14 JP |
806 | if (elf_update(elf->elf, ELF_C_WRITE) < 0) { |
807 | WARN_ELF("elf_update"); | |
808 | return -1; | |
809 | } | |
810 | ||
811 | return 0; | |
812 | } | |
813 | ||
442f04c3 JP |
814 | void elf_close(struct elf *elf) |
815 | { | |
816 | struct section *sec, *tmpsec; | |
817 | struct symbol *sym, *tmpsym; | |
818 | struct rela *rela, *tmprela; | |
819 | ||
baa41469 JP |
820 | if (elf->elf) |
821 | elf_end(elf->elf); | |
822 | ||
823 | if (elf->fd > 0) | |
824 | close(elf->fd); | |
825 | ||
442f04c3 | 826 | list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { |
a196e171 | 827 | list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { |
442f04c3 | 828 | list_del(&sym->list); |
042ba73f | 829 | hash_del(&sym->hash); |
442f04c3 JP |
830 | free(sym); |
831 | } | |
a196e171 | 832 | list_for_each_entry_safe(rela, tmprela, &sec->rela_list, list) { |
442f04c3 | 833 | list_del(&rela->list); |
042ba73f | 834 | hash_del(&rela->hash); |
442f04c3 JP |
835 | free(rela); |
836 | } | |
837 | list_del(&sec->list); | |
838 | free(sec); | |
839 | } | |
baa41469 | 840 | |
442f04c3 JP |
841 | free(elf); |
842 | } |