Blame view
tools/objtool/elf.c
21.5 KB
1ccea77e2 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
442f04c34 objtool: Add tool... |
2 3 4 5 6 7 |
/* * elf.c - ELF access library * * Adapted from kpatch (https://github.com/dynup/kpatch): * Copyright (C) 2013-2015 Josh Poimboeuf <jpoimboe@redhat.com> * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com> |
442f04c34 objtool: Add tool... |
8 9 10 11 12 13 14 15 16 |
*/ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> |
385d11b15 objtool: Improve ... |
17 |
#include <errno.h> |
1e11f3fdc objtool: Add a st... |
18 |
#include "builtin.h" |
442f04c34 objtool: Add tool... |
19 20 21 |
#include "elf.h" #include "warn.h" |
22566c160 objtool: Fix segf... |
22 |
#define MAX_NAME_LEN 128 |
ae358196f objtool: Optimize... |
23 24 25 26 |
static inline u32 str_hash(const char *str) { return jhash(str, strlen(str), 0); } |
34f7c96d9 objtool: Optimize... |
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
static inline int elf_hash_bits(void) { return vmlinux ? ELF_HASH_BITS : 16; } #define elf_hash_add(hashtable, node, key) \ hlist_add_head(node, &hashtable[hash_min(key, elf_hash_bits())]) static void elf_hash_init(struct hlist_head *table) { __hash_init(table, 1U << elf_hash_bits()); } #define elf_hash_for_each_possible(name, obj, member, key) \ hlist_for_each_entry(obj, &name[hash_min(key, elf_hash_bits())], member) |
2a362ecc3 objtool: Optimize... |
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
static void rb_add(struct rb_root *tree, struct rb_node *node, int (*cmp)(struct rb_node *, const struct rb_node *)) { struct rb_node **link = &tree->rb_node; struct rb_node *parent = NULL; while (*link) { parent = *link; if (cmp(node, parent) < 0) link = &parent->rb_left; else link = &parent->rb_right; } rb_link_node(node, parent, link); rb_insert_color(node, tree); } |
b490f4536 objtool: Move the... |
59 |
static struct rb_node *rb_find_first(const struct rb_root *tree, const void *key, |
2a362ecc3 objtool: Optimize... |
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
int (*cmp)(const void *key, const struct rb_node *)) { struct rb_node *node = tree->rb_node; struct rb_node *match = NULL; while (node) { int c = cmp(key, node); if (c <= 0) { if (!c) match = node; node = node->rb_left; } else if (c > 0) { node = node->rb_right; } } return match; } static struct rb_node *rb_next_match(struct rb_node *node, const void *key, int (*cmp)(const void *key, const struct rb_node *)) { node = rb_next(node); if (node && cmp(key, node)) node = NULL; return node; } #define rb_for_each(tree, node, key, cmp) \ for ((node) = rb_find_first((tree), (key), (cmp)); \ (node); (node) = rb_next_match((node), (key), (cmp))) static int symbol_to_offset(struct rb_node *a, const struct rb_node *b) { struct symbol *sa = rb_entry(a, struct symbol, node); struct symbol *sb = rb_entry(b, struct symbol, node); if (sa->offset < sb->offset) return -1; if (sa->offset > sb->offset) return 1; if (sa->len < sb->len) return -1; if (sa->len > sb->len) return 1; sa->alias = sb; return 0; } static int symbol_by_offset(const void *key, const struct rb_node *node) { const struct symbol *s = rb_entry(node, struct symbol, node); const unsigned long *o = key; if (*o < s->offset) return -1; |
5377cae94 objtool: Fix off-... |
119 |
if (*o >= s->offset + s->len) |
2a362ecc3 objtool: Optimize... |
120 121 122 123 |
return 1; return 0; } |
894e48cad objtool: Constify... |
124 |
struct section *find_section_by_name(const struct elf *elf, const char *name) |
442f04c34 objtool: Add tool... |
125 126 |
{ struct section *sec; |
34f7c96d9 objtool: Optimize... |
127 |
elf_hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name)) |
442f04c34 objtool: Add tool... |
128 129 130 131 132 133 134 135 136 137 |
if (!strcmp(sec->name, name)) return sec; return NULL; } static struct section *find_section_by_index(struct elf *elf, unsigned int idx) { struct section *sec; |
34f7c96d9 objtool: Optimize... |
138 |
elf_hash_for_each_possible(elf->section_hash, sec, hash, idx) |
442f04c34 objtool: Add tool... |
139 140 141 142 143 144 145 146 |
if (sec->idx == idx) return sec; return NULL; } static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) { |
442f04c34 objtool: Add tool... |
147 |
struct symbol *sym; |
34f7c96d9 objtool: Optimize... |
148 |
elf_hash_for_each_possible(elf->symbol_hash, sym, hash, idx) |
65fb11a7f objtool: Optimize... |
149 150 |
if (sym->idx == idx) return sym; |
442f04c34 objtool: Add tool... |
151 152 153 154 155 156 |
return NULL; } struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) { |
2a362ecc3 objtool: Optimize... |
157 |
struct rb_node *node; |
442f04c34 objtool: Add tool... |
158 |
|
2a362ecc3 objtool: Optimize... |
159 160 161 162 163 164 |
rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { struct symbol *s = rb_entry(node, struct symbol, node); if (s->offset == offset && s->type != STT_SECTION) return s; } |
7acfe5315 objtool: Improve ... |
165 166 167 168 169 170 |
return NULL; } struct symbol *find_func_by_offset(struct section *sec, unsigned long offset) { |
2a362ecc3 objtool: Optimize... |
171 |
struct rb_node *node; |
7acfe5315 objtool: Improve ... |
172 |
|
2a362ecc3 objtool: Optimize... |
173 174 175 176 177 178 |
rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { struct symbol *s = rb_entry(node, struct symbol, node); if (s->offset == offset && s->type == STT_FUNC) return s; } |
442f04c34 objtool: Add tool... |
179 180 181 |
return NULL; } |
b490f4536 objtool: Move the... |
182 |
struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset) |
13810435b objtool: Support ... |
183 |
{ |
2a362ecc3 objtool: Optimize... |
184 |
struct rb_node *node; |
13810435b objtool: Support ... |
185 |
|
2a362ecc3 objtool: Optimize... |
186 187 188 189 190 191 |
rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { struct symbol *s = rb_entry(node, struct symbol, node); if (s->type != STT_SECTION) return s; } |
13810435b objtool: Support ... |
192 193 194 |
return NULL; } |
53d20720b objtool: Rename f... |
195 |
struct symbol *find_func_containing(struct section *sec, unsigned long offset) |
2a362ecc3 objtool: Optimize... |
196 197 198 199 200 201 202 203 204 205 206 207 |
{ struct rb_node *node; rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { struct symbol *s = rb_entry(node, struct symbol, node); if (s->type == STT_FUNC) return s; } return NULL; } |
894e48cad objtool: Constify... |
208 |
struct symbol *find_symbol_by_name(const struct elf *elf, const char *name) |
5c51f4ae8 objtool: Fix anot... |
209 210 |
{ struct symbol *sym; |
34f7c96d9 objtool: Optimize... |
211 |
elf_hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name)) |
cdb3d057a objtool: Optimize... |
212 213 |
if (!strcmp(sym->name, name)) return sym; |
5c51f4ae8 objtool: Fix anot... |
214 215 216 |
return NULL; } |
f19742226 objtool: Rename r... |
217 |
struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, |
8b5fa6bc3 objtool: Optimize... |
218 |
unsigned long offset, unsigned int len) |
442f04c34 objtool: Add tool... |
219 |
{ |
f19742226 objtool: Rename r... |
220 |
struct reloc *reloc, *r = NULL; |
042ba73fe objtool: Add seve... |
221 |
unsigned long o; |
442f04c34 objtool: Add tool... |
222 |
|
f19742226 objtool: Rename r... |
223 |
if (!sec->reloc) |
442f04c34 objtool: Add tool... |
224 |
return NULL; |
f19742226 objtool: Rename r... |
225 |
sec = sec->reloc; |
8b5fa6bc3 objtool: Optimize... |
226 |
|
74b873e49 objtool: Optimize... |
227 |
for_offset_range(o, offset, offset + len) { |
f19742226 objtool: Rename r... |
228 |
elf_hash_for_each_possible(elf->reloc_hash, reloc, hash, |
8b5fa6bc3 objtool: Optimize... |
229 |
sec_offset_hash(sec, o)) { |
f19742226 objtool: Rename r... |
230 |
if (reloc->sec != sec) |
74b873e49 objtool: Optimize... |
231 |
continue; |
f19742226 objtool: Rename r... |
232 233 234 |
if (reloc->offset >= offset && reloc->offset < offset + len) { if (!r || reloc->offset < r->offset) r = reloc; |
74b873e49 objtool: Optimize... |
235 |
} |
8b5fa6bc3 objtool: Optimize... |
236 |
} |
74b873e49 objtool: Optimize... |
237 238 |
if (r) return r; |
8b5fa6bc3 objtool: Optimize... |
239 |
} |
442f04c34 objtool: Add tool... |
240 241 242 |
return NULL; } |
f19742226 objtool: Rename r... |
243 |
struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset) |
442f04c34 objtool: Add tool... |
244 |
{ |
f19742226 objtool: Rename r... |
245 |
return find_reloc_by_dest_range(elf, sec, offset, 1); |
442f04c34 objtool: Add tool... |
246 |
} |
442f04c34 objtool: Add tool... |
247 248 249 250 251 252 253 254 |
static int read_sections(struct elf *elf) { Elf_Scn *s = NULL; struct section *sec; size_t shstrndx, sections_nr; int i; if (elf_getshdrnum(elf->elf, §ions_nr)) { |
baa41469a objtool: Implemen... |
255 |
WARN_ELF("elf_getshdrnum"); |
442f04c34 objtool: Add tool... |
256 257 258 259 |
return -1; } if (elf_getshdrstrndx(elf->elf, &shstrndx)) { |
baa41469a objtool: Implemen... |
260 |
WARN_ELF("elf_getshdrstrndx"); |
442f04c34 objtool: Add tool... |
261 262 263 264 265 266 267 268 269 270 |
return -1; } for (i = 0; i < sections_nr; i++) { sec = malloc(sizeof(*sec)); if (!sec) { perror("malloc"); return -1; } memset(sec, 0, sizeof(*sec)); |
a196e1719 objtool: Rename s... |
271 |
INIT_LIST_HEAD(&sec->symbol_list); |
f19742226 objtool: Rename r... |
272 |
INIT_LIST_HEAD(&sec->reloc_list); |
442f04c34 objtool: Add tool... |
273 |
|
442f04c34 objtool: Add tool... |
274 275 |
s = elf_getscn(elf->elf, i); if (!s) { |
baa41469a objtool: Implemen... |
276 |
WARN_ELF("elf_getscn"); |
442f04c34 objtool: Add tool... |
277 278 279 280 281 282 |
return -1; } sec->idx = elf_ndxscn(s); if (!gelf_getshdr(s, &sec->sh)) { |
baa41469a objtool: Implemen... |
283 |
WARN_ELF("gelf_getshdr"); |
442f04c34 objtool: Add tool... |
284 285 286 287 288 |
return -1; } sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name); if (!sec->name) { |
baa41469a objtool: Implemen... |
289 |
WARN_ELF("elf_strptr"); |
442f04c34 objtool: Add tool... |
290 291 |
return -1; } |
df968c932 objtool: Do not r... |
292 293 294 295 296 297 298 299 300 301 302 303 |
if (sec->sh.sh_size != 0) { sec->data = elf_getdata(s, NULL); if (!sec->data) { WARN_ELF("elf_getdata"); return -1; } if (sec->data->d_off != 0 || sec->data->d_size != sec->sh.sh_size) { WARN("unexpected data attributes for %s", sec->name); return -1; } |
442f04c34 objtool: Add tool... |
304 |
} |
df968c932 objtool: Do not r... |
305 |
sec->len = sec->sh.sh_size; |
530389968 objtool: Optimize... |
306 307 |
list_add_tail(&sec->list, &elf->sections); |
34f7c96d9 objtool: Optimize... |
308 309 |
elf_hash_add(elf->section_hash, &sec->hash, sec->idx); elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name)); |
442f04c34 objtool: Add tool... |
310 |
} |
1e11f3fdc objtool: Add a st... |
311 312 313 |
if (stats) printf("nr_sections: %lu ", (unsigned long)sections_nr); |
442f04c34 objtool: Add tool... |
314 315 316 317 318 319 320 321 322 323 324 |
/* sanity check, one more call to elf_nextscn() should return NULL */ if (elf_nextscn(elf->elf, s)) { WARN("section entry mismatch"); return -1; } return 0; } static int read_symbols(struct elf *elf) { |
28fe1d7bf objtool: use gelf... |
325 |
struct section *symtab, *symtab_shndx, *sec; |
2a362ecc3 objtool: Optimize... |
326 327 328 |
struct symbol *sym, *pfunc; struct list_head *entry; struct rb_node *pnode; |
442f04c34 objtool: Add tool... |
329 |
int symbols_nr, i; |
13810435b objtool: Support ... |
330 |
char *coldstr; |
28fe1d7bf objtool: use gelf... |
331 332 |
Elf_Data *shndx_data = NULL; Elf32_Word shndx; |
442f04c34 objtool: Add tool... |
333 334 335 336 337 338 |
symtab = find_section_by_name(elf, ".symtab"); if (!symtab) { WARN("missing symbol table"); return -1; } |
28fe1d7bf objtool: use gelf... |
339 340 341 |
symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); if (symtab_shndx) shndx_data = symtab_shndx->data; |
442f04c34 objtool: Add tool... |
342 343 344 345 346 347 348 349 350 |
symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize; for (i = 0; i < symbols_nr; i++) { sym = malloc(sizeof(*sym)); if (!sym) { perror("malloc"); return -1; } memset(sym, 0, sizeof(*sym)); |
2a362ecc3 objtool: Optimize... |
351 |
sym->alias = sym; |
442f04c34 objtool: Add tool... |
352 353 |
sym->idx = i; |
28fe1d7bf objtool: use gelf... |
354 355 356 |
if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym, &shndx)) { WARN_ELF("gelf_getsymshndx"); |
442f04c34 objtool: Add tool... |
357 358 359 360 361 362 |
goto err; } sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, sym->sym.st_name); if (!sym->name) { |
baa41469a objtool: Implemen... |
363 |
WARN_ELF("elf_strptr"); |
442f04c34 objtool: Add tool... |
364 365 366 367 368 |
goto err; } sym->type = GELF_ST_TYPE(sym->sym.st_info); sym->bind = GELF_ST_BIND(sym->sym.st_info); |
28fe1d7bf objtool: use gelf... |
369 370 371 372 373 374 375 |
if ((sym->sym.st_shndx > SHN_UNDEF && sym->sym.st_shndx < SHN_LORESERVE) || (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) { if (sym->sym.st_shndx != SHN_XINDEX) shndx = sym->sym.st_shndx; sym->sec = find_section_by_index(elf, shndx); |
442f04c34 objtool: Add tool... |
376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
if (!sym->sec) { WARN("couldn't find section for symbol %s", sym->name); goto err; } if (sym->type == STT_SECTION) { sym->name = sym->sec->name; sym->sec->sym = sym; } } else sym->sec = find_section_by_index(elf, 0); sym->offset = sym->sym.st_value; sym->len = sym->sym.st_size; |
2a362ecc3 objtool: Optimize... |
390 391 392 393 394 395 |
rb_add(&sym->sec->symbol_tree, &sym->node, symbol_to_offset); pnode = rb_prev(&sym->node); if (pnode) entry = &rb_entry(pnode, struct symbol, node)->list; else entry = &sym->sec->symbol_list; |
442f04c34 objtool: Add tool... |
396 |
list_add(&sym->list, entry); |
34f7c96d9 objtool: Optimize... |
397 398 |
elf_hash_add(elf->symbol_hash, &sym->hash, sym->idx); elf_hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name)); |
442f04c34 objtool: Add tool... |
399 |
} |
1e11f3fdc objtool: Add a st... |
400 401 402 |
if (stats) printf("nr_symbols: %lu ", (unsigned long)symbols_nr); |
13810435b objtool: Support ... |
403 404 405 |
/* Create parent/child links for any cold subfunctions */ list_for_each_entry(sec, &elf->sections, list) { list_for_each_entry(sym, &sec->symbol_list, list) { |
22566c160 objtool: Fix segf... |
406 407 |
char pname[MAX_NAME_LEN + 1]; size_t pnamelen; |
13810435b objtool: Support ... |
408 409 |
if (sym->type != STT_FUNC) continue; |
e000acc14 objtool: Do not a... |
410 411 412 413 414 415 |
if (sym->pfunc == NULL) sym->pfunc = sym; if (sym->cfunc == NULL) sym->cfunc = sym; |
bcb6fb5da objtool: Support ... |
416 |
coldstr = strstr(sym->name, ".cold"); |
08b393d01 objtool: Support ... |
417 418 |
if (!coldstr) continue; |
22566c160 objtool: Fix segf... |
419 420 421 422 423 424 425 426 427 428 |
pnamelen = coldstr - sym->name; if (pnamelen > MAX_NAME_LEN) { WARN("%s(): parent function name exceeds maximum length of %d characters", sym->name, MAX_NAME_LEN); return -1; } strncpy(pname, sym->name, pnamelen); pname[pnamelen] = '\0'; pfunc = find_symbol_by_name(elf, pname); |
08b393d01 objtool: Support ... |
429 430 431 432 |
if (!pfunc) { WARN("%s(): can't find parent function", sym->name); |
0b9301fb6 objtool: Fix doub... |
433 |
return -1; |
08b393d01 objtool: Support ... |
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 |
} sym->pfunc = pfunc; pfunc->cfunc = sym; /* * Unfortunately, -fnoreorder-functions puts the child * inside the parent. Remove the overlap so we can * have sane assumptions. * * Note that pfunc->len now no longer matches * pfunc->sym.st_size. */ if (sym->sec == pfunc->sec && sym->offset >= pfunc->offset && sym->offset + sym->len == pfunc->offset + pfunc->len) { pfunc->len -= sym->len; |
13810435b objtool: Support ... |
451 452 453 |
} } } |
442f04c34 objtool: Add tool... |
454 455 456 457 458 459 |
return 0; err: free(sym); return -1; } |
f19742226 objtool: Rename r... |
460 |
void elf_add_reloc(struct elf *elf, struct reloc *reloc) |
34f7c96d9 objtool: Optimize... |
461 |
{ |
f19742226 objtool: Rename r... |
462 |
struct section *sec = reloc->sec; |
34f7c96d9 objtool: Optimize... |
463 |
|
f19742226 objtool: Rename r... |
464 465 |
list_add_tail(&reloc->list, &sec->reloc_list); elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc)); |
34f7c96d9 objtool: Optimize... |
466 |
} |
fb414783b objtool: Add supp... |
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) { if (!gelf_getrel(sec->data, i, &reloc->rel)) { WARN_ELF("gelf_getrel"); return -1; } reloc->type = GELF_R_TYPE(reloc->rel.r_info); reloc->addend = 0; reloc->offset = reloc->rel.r_offset; *symndx = GELF_R_SYM(reloc->rel.r_info); return 0; } static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) { if (!gelf_getrela(sec->data, i, &reloc->rela)) { WARN_ELF("gelf_getrela"); return -1; } reloc->type = GELF_R_TYPE(reloc->rela.r_info); reloc->addend = reloc->rela.r_addend; reloc->offset = reloc->rela.r_offset; *symndx = GELF_R_SYM(reloc->rela.r_info); return 0; } |
f19742226 objtool: Rename r... |
492 |
static int read_relocs(struct elf *elf) |
442f04c34 objtool: Add tool... |
493 494 |
{ struct section *sec; |
f19742226 objtool: Rename r... |
495 |
struct reloc *reloc; |
442f04c34 objtool: Add tool... |
496 497 |
int i; unsigned int symndx; |
f19742226 objtool: Rename r... |
498 |
unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0; |
442f04c34 objtool: Add tool... |
499 500 |
list_for_each_entry(sec, &elf->sections, list) { |
fb414783b objtool: Add supp... |
501 502 |
if ((sec->sh.sh_type != SHT_RELA) && (sec->sh.sh_type != SHT_REL)) |
442f04c34 objtool: Add tool... |
503 |
continue; |
1e968bf5c objtool: Use sh_i... |
504 |
sec->base = find_section_by_index(elf, sec->sh.sh_info); |
442f04c34 objtool: Add tool... |
505 |
if (!sec->base) { |
f19742226 objtool: Rename r... |
506 |
WARN("can't find base section for reloc section %s", |
442f04c34 objtool: Add tool... |
507 508 509 |
sec->name); return -1; } |
f19742226 objtool: Rename r... |
510 |
sec->base->reloc = sec; |
442f04c34 objtool: Add tool... |
511 |
|
f19742226 objtool: Rename r... |
512 |
nr_reloc = 0; |
442f04c34 objtool: Add tool... |
513 |
for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { |
f19742226 objtool: Rename r... |
514 515 |
reloc = malloc(sizeof(*reloc)); if (!reloc) { |
442f04c34 objtool: Add tool... |
516 517 518 |
perror("malloc"); return -1; } |
f19742226 objtool: Rename r... |
519 |
memset(reloc, 0, sizeof(*reloc)); |
fb414783b objtool: Add supp... |
520 521 522 523 524 525 526 527 528 529 |
switch (sec->sh.sh_type) { case SHT_REL: if (read_rel_reloc(sec, i, reloc, &symndx)) return -1; break; case SHT_RELA: if (read_rela_reloc(sec, i, reloc, &symndx)) return -1; break; default: return -1; |
442f04c34 objtool: Add tool... |
530 |
} |
f19742226 objtool: Rename r... |
531 |
reloc->sec = sec; |
d832c0051 Merge branch 'obj... |
532 533 |
reloc->idx = i; reloc->sym = find_symbol_by_index(elf, symndx); |
f19742226 objtool: Rename r... |
534 535 |
if (!reloc->sym) { WARN("can't find reloc entry symbol %d for %s", |
442f04c34 objtool: Add tool... |
536 537 538 |
symndx, sec->name); return -1; } |
042ba73fe objtool: Add seve... |
539 |
|
f19742226 objtool: Rename r... |
540 541 |
elf_add_reloc(elf, reloc); nr_reloc++; |
442f04c34 objtool: Add tool... |
542 |
} |
f19742226 objtool: Rename r... |
543 544 |
max_reloc = max(max_reloc, nr_reloc); tot_reloc += nr_reloc; |
1e11f3fdc objtool: Add a st... |
545 546 547 |
} if (stats) { |
f19742226 objtool: Rename r... |
548 549 550 551 |
printf("max_reloc: %lu ", max_reloc); printf("tot_reloc: %lu ", tot_reloc); |
442f04c34 objtool: Add tool... |
552 553 554 555 |
} return 0; } |
bc359ff2f objtool: Rename e... |
556 |
struct elf *elf_open_read(const char *name, int flags) |
442f04c34 objtool: Add tool... |
557 558 |
{ struct elf *elf; |
627fce148 objtool: Add ORC ... |
559 |
Elf_Cmd cmd; |
442f04c34 objtool: Add tool... |
560 561 562 563 564 565 566 567 |
elf_version(EV_CURRENT); elf = malloc(sizeof(*elf)); if (!elf) { perror("malloc"); return NULL; } |
34f7c96d9 objtool: Optimize... |
568 |
memset(elf, 0, offsetof(struct elf, sections)); |
442f04c34 objtool: Add tool... |
569 570 |
INIT_LIST_HEAD(&elf->sections); |
34f7c96d9 objtool: Optimize... |
571 572 573 574 |
elf_hash_init(elf->symbol_hash); elf_hash_init(elf->symbol_name_hash); elf_hash_init(elf->section_hash); elf_hash_init(elf->section_name_hash); |
f19742226 objtool: Rename r... |
575 |
elf_hash_init(elf->reloc_hash); |
34f7c96d9 objtool: Optimize... |
576 |
|
627fce148 objtool: Add ORC ... |
577 |
elf->fd = open(name, flags); |
442f04c34 objtool: Add tool... |
578 |
if (elf->fd == -1) { |
385d11b15 objtool: Improve ... |
579 580 581 |
fprintf(stderr, "objtool: Can't open '%s': %s ", name, strerror(errno)); |
442f04c34 objtool: Add tool... |
582 583 |
goto err; } |
627fce148 objtool: Add ORC ... |
584 585 586 587 588 589 590 591 |
if ((flags & O_ACCMODE) == O_RDONLY) cmd = ELF_C_READ_MMAP; else if ((flags & O_ACCMODE) == O_RDWR) cmd = ELF_C_RDWR; else /* O_WRONLY */ cmd = ELF_C_WRITE; elf->elf = elf_begin(elf->fd, cmd, NULL); |
442f04c34 objtool: Add tool... |
592 |
if (!elf->elf) { |
baa41469a objtool: Implemen... |
593 |
WARN_ELF("elf_begin"); |
442f04c34 objtool: Add tool... |
594 595 596 597 |
goto err; } if (!gelf_getehdr(elf->elf, &elf->ehdr)) { |
baa41469a objtool: Implemen... |
598 |
WARN_ELF("gelf_getehdr"); |
442f04c34 objtool: Add tool... |
599 600 601 602 603 604 605 606 |
goto err; } if (read_sections(elf)) goto err; if (read_symbols(elf)) goto err; |
f19742226 objtool: Rename r... |
607 |
if (read_relocs(elf)) |
442f04c34 objtool: Add tool... |
608 609 610 611 612 613 614 615 |
goto err; return elf; err: elf_close(elf); return NULL; } |
627fce148 objtool: Add ORC ... |
616 |
struct section *elf_create_section(struct elf *elf, const char *name, |
1e7e47883 x86/static_call: ... |
617 |
unsigned int sh_flags, size_t entsize, int nr) |
627fce148 objtool: Add ORC ... |
618 619 620 |
{ struct section *sec, *shstrtab; size_t size = entsize * nr; |
3c3ea5031 objtool: Use Elf_... |
621 |
Elf_Scn *s; |
627fce148 objtool: Add ORC ... |
622 623 624 625 626 627 628 629 630 631 |
Elf_Data *data; sec = malloc(sizeof(*sec)); if (!sec) { perror("malloc"); return NULL; } memset(sec, 0, sizeof(*sec)); INIT_LIST_HEAD(&sec->symbol_list); |
f19742226 objtool: Rename r... |
632 |
INIT_LIST_HEAD(&sec->reloc_list); |
627fce148 objtool: Add ORC ... |
633 |
|
627fce148 objtool: Add ORC ... |
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 |
s = elf_newscn(elf->elf); if (!s) { WARN_ELF("elf_newscn"); return NULL; } sec->name = strdup(name); if (!sec->name) { perror("strdup"); return NULL; } sec->idx = elf_ndxscn(s); sec->len = size; sec->changed = true; sec->data = elf_newdata(s); if (!sec->data) { WARN_ELF("elf_newdata"); return NULL; } sec->data->d_size = size; sec->data->d_align = 1; if (size) { sec->data->d_buf = malloc(size); if (!sec->data->d_buf) { perror("malloc"); return NULL; } memset(sec->data->d_buf, 0, size); } if (!gelf_getshdr(s, &sec->sh)) { WARN_ELF("gelf_getshdr"); return NULL; } sec->sh.sh_size = size; sec->sh.sh_entsize = entsize; sec->sh.sh_type = SHT_PROGBITS; sec->sh.sh_addralign = 1; |
1e7e47883 x86/static_call: ... |
677 |
sec->sh.sh_flags = SHF_ALLOC | sh_flags; |
627fce148 objtool: Add ORC ... |
678 |
|
6d77d3b43 objtool: Use '.st... |
679 |
/* Add section name to .shstrtab (or .strtab for Clang) */ |
627fce148 objtool: Add ORC ... |
680 |
shstrtab = find_section_by_name(elf, ".shstrtab"); |
6d77d3b43 objtool: Use '.st... |
681 682 |
if (!shstrtab) shstrtab = find_section_by_name(elf, ".strtab"); |
627fce148 objtool: Add ORC ... |
683 |
if (!shstrtab) { |
6d77d3b43 objtool: Use '.st... |
684 |
WARN("can't find .shstrtab or .strtab section"); |
627fce148 objtool: Add ORC ... |
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 |
return NULL; } s = elf_getscn(elf->elf, shstrtab->idx); if (!s) { WARN_ELF("elf_getscn"); return NULL; } data = elf_newdata(s); if (!data) { WARN_ELF("elf_newdata"); return NULL; } data->d_buf = sec->name; data->d_size = strlen(name) + 1; data->d_align = 1; sec->sh.sh_name = shstrtab->len; shstrtab->len += strlen(name) + 1; shstrtab->changed = true; |
530389968 objtool: Optimize... |
708 |
list_add_tail(&sec->list, &elf->sections); |
34f7c96d9 objtool: Optimize... |
709 710 |
elf_hash_add(elf->section_hash, &sec->hash, sec->idx); elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name)); |
530389968 objtool: Optimize... |
711 |
|
2b10be23a objtool: Clean up... |
712 |
elf->changed = true; |
627fce148 objtool: Add ORC ... |
713 714 |
return sec; } |
fb414783b objtool: Add supp... |
715 716 717 718 719 720 721 722 723 724 725 726 |
static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base) { char *relocname; struct section *sec; relocname = malloc(strlen(base->name) + strlen(".rel") + 1); if (!relocname) { perror("malloc"); return NULL; } strcpy(relocname, ".rel"); strcat(relocname, base->name); |
1e7e47883 x86/static_call: ... |
727 |
sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rel), 0); |
fb414783b objtool: Add supp... |
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 |
free(relocname); if (!sec) return NULL; base->reloc = sec; sec->base = base; sec->sh.sh_type = SHT_REL; sec->sh.sh_addralign = 8; sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; sec->sh.sh_info = base->idx; sec->sh.sh_flags = SHF_INFO_LINK; return sec; } static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base) |
627fce148 objtool: Add ORC ... |
745 |
{ |
f19742226 objtool: Rename r... |
746 |
char *relocname; |
627fce148 objtool: Add ORC ... |
747 |
struct section *sec; |
f19742226 objtool: Rename r... |
748 749 |
relocname = malloc(strlen(base->name) + strlen(".rela") + 1); if (!relocname) { |
627fce148 objtool: Add ORC ... |
750 751 752 |
perror("malloc"); return NULL; } |
f19742226 objtool: Rename r... |
753 754 |
strcpy(relocname, ".rela"); strcat(relocname, base->name); |
627fce148 objtool: Add ORC ... |
755 |
|
1e7e47883 x86/static_call: ... |
756 |
sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rela), 0); |
f19742226 objtool: Rename r... |
757 |
free(relocname); |
627fce148 objtool: Add ORC ... |
758 759 |
if (!sec) return NULL; |
f19742226 objtool: Rename r... |
760 |
base->reloc = sec; |
627fce148 objtool: Add ORC ... |
761 762 763 764 765 766 767 768 769 770 |
sec->base = base; sec->sh.sh_type = SHT_RELA; sec->sh.sh_addralign = 8; sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; sec->sh.sh_info = base->idx; sec->sh.sh_flags = SHF_INFO_LINK; return sec; } |
fb414783b objtool: Add supp... |
771 772 773 774 775 776 777 778 779 780 781 782 |
struct section *elf_create_reloc_section(struct elf *elf, struct section *base, int reltype) { switch (reltype) { case SHT_REL: return elf_create_rel_reloc_section(elf, base); case SHT_RELA: return elf_create_rela_reloc_section(elf, base); default: return NULL; } } static int elf_rebuild_rel_reloc_section(struct section *sec, int nr) |
627fce148 objtool: Add ORC ... |
783 |
{ |
f19742226 objtool: Rename r... |
784 |
struct reloc *reloc; |
fb414783b objtool: Add supp... |
785 786 |
int idx = 0, size; GElf_Rel *relocs; |
627fce148 objtool: Add ORC ... |
787 |
|
fb414783b objtool: Add supp... |
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 |
/* Allocate a buffer for relocations */ size = nr * sizeof(*relocs); relocs = malloc(size); if (!relocs) { perror("malloc"); return -1; } sec->data->d_buf = relocs; sec->data->d_size = size; sec->sh.sh_size = size; idx = 0; list_for_each_entry(reloc, &sec->reloc_list, list) { relocs[idx].r_offset = reloc->offset; relocs[idx].r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); idx++; } return 0; } |
627fce148 objtool: Add ORC ... |
810 |
|
fb414783b objtool: Add supp... |
811 812 813 814 815 816 817 |
static int elf_rebuild_rela_reloc_section(struct section *sec, int nr) { struct reloc *reloc; int idx = 0, size; GElf_Rela *relocs; /* Allocate a buffer for relocations with addends */ |
f19742226 objtool: Rename r... |
818 819 820 |
size = nr * sizeof(*relocs); relocs = malloc(size); if (!relocs) { |
627fce148 objtool: Add ORC ... |
821 822 823 |
perror("malloc"); return -1; } |
f19742226 objtool: Rename r... |
824 |
sec->data->d_buf = relocs; |
627fce148 objtool: Add ORC ... |
825 826 827 828 829 |
sec->data->d_size = size; sec->sh.sh_size = size; idx = 0; |
f19742226 objtool: Rename r... |
830 831 832 833 |
list_for_each_entry(reloc, &sec->reloc_list, list) { relocs[idx].r_offset = reloc->offset; relocs[idx].r_addend = reloc->addend; relocs[idx].r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); |
627fce148 objtool: Add ORC ... |
834 835 836 837 838 |
idx++; } return 0; } |
d832c0051 Merge branch 'obj... |
839 |
int elf_rebuild_reloc_section(struct elf *elf, struct section *sec) |
fb414783b objtool: Add supp... |
840 841 842 |
{ struct reloc *reloc; int nr; |
d832c0051 Merge branch 'obj... |
843 844 |
sec->changed = true; elf->changed = true; |
fb414783b objtool: Add supp... |
845 846 847 848 849 850 851 852 853 854 |
nr = 0; list_for_each_entry(reloc, &sec->reloc_list, list) nr++; switch (sec->sh.sh_type) { case SHT_REL: return elf_rebuild_rel_reloc_section(sec, nr); case SHT_RELA: return elf_rebuild_rela_reloc_section(sec, nr); default: return -1; } } |
fdabdd0b0 objtool: Provide ... |
855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 |
int elf_write_insn(struct elf *elf, struct section *sec, unsigned long offset, unsigned int len, const char *insn) { Elf_Data *data = sec->data; if (data->d_type != ELF_T_BYTE || data->d_off) { WARN("write to unexpected data for section: %s", sec->name); return -1; } memcpy(data->d_buf + offset, insn, len); elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY); elf->changed = true; return 0; } |
d832c0051 Merge branch 'obj... |
873 |
int elf_write_reloc(struct elf *elf, struct reloc *reloc) |
fdabdd0b0 objtool: Provide ... |
874 |
{ |
d832c0051 Merge branch 'obj... |
875 |
struct section *sec = reloc->sec; |
fdabdd0b0 objtool: Provide ... |
876 |
|
d832c0051 Merge branch 'obj... |
877 878 879 |
if (sec->sh.sh_type == SHT_REL) { reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); reloc->rel.r_offset = reloc->offset; |
fdabdd0b0 objtool: Provide ... |
880 |
|
d832c0051 Merge branch 'obj... |
881 882 883 884 885 886 887 888 889 890 891 892 893 |
if (!gelf_update_rel(sec->data, reloc->idx, &reloc->rel)) { WARN_ELF("gelf_update_rel"); return -1; } } else { reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); reloc->rela.r_addend = reloc->addend; reloc->rela.r_offset = reloc->offset; if (!gelf_update_rela(sec->data, reloc->idx, &reloc->rela)) { WARN_ELF("gelf_update_rela"); return -1; } |
fdabdd0b0 objtool: Provide ... |
894 895 896 897 898 899 |
} elf->changed = true; return 0; } |
2b10be23a objtool: Clean up... |
900 |
int elf_write(struct elf *elf) |
627fce148 objtool: Add ORC ... |
901 902 903 |
{ struct section *sec; Elf_Scn *s; |
97dab2ae7 objtool: Fix obje... |
904 |
/* Update section headers for changed sections: */ |
627fce148 objtool: Add ORC ... |
905 906 907 908 909 910 911 |
list_for_each_entry(sec, &elf->sections, list) { if (sec->changed) { s = elf_getscn(elf->elf, sec->idx); if (!s) { WARN_ELF("elf_getscn"); return -1; } |
97dab2ae7 objtool: Fix obje... |
912 |
if (!gelf_update_shdr(s, &sec->sh)) { |
627fce148 objtool: Add ORC ... |
913 914 915 |
WARN_ELF("gelf_update_shdr"); return -1; } |
2b10be23a objtool: Clean up... |
916 917 |
sec->changed = false; |
627fce148 objtool: Add ORC ... |
918 919 |
} } |
97dab2ae7 objtool: Fix obje... |
920 921 922 923 |
/* Make sure the new section header entries get updated properly. */ elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY); /* Write all changes to the file. */ |
627fce148 objtool: Add ORC ... |
924 925 926 927 |
if (elf_update(elf->elf, ELF_C_WRITE) < 0) { WARN_ELF("elf_update"); return -1; } |
2b10be23a objtool: Clean up... |
928 |
elf->changed = false; |
627fce148 objtool: Add ORC ... |
929 930 |
return 0; } |
442f04c34 objtool: Add tool... |
931 932 933 934 |
void elf_close(struct elf *elf) { struct section *sec, *tmpsec; struct symbol *sym, *tmpsym; |
f19742226 objtool: Rename r... |
935 |
struct reloc *reloc, *tmpreloc; |
442f04c34 objtool: Add tool... |
936 |
|
baa41469a objtool: Implemen... |
937 938 939 940 941 |
if (elf->elf) elf_end(elf->elf); if (elf->fd > 0) close(elf->fd); |
442f04c34 objtool: Add tool... |
942 |
list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { |
a196e1719 objtool: Rename s... |
943 |
list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { |
442f04c34 objtool: Add tool... |
944 |
list_del(&sym->list); |
042ba73fe objtool: Add seve... |
945 |
hash_del(&sym->hash); |
442f04c34 objtool: Add tool... |
946 947 |
free(sym); } |
f19742226 objtool: Rename r... |
948 949 950 951 |
list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) { list_del(&reloc->list); hash_del(&reloc->hash); free(reloc); |
442f04c34 objtool: Add tool... |
952 953 954 955 |
} list_del(&sec->list); free(sec); } |
baa41469a objtool: Implemen... |
956 |
|
442f04c34 objtool: Add tool... |
957 958 |
free(elf); } |