11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2442f04c3SJosh Poimboeuf /* 3442f04c3SJosh Poimboeuf * elf.c - ELF access library 4442f04c3SJosh Poimboeuf * 5442f04c3SJosh Poimboeuf * Adapted from kpatch (https://github.com/dynup/kpatch): 6442f04c3SJosh Poimboeuf * Copyright (C) 2013-2015 Josh Poimboeuf <jpoimboe@redhat.com> 7442f04c3SJosh Poimboeuf * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com> 8442f04c3SJosh Poimboeuf */ 9442f04c3SJosh Poimboeuf 10442f04c3SJosh Poimboeuf #include <sys/types.h> 11442f04c3SJosh Poimboeuf #include <sys/stat.h> 12442f04c3SJosh Poimboeuf #include <fcntl.h> 13442f04c3SJosh Poimboeuf #include <stdio.h> 14442f04c3SJosh Poimboeuf #include <stdlib.h> 15442f04c3SJosh Poimboeuf #include <string.h> 16442f04c3SJosh Poimboeuf #include <unistd.h> 17385d11b1SJosh Poimboeuf #include <errno.h> 181e11f3fdSPeter Zijlstra #include "builtin.h" 19442f04c3SJosh Poimboeuf 20442f04c3SJosh Poimboeuf #include "elf.h" 21442f04c3SJosh Poimboeuf #include "warn.h" 22442f04c3SJosh Poimboeuf 2322566c16SArtem Savkov #define MAX_NAME_LEN 128 2422566c16SArtem Savkov 25ae358196SPeter Zijlstra static inline u32 str_hash(const char *str) 26ae358196SPeter Zijlstra { 27ae358196SPeter Zijlstra return jhash(str, strlen(str), 0); 28ae358196SPeter Zijlstra } 29ae358196SPeter Zijlstra 3034f7c96dSPeter Zijlstra static inline int elf_hash_bits(void) 3134f7c96dSPeter Zijlstra { 3234f7c96dSPeter Zijlstra return vmlinux ? ELF_HASH_BITS : 16; 3334f7c96dSPeter Zijlstra } 3434f7c96dSPeter Zijlstra 3534f7c96dSPeter Zijlstra #define elf_hash_add(hashtable, node, key) \ 3634f7c96dSPeter Zijlstra hlist_add_head(node, &hashtable[hash_min(key, elf_hash_bits())]) 3734f7c96dSPeter Zijlstra 3834f7c96dSPeter Zijlstra static void elf_hash_init(struct hlist_head *table) 3934f7c96dSPeter Zijlstra { 4034f7c96dSPeter Zijlstra __hash_init(table, 1U << elf_hash_bits()); 4134f7c96dSPeter Zijlstra } 4234f7c96dSPeter Zijlstra 4334f7c96dSPeter Zijlstra #define elf_hash_for_each_possible(name, obj, member, key) \ 4434f7c96dSPeter Zijlstra hlist_for_each_entry(obj, &name[hash_min(key, elf_hash_bits())], member) 4534f7c96dSPeter Zijlstra 462a362eccSPeter Zijlstra static void rb_add(struct rb_root *tree, struct rb_node *node, 472a362eccSPeter Zijlstra int (*cmp)(struct rb_node *, const struct rb_node *)) 482a362eccSPeter Zijlstra { 492a362eccSPeter Zijlstra struct rb_node **link = &tree->rb_node; 502a362eccSPeter Zijlstra struct rb_node *parent = NULL; 512a362eccSPeter Zijlstra 522a362eccSPeter Zijlstra while (*link) { 532a362eccSPeter Zijlstra parent = *link; 542a362eccSPeter Zijlstra if (cmp(node, parent) < 0) 552a362eccSPeter Zijlstra link = &parent->rb_left; 562a362eccSPeter Zijlstra else 572a362eccSPeter Zijlstra link = &parent->rb_right; 582a362eccSPeter Zijlstra } 592a362eccSPeter Zijlstra 602a362eccSPeter Zijlstra rb_link_node(node, parent, link); 612a362eccSPeter Zijlstra rb_insert_color(node, tree); 622a362eccSPeter Zijlstra } 632a362eccSPeter Zijlstra 64b490f453SMiroslav Benes static struct rb_node *rb_find_first(const struct rb_root *tree, const void *key, 652a362eccSPeter Zijlstra int (*cmp)(const void *key, const struct rb_node *)) 662a362eccSPeter Zijlstra { 672a362eccSPeter Zijlstra struct rb_node *node = tree->rb_node; 682a362eccSPeter Zijlstra struct rb_node *match = NULL; 692a362eccSPeter Zijlstra 702a362eccSPeter Zijlstra while (node) { 712a362eccSPeter Zijlstra int c = cmp(key, node); 722a362eccSPeter Zijlstra if (c <= 0) { 732a362eccSPeter Zijlstra if (!c) 742a362eccSPeter Zijlstra match = node; 752a362eccSPeter Zijlstra node = node->rb_left; 762a362eccSPeter Zijlstra } else if (c > 0) { 772a362eccSPeter Zijlstra node = node->rb_right; 782a362eccSPeter Zijlstra } 792a362eccSPeter Zijlstra } 802a362eccSPeter Zijlstra 812a362eccSPeter Zijlstra return match; 822a362eccSPeter Zijlstra } 832a362eccSPeter Zijlstra 842a362eccSPeter Zijlstra static struct rb_node *rb_next_match(struct rb_node *node, const void *key, 852a362eccSPeter Zijlstra int (*cmp)(const void *key, const struct rb_node *)) 862a362eccSPeter Zijlstra { 872a362eccSPeter Zijlstra node = rb_next(node); 882a362eccSPeter Zijlstra if (node && cmp(key, node)) 892a362eccSPeter Zijlstra node = NULL; 902a362eccSPeter Zijlstra return node; 912a362eccSPeter Zijlstra } 922a362eccSPeter Zijlstra 932a362eccSPeter Zijlstra #define rb_for_each(tree, node, key, cmp) \ 942a362eccSPeter Zijlstra for ((node) = rb_find_first((tree), (key), (cmp)); \ 952a362eccSPeter Zijlstra (node); (node) = rb_next_match((node), (key), (cmp))) 962a362eccSPeter Zijlstra 972a362eccSPeter Zijlstra static int symbol_to_offset(struct rb_node *a, const struct rb_node *b) 982a362eccSPeter Zijlstra { 992a362eccSPeter Zijlstra struct symbol *sa = rb_entry(a, struct symbol, node); 1002a362eccSPeter Zijlstra struct symbol *sb = rb_entry(b, struct symbol, node); 1012a362eccSPeter Zijlstra 1022a362eccSPeter Zijlstra if (sa->offset < sb->offset) 1032a362eccSPeter Zijlstra return -1; 1042a362eccSPeter Zijlstra if (sa->offset > sb->offset) 1052a362eccSPeter Zijlstra return 1; 1062a362eccSPeter Zijlstra 1072a362eccSPeter Zijlstra if (sa->len < sb->len) 1082a362eccSPeter Zijlstra return -1; 1092a362eccSPeter Zijlstra if (sa->len > sb->len) 1102a362eccSPeter Zijlstra return 1; 1112a362eccSPeter Zijlstra 1122a362eccSPeter Zijlstra sa->alias = sb; 1132a362eccSPeter Zijlstra 1142a362eccSPeter Zijlstra return 0; 1152a362eccSPeter Zijlstra } 1162a362eccSPeter Zijlstra 1172a362eccSPeter Zijlstra static int symbol_by_offset(const void *key, const struct rb_node *node) 1182a362eccSPeter Zijlstra { 1192a362eccSPeter Zijlstra const struct symbol *s = rb_entry(node, struct symbol, node); 1202a362eccSPeter Zijlstra const unsigned long *o = key; 1212a362eccSPeter Zijlstra 1222a362eccSPeter Zijlstra if (*o < s->offset) 1232a362eccSPeter Zijlstra return -1; 1245377cae9SJulien Thierry if (*o >= s->offset + s->len) 1252a362eccSPeter Zijlstra return 1; 1262a362eccSPeter Zijlstra 1272a362eccSPeter Zijlstra return 0; 1282a362eccSPeter Zijlstra } 1292a362eccSPeter Zijlstra 130894e48caSIngo Molnar struct section *find_section_by_name(const struct elf *elf, const char *name) 131442f04c3SJosh Poimboeuf { 132442f04c3SJosh Poimboeuf struct section *sec; 133442f04c3SJosh Poimboeuf 13434f7c96dSPeter Zijlstra elf_hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name)) 135442f04c3SJosh Poimboeuf if (!strcmp(sec->name, name)) 136442f04c3SJosh Poimboeuf return sec; 137442f04c3SJosh Poimboeuf 138442f04c3SJosh Poimboeuf return NULL; 139442f04c3SJosh Poimboeuf } 140442f04c3SJosh Poimboeuf 141442f04c3SJosh Poimboeuf static struct section *find_section_by_index(struct elf *elf, 142442f04c3SJosh Poimboeuf unsigned int idx) 143442f04c3SJosh Poimboeuf { 144442f04c3SJosh Poimboeuf struct section *sec; 145442f04c3SJosh Poimboeuf 14634f7c96dSPeter Zijlstra elf_hash_for_each_possible(elf->section_hash, sec, hash, idx) 147442f04c3SJosh Poimboeuf if (sec->idx == idx) 148442f04c3SJosh Poimboeuf return sec; 149442f04c3SJosh Poimboeuf 150442f04c3SJosh Poimboeuf return NULL; 151442f04c3SJosh Poimboeuf } 152442f04c3SJosh Poimboeuf 153442f04c3SJosh Poimboeuf static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) 154442f04c3SJosh Poimboeuf { 155442f04c3SJosh Poimboeuf struct symbol *sym; 156442f04c3SJosh Poimboeuf 15734f7c96dSPeter Zijlstra elf_hash_for_each_possible(elf->symbol_hash, sym, hash, idx) 158442f04c3SJosh Poimboeuf if (sym->idx == idx) 159442f04c3SJosh Poimboeuf return sym; 160442f04c3SJosh Poimboeuf 161442f04c3SJosh Poimboeuf return NULL; 162442f04c3SJosh Poimboeuf } 163442f04c3SJosh Poimboeuf 164442f04c3SJosh Poimboeuf struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) 165442f04c3SJosh Poimboeuf { 1662a362eccSPeter Zijlstra struct rb_node *node; 167442f04c3SJosh Poimboeuf 1682a362eccSPeter Zijlstra rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { 1692a362eccSPeter Zijlstra struct symbol *s = rb_entry(node, struct symbol, node); 1702a362eccSPeter Zijlstra 1712a362eccSPeter Zijlstra if (s->offset == offset && s->type != STT_SECTION) 1722a362eccSPeter Zijlstra return s; 1732a362eccSPeter Zijlstra } 1747acfe531SJosh Poimboeuf 1757acfe531SJosh Poimboeuf return NULL; 1767acfe531SJosh Poimboeuf } 1777acfe531SJosh Poimboeuf 1787acfe531SJosh Poimboeuf struct symbol *find_func_by_offset(struct section *sec, unsigned long offset) 1797acfe531SJosh Poimboeuf { 1802a362eccSPeter Zijlstra struct rb_node *node; 1817acfe531SJosh Poimboeuf 1822a362eccSPeter Zijlstra rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { 1832a362eccSPeter Zijlstra struct symbol *s = rb_entry(node, struct symbol, node); 1842a362eccSPeter Zijlstra 1852a362eccSPeter Zijlstra if (s->offset == offset && s->type == STT_FUNC) 1862a362eccSPeter Zijlstra return s; 1872a362eccSPeter Zijlstra } 1882a362eccSPeter Zijlstra 1892a362eccSPeter Zijlstra return NULL; 1902a362eccSPeter Zijlstra } 1912a362eccSPeter Zijlstra 192b490f453SMiroslav Benes struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset) 1932a362eccSPeter Zijlstra { 1942a362eccSPeter Zijlstra struct rb_node *node; 1952a362eccSPeter Zijlstra 1962a362eccSPeter Zijlstra rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { 1972a362eccSPeter Zijlstra struct symbol *s = rb_entry(node, struct symbol, node); 1982a362eccSPeter Zijlstra 1992a362eccSPeter Zijlstra if (s->type != STT_SECTION) 2002a362eccSPeter Zijlstra return s; 2012a362eccSPeter Zijlstra } 2022a362eccSPeter Zijlstra 2032a362eccSPeter Zijlstra return NULL; 2042a362eccSPeter Zijlstra } 2052a362eccSPeter Zijlstra 20653d20720SPeter Zijlstra struct symbol *find_func_containing(struct section *sec, unsigned long offset) 2072a362eccSPeter Zijlstra { 2082a362eccSPeter Zijlstra struct rb_node *node; 2092a362eccSPeter Zijlstra 2102a362eccSPeter Zijlstra rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { 2112a362eccSPeter Zijlstra struct symbol *s = rb_entry(node, struct symbol, node); 2122a362eccSPeter Zijlstra 2132a362eccSPeter Zijlstra if (s->type == STT_FUNC) 2142a362eccSPeter Zijlstra return s; 2152a362eccSPeter Zijlstra } 216442f04c3SJosh Poimboeuf 217442f04c3SJosh Poimboeuf return NULL; 218442f04c3SJosh Poimboeuf } 219442f04c3SJosh Poimboeuf 220894e48caSIngo Molnar struct symbol *find_symbol_by_name(const struct elf *elf, const char *name) 22113810435SJosh Poimboeuf { 22213810435SJosh Poimboeuf struct symbol *sym; 22313810435SJosh Poimboeuf 22434f7c96dSPeter Zijlstra elf_hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name)) 22513810435SJosh Poimboeuf if (!strcmp(sym->name, name)) 22613810435SJosh Poimboeuf return sym; 22713810435SJosh Poimboeuf 22813810435SJosh Poimboeuf return NULL; 22913810435SJosh Poimboeuf } 23013810435SJosh Poimboeuf 231f1974222SMatt Helsley struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, 2328b5fa6bcSPeter Zijlstra unsigned long offset, unsigned int len) 233442f04c3SJosh Poimboeuf { 234f1974222SMatt Helsley struct reloc *reloc, *r = NULL; 235042ba73fSJosh Poimboeuf unsigned long o; 236442f04c3SJosh Poimboeuf 237f1974222SMatt Helsley if (!sec->reloc) 238442f04c3SJosh Poimboeuf return NULL; 239442f04c3SJosh Poimboeuf 240f1974222SMatt Helsley sec = sec->reloc; 2418b5fa6bcSPeter Zijlstra 24274b873e4SPeter Zijlstra for_offset_range(o, offset, offset + len) { 243f1974222SMatt Helsley elf_hash_for_each_possible(elf->reloc_hash, reloc, hash, 2448b5fa6bcSPeter Zijlstra sec_offset_hash(sec, o)) { 245f1974222SMatt Helsley if (reloc->sec != sec) 24674b873e4SPeter Zijlstra continue; 24774b873e4SPeter Zijlstra 248f1974222SMatt Helsley if (reloc->offset >= offset && reloc->offset < offset + len) { 249f1974222SMatt Helsley if (!r || reloc->offset < r->offset) 250f1974222SMatt Helsley r = reloc; 2518b5fa6bcSPeter Zijlstra } 2528b5fa6bcSPeter Zijlstra } 25374b873e4SPeter Zijlstra if (r) 25474b873e4SPeter Zijlstra return r; 25574b873e4SPeter Zijlstra } 256442f04c3SJosh Poimboeuf 257442f04c3SJosh Poimboeuf return NULL; 258442f04c3SJosh Poimboeuf } 259442f04c3SJosh Poimboeuf 260f1974222SMatt Helsley struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset) 261442f04c3SJosh Poimboeuf { 262f1974222SMatt Helsley return find_reloc_by_dest_range(elf, sec, offset, 1); 263442f04c3SJosh Poimboeuf } 264442f04c3SJosh Poimboeuf 26544f6a7c0SJosh Poimboeuf void insn_to_reloc_sym_addend(struct section *sec, unsigned long offset, 26644f6a7c0SJosh Poimboeuf struct reloc *reloc) 26744f6a7c0SJosh Poimboeuf { 26844f6a7c0SJosh Poimboeuf if (sec->sym) { 26944f6a7c0SJosh Poimboeuf reloc->sym = sec->sym; 27044f6a7c0SJosh Poimboeuf reloc->addend = offset; 27144f6a7c0SJosh Poimboeuf return; 27244f6a7c0SJosh Poimboeuf } 27344f6a7c0SJosh Poimboeuf 27444f6a7c0SJosh Poimboeuf /* 27544f6a7c0SJosh Poimboeuf * The Clang assembler strips section symbols, so we have to reference 27644f6a7c0SJosh Poimboeuf * the function symbol instead: 27744f6a7c0SJosh Poimboeuf */ 27844f6a7c0SJosh Poimboeuf reloc->sym = find_symbol_containing(sec, offset); 27944f6a7c0SJosh Poimboeuf if (!reloc->sym) { 28044f6a7c0SJosh Poimboeuf /* 28144f6a7c0SJosh Poimboeuf * Hack alert. This happens when we need to reference the NOP 28244f6a7c0SJosh Poimboeuf * pad insn immediately after the function. 28344f6a7c0SJosh Poimboeuf */ 28444f6a7c0SJosh Poimboeuf reloc->sym = find_symbol_containing(sec, offset - 1); 28544f6a7c0SJosh Poimboeuf } 28644f6a7c0SJosh Poimboeuf 28744f6a7c0SJosh Poimboeuf if (reloc->sym) 28844f6a7c0SJosh Poimboeuf reloc->addend = offset - reloc->sym->offset; 28944f6a7c0SJosh Poimboeuf } 29044f6a7c0SJosh Poimboeuf 291442f04c3SJosh Poimboeuf static int read_sections(struct elf *elf) 292442f04c3SJosh Poimboeuf { 293442f04c3SJosh Poimboeuf Elf_Scn *s = NULL; 294442f04c3SJosh Poimboeuf struct section *sec; 295442f04c3SJosh Poimboeuf size_t shstrndx, sections_nr; 296442f04c3SJosh Poimboeuf int i; 297442f04c3SJosh Poimboeuf 298442f04c3SJosh Poimboeuf if (elf_getshdrnum(elf->elf, §ions_nr)) { 299baa41469SJosh Poimboeuf WARN_ELF("elf_getshdrnum"); 300442f04c3SJosh Poimboeuf return -1; 301442f04c3SJosh Poimboeuf } 302442f04c3SJosh Poimboeuf 303442f04c3SJosh Poimboeuf if (elf_getshdrstrndx(elf->elf, &shstrndx)) { 304baa41469SJosh Poimboeuf WARN_ELF("elf_getshdrstrndx"); 305442f04c3SJosh Poimboeuf return -1; 306442f04c3SJosh Poimboeuf } 307442f04c3SJosh Poimboeuf 308442f04c3SJosh Poimboeuf for (i = 0; i < sections_nr; i++) { 309442f04c3SJosh Poimboeuf sec = malloc(sizeof(*sec)); 310442f04c3SJosh Poimboeuf if (!sec) { 311442f04c3SJosh Poimboeuf perror("malloc"); 312442f04c3SJosh Poimboeuf return -1; 313442f04c3SJosh Poimboeuf } 314442f04c3SJosh Poimboeuf memset(sec, 0, sizeof(*sec)); 315442f04c3SJosh Poimboeuf 316a196e171SJosh Poimboeuf INIT_LIST_HEAD(&sec->symbol_list); 317f1974222SMatt Helsley INIT_LIST_HEAD(&sec->reloc_list); 318442f04c3SJosh Poimboeuf 319442f04c3SJosh Poimboeuf s = elf_getscn(elf->elf, i); 320442f04c3SJosh Poimboeuf if (!s) { 321baa41469SJosh Poimboeuf WARN_ELF("elf_getscn"); 322442f04c3SJosh Poimboeuf return -1; 323442f04c3SJosh Poimboeuf } 324442f04c3SJosh Poimboeuf 325442f04c3SJosh Poimboeuf sec->idx = elf_ndxscn(s); 326442f04c3SJosh Poimboeuf 327442f04c3SJosh Poimboeuf if (!gelf_getshdr(s, &sec->sh)) { 328baa41469SJosh Poimboeuf WARN_ELF("gelf_getshdr"); 329442f04c3SJosh Poimboeuf return -1; 330442f04c3SJosh Poimboeuf } 331442f04c3SJosh Poimboeuf 332442f04c3SJosh Poimboeuf sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name); 333442f04c3SJosh Poimboeuf if (!sec->name) { 334baa41469SJosh Poimboeuf WARN_ELF("elf_strptr"); 335442f04c3SJosh Poimboeuf return -1; 336442f04c3SJosh Poimboeuf } 337442f04c3SJosh Poimboeuf 338df968c93SPetr Vandrovec if (sec->sh.sh_size != 0) { 339baa41469SJosh Poimboeuf sec->data = elf_getdata(s, NULL); 340baa41469SJosh Poimboeuf if (!sec->data) { 341baa41469SJosh Poimboeuf WARN_ELF("elf_getdata"); 342442f04c3SJosh Poimboeuf return -1; 343442f04c3SJosh Poimboeuf } 344baa41469SJosh Poimboeuf if (sec->data->d_off != 0 || 345baa41469SJosh Poimboeuf sec->data->d_size != sec->sh.sh_size) { 346df968c93SPetr Vandrovec WARN("unexpected data attributes for %s", 347df968c93SPetr Vandrovec sec->name); 348442f04c3SJosh Poimboeuf return -1; 349442f04c3SJosh Poimboeuf } 350df968c93SPetr Vandrovec } 351df968c93SPetr Vandrovec sec->len = sec->sh.sh_size; 35253038996SPeter Zijlstra 35353038996SPeter Zijlstra list_add_tail(&sec->list, &elf->sections); 35434f7c96dSPeter Zijlstra elf_hash_add(elf->section_hash, &sec->hash, sec->idx); 35534f7c96dSPeter Zijlstra elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name)); 356442f04c3SJosh Poimboeuf } 357442f04c3SJosh Poimboeuf 3581e11f3fdSPeter Zijlstra if (stats) 3591e11f3fdSPeter Zijlstra printf("nr_sections: %lu\n", (unsigned long)sections_nr); 3601e11f3fdSPeter Zijlstra 361442f04c3SJosh Poimboeuf /* sanity check, one more call to elf_nextscn() should return NULL */ 362442f04c3SJosh Poimboeuf if (elf_nextscn(elf->elf, s)) { 363442f04c3SJosh Poimboeuf WARN("section entry mismatch"); 364442f04c3SJosh Poimboeuf return -1; 365442f04c3SJosh Poimboeuf } 366442f04c3SJosh Poimboeuf 367442f04c3SJosh Poimboeuf return 0; 368442f04c3SJosh Poimboeuf } 369442f04c3SJosh Poimboeuf 370442f04c3SJosh Poimboeuf static int read_symbols(struct elf *elf) 371442f04c3SJosh Poimboeuf { 37228fe1d7bSSami Tolvanen struct section *symtab, *symtab_shndx, *sec; 3732a362eccSPeter Zijlstra struct symbol *sym, *pfunc; 3742a362eccSPeter Zijlstra struct list_head *entry; 3752a362eccSPeter Zijlstra struct rb_node *pnode; 376442f04c3SJosh Poimboeuf int symbols_nr, i; 37713810435SJosh Poimboeuf char *coldstr; 37828fe1d7bSSami Tolvanen Elf_Data *shndx_data = NULL; 37928fe1d7bSSami Tolvanen Elf32_Word shndx; 380442f04c3SJosh Poimboeuf 381442f04c3SJosh Poimboeuf symtab = find_section_by_name(elf, ".symtab"); 382442f04c3SJosh Poimboeuf if (!symtab) { 383442f04c3SJosh Poimboeuf WARN("missing symbol table"); 384442f04c3SJosh Poimboeuf return -1; 385442f04c3SJosh Poimboeuf } 386442f04c3SJosh Poimboeuf 38728fe1d7bSSami Tolvanen symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); 38828fe1d7bSSami Tolvanen if (symtab_shndx) 38928fe1d7bSSami Tolvanen shndx_data = symtab_shndx->data; 39028fe1d7bSSami Tolvanen 391442f04c3SJosh Poimboeuf symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize; 392442f04c3SJosh Poimboeuf 393442f04c3SJosh Poimboeuf for (i = 0; i < symbols_nr; i++) { 394442f04c3SJosh Poimboeuf sym = malloc(sizeof(*sym)); 395442f04c3SJosh Poimboeuf if (!sym) { 396442f04c3SJosh Poimboeuf perror("malloc"); 397442f04c3SJosh Poimboeuf return -1; 398442f04c3SJosh Poimboeuf } 399442f04c3SJosh Poimboeuf memset(sym, 0, sizeof(*sym)); 4002a362eccSPeter Zijlstra sym->alias = sym; 401442f04c3SJosh Poimboeuf 402442f04c3SJosh Poimboeuf sym->idx = i; 403442f04c3SJosh Poimboeuf 40428fe1d7bSSami Tolvanen if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym, 40528fe1d7bSSami Tolvanen &shndx)) { 40628fe1d7bSSami Tolvanen WARN_ELF("gelf_getsymshndx"); 407442f04c3SJosh Poimboeuf goto err; 408442f04c3SJosh Poimboeuf } 409442f04c3SJosh Poimboeuf 410442f04c3SJosh Poimboeuf sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, 411442f04c3SJosh Poimboeuf sym->sym.st_name); 412442f04c3SJosh Poimboeuf if (!sym->name) { 413baa41469SJosh Poimboeuf WARN_ELF("elf_strptr"); 414442f04c3SJosh Poimboeuf goto err; 415442f04c3SJosh Poimboeuf } 416442f04c3SJosh Poimboeuf 417442f04c3SJosh Poimboeuf sym->type = GELF_ST_TYPE(sym->sym.st_info); 418442f04c3SJosh Poimboeuf sym->bind = GELF_ST_BIND(sym->sym.st_info); 419442f04c3SJosh Poimboeuf 42028fe1d7bSSami Tolvanen if ((sym->sym.st_shndx > SHN_UNDEF && 42128fe1d7bSSami Tolvanen sym->sym.st_shndx < SHN_LORESERVE) || 42228fe1d7bSSami Tolvanen (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) { 42328fe1d7bSSami Tolvanen if (sym->sym.st_shndx != SHN_XINDEX) 42428fe1d7bSSami Tolvanen shndx = sym->sym.st_shndx; 42528fe1d7bSSami Tolvanen 42628fe1d7bSSami Tolvanen sym->sec = find_section_by_index(elf, shndx); 427442f04c3SJosh Poimboeuf if (!sym->sec) { 428442f04c3SJosh Poimboeuf WARN("couldn't find section for symbol %s", 429442f04c3SJosh Poimboeuf sym->name); 430442f04c3SJosh Poimboeuf goto err; 431442f04c3SJosh Poimboeuf } 432442f04c3SJosh Poimboeuf if (sym->type == STT_SECTION) { 433442f04c3SJosh Poimboeuf sym->name = sym->sec->name; 434442f04c3SJosh Poimboeuf sym->sec->sym = sym; 435442f04c3SJosh Poimboeuf } 436442f04c3SJosh Poimboeuf } else 437442f04c3SJosh Poimboeuf sym->sec = find_section_by_index(elf, 0); 438442f04c3SJosh Poimboeuf 439442f04c3SJosh Poimboeuf sym->offset = sym->sym.st_value; 440442f04c3SJosh Poimboeuf sym->len = sym->sym.st_size; 441442f04c3SJosh Poimboeuf 4422a362eccSPeter Zijlstra rb_add(&sym->sec->symbol_tree, &sym->node, symbol_to_offset); 4432a362eccSPeter Zijlstra pnode = rb_prev(&sym->node); 4442a362eccSPeter Zijlstra if (pnode) 4452a362eccSPeter Zijlstra entry = &rb_entry(pnode, struct symbol, node)->list; 4462a362eccSPeter Zijlstra else 447a196e171SJosh Poimboeuf entry = &sym->sec->symbol_list; 448442f04c3SJosh Poimboeuf list_add(&sym->list, entry); 44934f7c96dSPeter Zijlstra elf_hash_add(elf->symbol_hash, &sym->hash, sym->idx); 45034f7c96dSPeter Zijlstra elf_hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name)); 451442f04c3SJosh Poimboeuf } 452442f04c3SJosh Poimboeuf 4531e11f3fdSPeter Zijlstra if (stats) 4541e11f3fdSPeter Zijlstra printf("nr_symbols: %lu\n", (unsigned long)symbols_nr); 4551e11f3fdSPeter Zijlstra 45613810435SJosh Poimboeuf /* Create parent/child links for any cold subfunctions */ 45713810435SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 45813810435SJosh Poimboeuf list_for_each_entry(sym, &sec->symbol_list, list) { 45922566c16SArtem Savkov char pname[MAX_NAME_LEN + 1]; 46022566c16SArtem Savkov size_t pnamelen; 46113810435SJosh Poimboeuf if (sym->type != STT_FUNC) 46213810435SJosh Poimboeuf continue; 463e000acc1SKristen Carlson Accardi 464e000acc1SKristen Carlson Accardi if (sym->pfunc == NULL) 465e000acc1SKristen Carlson Accardi sym->pfunc = sym; 466e000acc1SKristen Carlson Accardi 467e000acc1SKristen Carlson Accardi if (sym->cfunc == NULL) 468e000acc1SKristen Carlson Accardi sym->cfunc = sym; 469e000acc1SKristen Carlson Accardi 470bcb6fb5dSJosh Poimboeuf coldstr = strstr(sym->name, ".cold"); 47108b393d0SJosh Poimboeuf if (!coldstr) 47208b393d0SJosh Poimboeuf continue; 47308b393d0SJosh Poimboeuf 47422566c16SArtem Savkov pnamelen = coldstr - sym->name; 47522566c16SArtem Savkov if (pnamelen > MAX_NAME_LEN) { 47622566c16SArtem Savkov WARN("%s(): parent function name exceeds maximum length of %d characters", 47722566c16SArtem Savkov sym->name, MAX_NAME_LEN); 47822566c16SArtem Savkov return -1; 47922566c16SArtem Savkov } 48022566c16SArtem Savkov 48122566c16SArtem Savkov strncpy(pname, sym->name, pnamelen); 48222566c16SArtem Savkov pname[pnamelen] = '\0'; 48322566c16SArtem Savkov pfunc = find_symbol_by_name(elf, pname); 48413810435SJosh Poimboeuf 48513810435SJosh Poimboeuf if (!pfunc) { 48613810435SJosh Poimboeuf WARN("%s(): can't find parent function", 48713810435SJosh Poimboeuf sym->name); 4880b9301fbSArtem Savkov return -1; 48913810435SJosh Poimboeuf } 49013810435SJosh Poimboeuf 49113810435SJosh Poimboeuf sym->pfunc = pfunc; 49213810435SJosh Poimboeuf pfunc->cfunc = sym; 49308b393d0SJosh Poimboeuf 49408b393d0SJosh Poimboeuf /* 49508b393d0SJosh Poimboeuf * Unfortunately, -fnoreorder-functions puts the child 49608b393d0SJosh Poimboeuf * inside the parent. Remove the overlap so we can 49708b393d0SJosh Poimboeuf * have sane assumptions. 49808b393d0SJosh Poimboeuf * 49908b393d0SJosh Poimboeuf * Note that pfunc->len now no longer matches 50008b393d0SJosh Poimboeuf * pfunc->sym.st_size. 50108b393d0SJosh Poimboeuf */ 50208b393d0SJosh Poimboeuf if (sym->sec == pfunc->sec && 50308b393d0SJosh Poimboeuf sym->offset >= pfunc->offset && 50408b393d0SJosh Poimboeuf sym->offset + sym->len == pfunc->offset + pfunc->len) { 50508b393d0SJosh Poimboeuf pfunc->len -= sym->len; 50613810435SJosh Poimboeuf } 50713810435SJosh Poimboeuf } 50813810435SJosh Poimboeuf } 50913810435SJosh Poimboeuf 510442f04c3SJosh Poimboeuf return 0; 511442f04c3SJosh Poimboeuf 512442f04c3SJosh Poimboeuf err: 513442f04c3SJosh Poimboeuf free(sym); 514442f04c3SJosh Poimboeuf return -1; 515442f04c3SJosh Poimboeuf } 516442f04c3SJosh Poimboeuf 517f1974222SMatt Helsley void elf_add_reloc(struct elf *elf, struct reloc *reloc) 51834f7c96dSPeter Zijlstra { 519f1974222SMatt Helsley struct section *sec = reloc->sec; 52034f7c96dSPeter Zijlstra 521f1974222SMatt Helsley list_add_tail(&reloc->list, &sec->reloc_list); 522f1974222SMatt Helsley elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc)); 52334f7c96dSPeter Zijlstra } 52434f7c96dSPeter Zijlstra 525fb414783SMatt Helsley static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) 526fb414783SMatt Helsley { 527fb414783SMatt Helsley if (!gelf_getrel(sec->data, i, &reloc->rel)) { 528fb414783SMatt Helsley WARN_ELF("gelf_getrel"); 529fb414783SMatt Helsley return -1; 530fb414783SMatt Helsley } 531fb414783SMatt Helsley reloc->type = GELF_R_TYPE(reloc->rel.r_info); 532fb414783SMatt Helsley reloc->addend = 0; 533fb414783SMatt Helsley reloc->offset = reloc->rel.r_offset; 534fb414783SMatt Helsley *symndx = GELF_R_SYM(reloc->rel.r_info); 535fb414783SMatt Helsley return 0; 536fb414783SMatt Helsley } 537fb414783SMatt Helsley 538fb414783SMatt Helsley static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) 539fb414783SMatt Helsley { 540fb414783SMatt Helsley if (!gelf_getrela(sec->data, i, &reloc->rela)) { 541fb414783SMatt Helsley WARN_ELF("gelf_getrela"); 542fb414783SMatt Helsley return -1; 543fb414783SMatt Helsley } 544fb414783SMatt Helsley reloc->type = GELF_R_TYPE(reloc->rela.r_info); 545fb414783SMatt Helsley reloc->addend = reloc->rela.r_addend; 546fb414783SMatt Helsley reloc->offset = reloc->rela.r_offset; 547fb414783SMatt Helsley *symndx = GELF_R_SYM(reloc->rela.r_info); 548fb414783SMatt Helsley return 0; 549fb414783SMatt Helsley } 550fb414783SMatt Helsley 551f1974222SMatt Helsley static int read_relocs(struct elf *elf) 552442f04c3SJosh Poimboeuf { 553442f04c3SJosh Poimboeuf struct section *sec; 554f1974222SMatt Helsley struct reloc *reloc; 555442f04c3SJosh Poimboeuf int i; 556442f04c3SJosh Poimboeuf unsigned int symndx; 557f1974222SMatt Helsley unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0; 558442f04c3SJosh Poimboeuf 559442f04c3SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 560fb414783SMatt Helsley if ((sec->sh.sh_type != SHT_RELA) && 561fb414783SMatt Helsley (sec->sh.sh_type != SHT_REL)) 562442f04c3SJosh Poimboeuf continue; 563442f04c3SJosh Poimboeuf 5641e968bf5SSami Tolvanen sec->base = find_section_by_index(elf, sec->sh.sh_info); 565442f04c3SJosh Poimboeuf if (!sec->base) { 566f1974222SMatt Helsley WARN("can't find base section for reloc section %s", 567442f04c3SJosh Poimboeuf sec->name); 568442f04c3SJosh Poimboeuf return -1; 569442f04c3SJosh Poimboeuf } 570442f04c3SJosh Poimboeuf 571f1974222SMatt Helsley sec->base->reloc = sec; 572442f04c3SJosh Poimboeuf 573f1974222SMatt Helsley nr_reloc = 0; 574442f04c3SJosh Poimboeuf for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { 575f1974222SMatt Helsley reloc = malloc(sizeof(*reloc)); 576f1974222SMatt Helsley if (!reloc) { 577442f04c3SJosh Poimboeuf perror("malloc"); 578442f04c3SJosh Poimboeuf return -1; 579442f04c3SJosh Poimboeuf } 580f1974222SMatt Helsley memset(reloc, 0, sizeof(*reloc)); 581fb414783SMatt Helsley switch (sec->sh.sh_type) { 582fb414783SMatt Helsley case SHT_REL: 583fb414783SMatt Helsley if (read_rel_reloc(sec, i, reloc, &symndx)) 584442f04c3SJosh Poimboeuf return -1; 585fb414783SMatt Helsley break; 586fb414783SMatt Helsley case SHT_RELA: 587fb414783SMatt Helsley if (read_rela_reloc(sec, i, reloc, &symndx)) 588fb414783SMatt Helsley return -1; 589fb414783SMatt Helsley break; 590fb414783SMatt Helsley default: return -1; 591442f04c3SJosh Poimboeuf } 592442f04c3SJosh Poimboeuf 593f1974222SMatt Helsley reloc->sec = sec; 594d832c005SPeter Zijlstra reloc->idx = i; 595d832c005SPeter Zijlstra reloc->sym = find_symbol_by_index(elf, symndx); 596f1974222SMatt Helsley if (!reloc->sym) { 597f1974222SMatt Helsley WARN("can't find reloc entry symbol %d for %s", 598442f04c3SJosh Poimboeuf symndx, sec->name); 599442f04c3SJosh Poimboeuf return -1; 600442f04c3SJosh Poimboeuf } 601042ba73fSJosh Poimboeuf 602f1974222SMatt Helsley elf_add_reloc(elf, reloc); 603f1974222SMatt Helsley nr_reloc++; 604442f04c3SJosh Poimboeuf } 605f1974222SMatt Helsley max_reloc = max(max_reloc, nr_reloc); 606f1974222SMatt Helsley tot_reloc += nr_reloc; 6071e11f3fdSPeter Zijlstra } 6081e11f3fdSPeter Zijlstra 6091e11f3fdSPeter Zijlstra if (stats) { 610f1974222SMatt Helsley printf("max_reloc: %lu\n", max_reloc); 611f1974222SMatt Helsley printf("tot_reloc: %lu\n", tot_reloc); 612442f04c3SJosh Poimboeuf } 613442f04c3SJosh Poimboeuf 614442f04c3SJosh Poimboeuf return 0; 615442f04c3SJosh Poimboeuf } 616442f04c3SJosh Poimboeuf 617bc359ff2SIngo Molnar struct elf *elf_open_read(const char *name, int flags) 618442f04c3SJosh Poimboeuf { 619442f04c3SJosh Poimboeuf struct elf *elf; 620627fce14SJosh Poimboeuf Elf_Cmd cmd; 621442f04c3SJosh Poimboeuf 622442f04c3SJosh Poimboeuf elf_version(EV_CURRENT); 623442f04c3SJosh Poimboeuf 624442f04c3SJosh Poimboeuf elf = malloc(sizeof(*elf)); 625442f04c3SJosh Poimboeuf if (!elf) { 626442f04c3SJosh Poimboeuf perror("malloc"); 627442f04c3SJosh Poimboeuf return NULL; 628442f04c3SJosh Poimboeuf } 62934f7c96dSPeter Zijlstra memset(elf, 0, offsetof(struct elf, sections)); 630442f04c3SJosh Poimboeuf 631442f04c3SJosh Poimboeuf INIT_LIST_HEAD(&elf->sections); 632442f04c3SJosh Poimboeuf 63334f7c96dSPeter Zijlstra elf_hash_init(elf->symbol_hash); 63434f7c96dSPeter Zijlstra elf_hash_init(elf->symbol_name_hash); 63534f7c96dSPeter Zijlstra elf_hash_init(elf->section_hash); 63634f7c96dSPeter Zijlstra elf_hash_init(elf->section_name_hash); 637f1974222SMatt Helsley elf_hash_init(elf->reloc_hash); 63834f7c96dSPeter Zijlstra 639627fce14SJosh Poimboeuf elf->fd = open(name, flags); 640442f04c3SJosh Poimboeuf if (elf->fd == -1) { 641385d11b1SJosh Poimboeuf fprintf(stderr, "objtool: Can't open '%s': %s\n", 642385d11b1SJosh Poimboeuf name, strerror(errno)); 643442f04c3SJosh Poimboeuf goto err; 644442f04c3SJosh Poimboeuf } 645442f04c3SJosh Poimboeuf 646627fce14SJosh Poimboeuf if ((flags & O_ACCMODE) == O_RDONLY) 647627fce14SJosh Poimboeuf cmd = ELF_C_READ_MMAP; 648627fce14SJosh Poimboeuf else if ((flags & O_ACCMODE) == O_RDWR) 649627fce14SJosh Poimboeuf cmd = ELF_C_RDWR; 650627fce14SJosh Poimboeuf else /* O_WRONLY */ 651627fce14SJosh Poimboeuf cmd = ELF_C_WRITE; 652627fce14SJosh Poimboeuf 653627fce14SJosh Poimboeuf elf->elf = elf_begin(elf->fd, cmd, NULL); 654442f04c3SJosh Poimboeuf if (!elf->elf) { 655baa41469SJosh Poimboeuf WARN_ELF("elf_begin"); 656442f04c3SJosh Poimboeuf goto err; 657442f04c3SJosh Poimboeuf } 658442f04c3SJosh Poimboeuf 659442f04c3SJosh Poimboeuf if (!gelf_getehdr(elf->elf, &elf->ehdr)) { 660baa41469SJosh Poimboeuf WARN_ELF("gelf_getehdr"); 661442f04c3SJosh Poimboeuf goto err; 662442f04c3SJosh Poimboeuf } 663442f04c3SJosh Poimboeuf 664442f04c3SJosh Poimboeuf if (read_sections(elf)) 665442f04c3SJosh Poimboeuf goto err; 666442f04c3SJosh Poimboeuf 667442f04c3SJosh Poimboeuf if (read_symbols(elf)) 668442f04c3SJosh Poimboeuf goto err; 669442f04c3SJosh Poimboeuf 670f1974222SMatt Helsley if (read_relocs(elf)) 671442f04c3SJosh Poimboeuf goto err; 672442f04c3SJosh Poimboeuf 673442f04c3SJosh Poimboeuf return elf; 674442f04c3SJosh Poimboeuf 675442f04c3SJosh Poimboeuf err: 676442f04c3SJosh Poimboeuf elf_close(elf); 677442f04c3SJosh Poimboeuf return NULL; 678442f04c3SJosh Poimboeuf } 679442f04c3SJosh Poimboeuf 680627fce14SJosh Poimboeuf struct section *elf_create_section(struct elf *elf, const char *name, 6811e7e4788SJosh Poimboeuf unsigned int sh_flags, size_t entsize, int nr) 682627fce14SJosh Poimboeuf { 683627fce14SJosh Poimboeuf struct section *sec, *shstrtab; 684627fce14SJosh Poimboeuf size_t size = entsize * nr; 6853c3ea503SMichael Forney Elf_Scn *s; 686627fce14SJosh Poimboeuf Elf_Data *data; 687627fce14SJosh Poimboeuf 688627fce14SJosh Poimboeuf sec = malloc(sizeof(*sec)); 689627fce14SJosh Poimboeuf if (!sec) { 690627fce14SJosh Poimboeuf perror("malloc"); 691627fce14SJosh Poimboeuf return NULL; 692627fce14SJosh Poimboeuf } 693627fce14SJosh Poimboeuf memset(sec, 0, sizeof(*sec)); 694627fce14SJosh Poimboeuf 695627fce14SJosh Poimboeuf INIT_LIST_HEAD(&sec->symbol_list); 696f1974222SMatt Helsley INIT_LIST_HEAD(&sec->reloc_list); 697627fce14SJosh Poimboeuf 698627fce14SJosh Poimboeuf s = elf_newscn(elf->elf); 699627fce14SJosh Poimboeuf if (!s) { 700627fce14SJosh Poimboeuf WARN_ELF("elf_newscn"); 701627fce14SJosh Poimboeuf return NULL; 702627fce14SJosh Poimboeuf } 703627fce14SJosh Poimboeuf 704627fce14SJosh Poimboeuf sec->name = strdup(name); 705627fce14SJosh Poimboeuf if (!sec->name) { 706627fce14SJosh Poimboeuf perror("strdup"); 707627fce14SJosh Poimboeuf return NULL; 708627fce14SJosh Poimboeuf } 709627fce14SJosh Poimboeuf 710627fce14SJosh Poimboeuf sec->idx = elf_ndxscn(s); 711627fce14SJosh Poimboeuf sec->len = size; 712627fce14SJosh Poimboeuf sec->changed = true; 713627fce14SJosh Poimboeuf 714627fce14SJosh Poimboeuf sec->data = elf_newdata(s); 715627fce14SJosh Poimboeuf if (!sec->data) { 716627fce14SJosh Poimboeuf WARN_ELF("elf_newdata"); 717627fce14SJosh Poimboeuf return NULL; 718627fce14SJosh Poimboeuf } 719627fce14SJosh Poimboeuf 720627fce14SJosh Poimboeuf sec->data->d_size = size; 721627fce14SJosh Poimboeuf sec->data->d_align = 1; 722627fce14SJosh Poimboeuf 723627fce14SJosh Poimboeuf if (size) { 724627fce14SJosh Poimboeuf sec->data->d_buf = malloc(size); 725627fce14SJosh Poimboeuf if (!sec->data->d_buf) { 726627fce14SJosh Poimboeuf perror("malloc"); 727627fce14SJosh Poimboeuf return NULL; 728627fce14SJosh Poimboeuf } 729627fce14SJosh Poimboeuf memset(sec->data->d_buf, 0, size); 730627fce14SJosh Poimboeuf } 731627fce14SJosh Poimboeuf 732627fce14SJosh Poimboeuf if (!gelf_getshdr(s, &sec->sh)) { 733627fce14SJosh Poimboeuf WARN_ELF("gelf_getshdr"); 734627fce14SJosh Poimboeuf return NULL; 735627fce14SJosh Poimboeuf } 736627fce14SJosh Poimboeuf 737627fce14SJosh Poimboeuf sec->sh.sh_size = size; 738627fce14SJosh Poimboeuf sec->sh.sh_entsize = entsize; 739627fce14SJosh Poimboeuf sec->sh.sh_type = SHT_PROGBITS; 740627fce14SJosh Poimboeuf sec->sh.sh_addralign = 1; 7411e7e4788SJosh Poimboeuf sec->sh.sh_flags = SHF_ALLOC | sh_flags; 742627fce14SJosh Poimboeuf 743627fce14SJosh Poimboeuf 7446d77d3b4SSimon Ser /* Add section name to .shstrtab (or .strtab for Clang) */ 745627fce14SJosh Poimboeuf shstrtab = find_section_by_name(elf, ".shstrtab"); 7466d77d3b4SSimon Ser if (!shstrtab) 7476d77d3b4SSimon Ser shstrtab = find_section_by_name(elf, ".strtab"); 748627fce14SJosh Poimboeuf if (!shstrtab) { 7496d77d3b4SSimon Ser WARN("can't find .shstrtab or .strtab section"); 750627fce14SJosh Poimboeuf return NULL; 751627fce14SJosh Poimboeuf } 752627fce14SJosh Poimboeuf 753627fce14SJosh Poimboeuf s = elf_getscn(elf->elf, shstrtab->idx); 754627fce14SJosh Poimboeuf if (!s) { 755627fce14SJosh Poimboeuf WARN_ELF("elf_getscn"); 756627fce14SJosh Poimboeuf return NULL; 757627fce14SJosh Poimboeuf } 758627fce14SJosh Poimboeuf 759627fce14SJosh Poimboeuf data = elf_newdata(s); 760627fce14SJosh Poimboeuf if (!data) { 761627fce14SJosh Poimboeuf WARN_ELF("elf_newdata"); 762627fce14SJosh Poimboeuf return NULL; 763627fce14SJosh Poimboeuf } 764627fce14SJosh Poimboeuf 765627fce14SJosh Poimboeuf data->d_buf = sec->name; 766627fce14SJosh Poimboeuf data->d_size = strlen(name) + 1; 767627fce14SJosh Poimboeuf data->d_align = 1; 768627fce14SJosh Poimboeuf 769627fce14SJosh Poimboeuf sec->sh.sh_name = shstrtab->len; 770627fce14SJosh Poimboeuf 771627fce14SJosh Poimboeuf shstrtab->len += strlen(name) + 1; 772627fce14SJosh Poimboeuf shstrtab->changed = true; 773627fce14SJosh Poimboeuf 77453038996SPeter Zijlstra list_add_tail(&sec->list, &elf->sections); 77534f7c96dSPeter Zijlstra elf_hash_add(elf->section_hash, &sec->hash, sec->idx); 77634f7c96dSPeter Zijlstra elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name)); 77753038996SPeter Zijlstra 7782b10be23SPeter Zijlstra elf->changed = true; 7792b10be23SPeter Zijlstra 780627fce14SJosh Poimboeuf return sec; 781627fce14SJosh Poimboeuf } 782627fce14SJosh Poimboeuf 783fb414783SMatt Helsley static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base) 784fb414783SMatt Helsley { 785fb414783SMatt Helsley char *relocname; 786fb414783SMatt Helsley struct section *sec; 787fb414783SMatt Helsley 788fb414783SMatt Helsley relocname = malloc(strlen(base->name) + strlen(".rel") + 1); 789fb414783SMatt Helsley if (!relocname) { 790fb414783SMatt Helsley perror("malloc"); 791fb414783SMatt Helsley return NULL; 792fb414783SMatt Helsley } 793fb414783SMatt Helsley strcpy(relocname, ".rel"); 794fb414783SMatt Helsley strcat(relocname, base->name); 795fb414783SMatt Helsley 7961e7e4788SJosh Poimboeuf sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rel), 0); 797fb414783SMatt Helsley free(relocname); 798fb414783SMatt Helsley if (!sec) 799fb414783SMatt Helsley return NULL; 800fb414783SMatt Helsley 801fb414783SMatt Helsley base->reloc = sec; 802fb414783SMatt Helsley sec->base = base; 803fb414783SMatt Helsley 804fb414783SMatt Helsley sec->sh.sh_type = SHT_REL; 805fb414783SMatt Helsley sec->sh.sh_addralign = 8; 806fb414783SMatt Helsley sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; 807fb414783SMatt Helsley sec->sh.sh_info = base->idx; 808fb414783SMatt Helsley sec->sh.sh_flags = SHF_INFO_LINK; 809fb414783SMatt Helsley 810fb414783SMatt Helsley return sec; 811fb414783SMatt Helsley } 812fb414783SMatt Helsley 813fb414783SMatt Helsley static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base) 814627fce14SJosh Poimboeuf { 815f1974222SMatt Helsley char *relocname; 816627fce14SJosh Poimboeuf struct section *sec; 817627fce14SJosh Poimboeuf 818f1974222SMatt Helsley relocname = malloc(strlen(base->name) + strlen(".rela") + 1); 819f1974222SMatt Helsley if (!relocname) { 820627fce14SJosh Poimboeuf perror("malloc"); 821627fce14SJosh Poimboeuf return NULL; 822627fce14SJosh Poimboeuf } 823f1974222SMatt Helsley strcpy(relocname, ".rela"); 824f1974222SMatt Helsley strcat(relocname, base->name); 825627fce14SJosh Poimboeuf 8261e7e4788SJosh Poimboeuf sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rela), 0); 827f1974222SMatt Helsley free(relocname); 828627fce14SJosh Poimboeuf if (!sec) 829627fce14SJosh Poimboeuf return NULL; 830627fce14SJosh Poimboeuf 831f1974222SMatt Helsley base->reloc = sec; 832627fce14SJosh Poimboeuf sec->base = base; 833627fce14SJosh Poimboeuf 834627fce14SJosh Poimboeuf sec->sh.sh_type = SHT_RELA; 835627fce14SJosh Poimboeuf sec->sh.sh_addralign = 8; 836627fce14SJosh Poimboeuf sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; 837627fce14SJosh Poimboeuf sec->sh.sh_info = base->idx; 838627fce14SJosh Poimboeuf sec->sh.sh_flags = SHF_INFO_LINK; 839627fce14SJosh Poimboeuf 840627fce14SJosh Poimboeuf return sec; 841627fce14SJosh Poimboeuf } 842627fce14SJosh Poimboeuf 843fb414783SMatt Helsley struct section *elf_create_reloc_section(struct elf *elf, 844fb414783SMatt Helsley struct section *base, 845fb414783SMatt Helsley int reltype) 846fb414783SMatt Helsley { 847fb414783SMatt Helsley switch (reltype) { 848fb414783SMatt Helsley case SHT_REL: return elf_create_rel_reloc_section(elf, base); 849fb414783SMatt Helsley case SHT_RELA: return elf_create_rela_reloc_section(elf, base); 850fb414783SMatt Helsley default: return NULL; 851fb414783SMatt Helsley } 852fb414783SMatt Helsley } 853fb414783SMatt Helsley 854fb414783SMatt Helsley static int elf_rebuild_rel_reloc_section(struct section *sec, int nr) 855627fce14SJosh Poimboeuf { 856f1974222SMatt Helsley struct reloc *reloc; 857fb414783SMatt Helsley int idx = 0, size; 858*a1a664ecSMartin Schwidefsky void *buf; 859fb414783SMatt Helsley 860fb414783SMatt Helsley /* Allocate a buffer for relocations */ 861*a1a664ecSMartin Schwidefsky size = nr * sizeof(GElf_Rel); 862*a1a664ecSMartin Schwidefsky buf = malloc(size); 863*a1a664ecSMartin Schwidefsky if (!buf) { 864fb414783SMatt Helsley perror("malloc"); 865fb414783SMatt Helsley return -1; 866fb414783SMatt Helsley } 867fb414783SMatt Helsley 868*a1a664ecSMartin Schwidefsky sec->data->d_buf = buf; 869fb414783SMatt Helsley sec->data->d_size = size; 870*a1a664ecSMartin Schwidefsky sec->data->d_type = ELF_T_REL; 871fb414783SMatt Helsley 872fb414783SMatt Helsley sec->sh.sh_size = size; 873fb414783SMatt Helsley 874fb414783SMatt Helsley idx = 0; 875fb414783SMatt Helsley list_for_each_entry(reloc, &sec->reloc_list, list) { 876*a1a664ecSMartin Schwidefsky reloc->rel.r_offset = reloc->offset; 877*a1a664ecSMartin Schwidefsky reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 878*a1a664ecSMartin Schwidefsky gelf_update_rel(sec->data, idx, &reloc->rel); 879fb414783SMatt Helsley idx++; 880fb414783SMatt Helsley } 881fb414783SMatt Helsley 882fb414783SMatt Helsley return 0; 883fb414783SMatt Helsley } 884fb414783SMatt Helsley 885fb414783SMatt Helsley static int elf_rebuild_rela_reloc_section(struct section *sec, int nr) 886fb414783SMatt Helsley { 887fb414783SMatt Helsley struct reloc *reloc; 888fb414783SMatt Helsley int idx = 0, size; 889*a1a664ecSMartin Schwidefsky void *buf; 890627fce14SJosh Poimboeuf 891fb414783SMatt Helsley /* Allocate a buffer for relocations with addends */ 892*a1a664ecSMartin Schwidefsky size = nr * sizeof(GElf_Rela); 893*a1a664ecSMartin Schwidefsky buf = malloc(size); 894*a1a664ecSMartin Schwidefsky if (!buf) { 895627fce14SJosh Poimboeuf perror("malloc"); 896627fce14SJosh Poimboeuf return -1; 897627fce14SJosh Poimboeuf } 898627fce14SJosh Poimboeuf 899*a1a664ecSMartin Schwidefsky sec->data->d_buf = buf; 900627fce14SJosh Poimboeuf sec->data->d_size = size; 901*a1a664ecSMartin Schwidefsky sec->data->d_type = ELF_T_RELA; 902627fce14SJosh Poimboeuf 903627fce14SJosh Poimboeuf sec->sh.sh_size = size; 904627fce14SJosh Poimboeuf 905627fce14SJosh Poimboeuf idx = 0; 906f1974222SMatt Helsley list_for_each_entry(reloc, &sec->reloc_list, list) { 907*a1a664ecSMartin Schwidefsky reloc->rela.r_offset = reloc->offset; 908*a1a664ecSMartin Schwidefsky reloc->rela.r_addend = reloc->addend; 909*a1a664ecSMartin Schwidefsky reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 910*a1a664ecSMartin Schwidefsky gelf_update_rela(sec->data, idx, &reloc->rela); 911627fce14SJosh Poimboeuf idx++; 912627fce14SJosh Poimboeuf } 913627fce14SJosh Poimboeuf 914627fce14SJosh Poimboeuf return 0; 915627fce14SJosh Poimboeuf } 916627fce14SJosh Poimboeuf 917d832c005SPeter Zijlstra int elf_rebuild_reloc_section(struct elf *elf, struct section *sec) 918fb414783SMatt Helsley { 919fb414783SMatt Helsley struct reloc *reloc; 920fb414783SMatt Helsley int nr; 921fb414783SMatt Helsley 922d832c005SPeter Zijlstra sec->changed = true; 923d832c005SPeter Zijlstra elf->changed = true; 924d832c005SPeter Zijlstra 925fb414783SMatt Helsley nr = 0; 926fb414783SMatt Helsley list_for_each_entry(reloc, &sec->reloc_list, list) 927fb414783SMatt Helsley nr++; 928fb414783SMatt Helsley 929fb414783SMatt Helsley switch (sec->sh.sh_type) { 930fb414783SMatt Helsley case SHT_REL: return elf_rebuild_rel_reloc_section(sec, nr); 931fb414783SMatt Helsley case SHT_RELA: return elf_rebuild_rela_reloc_section(sec, nr); 932fb414783SMatt Helsley default: return -1; 933fb414783SMatt Helsley } 934fb414783SMatt Helsley } 935fb414783SMatt Helsley 936fdabdd0bSPeter Zijlstra int elf_write_insn(struct elf *elf, struct section *sec, 937fdabdd0bSPeter Zijlstra unsigned long offset, unsigned int len, 938fdabdd0bSPeter Zijlstra const char *insn) 939fdabdd0bSPeter Zijlstra { 940fdabdd0bSPeter Zijlstra Elf_Data *data = sec->data; 941fdabdd0bSPeter Zijlstra 942fdabdd0bSPeter Zijlstra if (data->d_type != ELF_T_BYTE || data->d_off) { 943fdabdd0bSPeter Zijlstra WARN("write to unexpected data for section: %s", sec->name); 944fdabdd0bSPeter Zijlstra return -1; 945fdabdd0bSPeter Zijlstra } 946fdabdd0bSPeter Zijlstra 947fdabdd0bSPeter Zijlstra memcpy(data->d_buf + offset, insn, len); 948fdabdd0bSPeter Zijlstra elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY); 949fdabdd0bSPeter Zijlstra 950fdabdd0bSPeter Zijlstra elf->changed = true; 951fdabdd0bSPeter Zijlstra 952fdabdd0bSPeter Zijlstra return 0; 953fdabdd0bSPeter Zijlstra } 954fdabdd0bSPeter Zijlstra 955d832c005SPeter Zijlstra int elf_write_reloc(struct elf *elf, struct reloc *reloc) 956fdabdd0bSPeter Zijlstra { 957d832c005SPeter Zijlstra struct section *sec = reloc->sec; 958fdabdd0bSPeter Zijlstra 959d832c005SPeter Zijlstra if (sec->sh.sh_type == SHT_REL) { 960d832c005SPeter Zijlstra reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 961d832c005SPeter Zijlstra reloc->rel.r_offset = reloc->offset; 962fdabdd0bSPeter Zijlstra 963d832c005SPeter Zijlstra if (!gelf_update_rel(sec->data, reloc->idx, &reloc->rel)) { 964d832c005SPeter Zijlstra WARN_ELF("gelf_update_rel"); 965d832c005SPeter Zijlstra return -1; 966d832c005SPeter Zijlstra } 967d832c005SPeter Zijlstra } else { 968d832c005SPeter Zijlstra reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 969d832c005SPeter Zijlstra reloc->rela.r_addend = reloc->addend; 970d832c005SPeter Zijlstra reloc->rela.r_offset = reloc->offset; 971d832c005SPeter Zijlstra 972d832c005SPeter Zijlstra if (!gelf_update_rela(sec->data, reloc->idx, &reloc->rela)) { 973fdabdd0bSPeter Zijlstra WARN_ELF("gelf_update_rela"); 974fdabdd0bSPeter Zijlstra return -1; 975fdabdd0bSPeter Zijlstra } 976d832c005SPeter Zijlstra } 977fdabdd0bSPeter Zijlstra 978fdabdd0bSPeter Zijlstra elf->changed = true; 979fdabdd0bSPeter Zijlstra 980fdabdd0bSPeter Zijlstra return 0; 981fdabdd0bSPeter Zijlstra } 982fdabdd0bSPeter Zijlstra 9832b10be23SPeter Zijlstra int elf_write(struct elf *elf) 984627fce14SJosh Poimboeuf { 985627fce14SJosh Poimboeuf struct section *sec; 986627fce14SJosh Poimboeuf Elf_Scn *s; 987627fce14SJosh Poimboeuf 98897dab2aeSJosh Poimboeuf /* Update section headers for changed sections: */ 989627fce14SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 990627fce14SJosh Poimboeuf if (sec->changed) { 991627fce14SJosh Poimboeuf s = elf_getscn(elf->elf, sec->idx); 992627fce14SJosh Poimboeuf if (!s) { 993627fce14SJosh Poimboeuf WARN_ELF("elf_getscn"); 994627fce14SJosh Poimboeuf return -1; 995627fce14SJosh Poimboeuf } 996627fce14SJosh Poimboeuf if (!gelf_update_shdr(s, &sec->sh)) { 997627fce14SJosh Poimboeuf WARN_ELF("gelf_update_shdr"); 998627fce14SJosh Poimboeuf return -1; 999627fce14SJosh Poimboeuf } 10002b10be23SPeter Zijlstra 10012b10be23SPeter Zijlstra sec->changed = false; 1002627fce14SJosh Poimboeuf } 1003627fce14SJosh Poimboeuf } 1004627fce14SJosh Poimboeuf 100597dab2aeSJosh Poimboeuf /* Make sure the new section header entries get updated properly. */ 100697dab2aeSJosh Poimboeuf elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY); 100797dab2aeSJosh Poimboeuf 100897dab2aeSJosh Poimboeuf /* Write all changes to the file. */ 1009627fce14SJosh Poimboeuf if (elf_update(elf->elf, ELF_C_WRITE) < 0) { 1010627fce14SJosh Poimboeuf WARN_ELF("elf_update"); 1011627fce14SJosh Poimboeuf return -1; 1012627fce14SJosh Poimboeuf } 1013627fce14SJosh Poimboeuf 10142b10be23SPeter Zijlstra elf->changed = false; 10152b10be23SPeter Zijlstra 1016627fce14SJosh Poimboeuf return 0; 1017627fce14SJosh Poimboeuf } 1018627fce14SJosh Poimboeuf 1019442f04c3SJosh Poimboeuf void elf_close(struct elf *elf) 1020442f04c3SJosh Poimboeuf { 1021442f04c3SJosh Poimboeuf struct section *sec, *tmpsec; 1022442f04c3SJosh Poimboeuf struct symbol *sym, *tmpsym; 1023f1974222SMatt Helsley struct reloc *reloc, *tmpreloc; 1024442f04c3SJosh Poimboeuf 1025baa41469SJosh Poimboeuf if (elf->elf) 1026baa41469SJosh Poimboeuf elf_end(elf->elf); 1027baa41469SJosh Poimboeuf 1028baa41469SJosh Poimboeuf if (elf->fd > 0) 1029baa41469SJosh Poimboeuf close(elf->fd); 1030baa41469SJosh Poimboeuf 1031442f04c3SJosh Poimboeuf list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { 1032a196e171SJosh Poimboeuf list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { 1033442f04c3SJosh Poimboeuf list_del(&sym->list); 1034042ba73fSJosh Poimboeuf hash_del(&sym->hash); 1035442f04c3SJosh Poimboeuf free(sym); 1036442f04c3SJosh Poimboeuf } 1037f1974222SMatt Helsley list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) { 1038f1974222SMatt Helsley list_del(&reloc->list); 1039f1974222SMatt Helsley hash_del(&reloc->hash); 1040f1974222SMatt Helsley free(reloc); 1041442f04c3SJosh Poimboeuf } 1042442f04c3SJosh Poimboeuf list_del(&sec->list); 1043442f04c3SJosh Poimboeuf free(sec); 1044442f04c3SJosh Poimboeuf } 1045baa41469SJosh Poimboeuf 1046442f04c3SJosh Poimboeuf free(elf); 1047442f04c3SJosh Poimboeuf } 1048