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> 1225cf0d8aSPeter Zijlstra #include <sys/mman.h> 13442f04c3SJosh Poimboeuf #include <fcntl.h> 14442f04c3SJosh Poimboeuf #include <stdio.h> 15442f04c3SJosh Poimboeuf #include <stdlib.h> 16442f04c3SJosh Poimboeuf #include <string.h> 17442f04c3SJosh Poimboeuf #include <unistd.h> 18385d11b1SJosh Poimboeuf #include <errno.h> 195da6aea3SPeter Zijlstra #include <linux/interval_tree_generic.h> 207786032eSVasily Gorbik #include <objtool/builtin.h> 21442f04c3SJosh Poimboeuf 227786032eSVasily Gorbik #include <objtool/elf.h> 237786032eSVasily Gorbik #include <objtool/warn.h> 24442f04c3SJosh Poimboeuf 2522566c16SArtem Savkov #define MAX_NAME_LEN 128 2622566c16SArtem Savkov 27ae358196SPeter Zijlstra static inline u32 str_hash(const char *str) 28ae358196SPeter Zijlstra { 29ae358196SPeter Zijlstra return jhash(str, strlen(str), 0); 30ae358196SPeter Zijlstra } 31ae358196SPeter Zijlstra 3225cf0d8aSPeter Zijlstra #define __elf_table(name) (elf->name##_hash) 3325cf0d8aSPeter Zijlstra #define __elf_bits(name) (elf->name##_bits) 3434f7c96dSPeter Zijlstra 3525cf0d8aSPeter Zijlstra #define elf_hash_add(name, node, key) \ 3625cf0d8aSPeter Zijlstra hlist_add_head(node, &__elf_table(name)[hash_min(key, __elf_bits(name))]) 3734f7c96dSPeter Zijlstra 3834f7c96dSPeter Zijlstra #define elf_hash_for_each_possible(name, obj, member, key) \ 3925cf0d8aSPeter Zijlstra hlist_for_each_entry(obj, &__elf_table(name)[hash_min(key, __elf_bits(name))], member) 4025cf0d8aSPeter Zijlstra 4125cf0d8aSPeter Zijlstra #define elf_alloc_hash(name, size) \ 4225cf0d8aSPeter Zijlstra ({ \ 4325cf0d8aSPeter Zijlstra __elf_bits(name) = max(10, ilog2(size)); \ 4425cf0d8aSPeter Zijlstra __elf_table(name) = mmap(NULL, sizeof(struct hlist_head) << __elf_bits(name), \ 4525cf0d8aSPeter Zijlstra PROT_READ|PROT_WRITE, \ 4625cf0d8aSPeter Zijlstra MAP_PRIVATE|MAP_ANON, -1, 0); \ 4725cf0d8aSPeter Zijlstra if (__elf_table(name) == (void *)-1L) { \ 4825cf0d8aSPeter Zijlstra WARN("mmap fail " #name); \ 4925cf0d8aSPeter Zijlstra __elf_table(name) = NULL; \ 5025cf0d8aSPeter Zijlstra } \ 5125cf0d8aSPeter Zijlstra __elf_table(name); \ 5225cf0d8aSPeter Zijlstra }) 5334f7c96dSPeter Zijlstra 545da6aea3SPeter Zijlstra static inline unsigned long __sym_start(struct symbol *s) 552a362eccSPeter Zijlstra { 565da6aea3SPeter Zijlstra return s->offset; 572a362eccSPeter Zijlstra } 582a362eccSPeter Zijlstra 595da6aea3SPeter Zijlstra static inline unsigned long __sym_last(struct symbol *s) 602a362eccSPeter Zijlstra { 615da6aea3SPeter Zijlstra return s->offset + s->len - 1; 622a362eccSPeter Zijlstra } 632a362eccSPeter Zijlstra 645da6aea3SPeter Zijlstra INTERVAL_TREE_DEFINE(struct symbol, node, unsigned long, __subtree_last, 655da6aea3SPeter Zijlstra __sym_start, __sym_last, static, __sym) 665da6aea3SPeter Zijlstra 675da6aea3SPeter Zijlstra #define __sym_for_each(_iter, _tree, _start, _end) \ 685da6aea3SPeter Zijlstra for (_iter = __sym_iter_first((_tree), (_start), (_end)); \ 695da6aea3SPeter Zijlstra _iter; _iter = __sym_iter_next(_iter, (_start), (_end))) 705da6aea3SPeter Zijlstra 714adb2368SPeter Zijlstra struct symbol_hole { 724adb2368SPeter Zijlstra unsigned long key; 734adb2368SPeter Zijlstra const struct symbol *sym; 744adb2368SPeter Zijlstra }; 754adb2368SPeter Zijlstra 764adb2368SPeter Zijlstra /* 774adb2368SPeter Zijlstra * Find !section symbol where @offset is after it. 784adb2368SPeter Zijlstra */ 794adb2368SPeter Zijlstra static int symbol_hole_by_offset(const void *key, const struct rb_node *node) 804adb2368SPeter Zijlstra { 814adb2368SPeter Zijlstra const struct symbol *s = rb_entry(node, struct symbol, node); 824adb2368SPeter Zijlstra struct symbol_hole *sh = (void *)key; 834adb2368SPeter Zijlstra 844adb2368SPeter Zijlstra if (sh->key < s->offset) 854adb2368SPeter Zijlstra return -1; 864adb2368SPeter Zijlstra 874adb2368SPeter Zijlstra if (sh->key >= s->offset + s->len) { 884adb2368SPeter Zijlstra if (s->type != STT_SECTION) 894adb2368SPeter Zijlstra sh->sym = s; 904adb2368SPeter Zijlstra return 1; 914adb2368SPeter Zijlstra } 924adb2368SPeter Zijlstra 934adb2368SPeter Zijlstra return 0; 944adb2368SPeter Zijlstra } 954adb2368SPeter Zijlstra 96894e48caSIngo Molnar struct section *find_section_by_name(const struct elf *elf, const char *name) 97442f04c3SJosh Poimboeuf { 98442f04c3SJosh Poimboeuf struct section *sec; 99442f04c3SJosh Poimboeuf 10025cf0d8aSPeter Zijlstra elf_hash_for_each_possible(section_name, sec, name_hash, str_hash(name)) { 101442f04c3SJosh Poimboeuf if (!strcmp(sec->name, name)) 102442f04c3SJosh Poimboeuf return sec; 10325cf0d8aSPeter Zijlstra } 104442f04c3SJosh Poimboeuf 105442f04c3SJosh Poimboeuf return NULL; 106442f04c3SJosh Poimboeuf } 107442f04c3SJosh Poimboeuf 108442f04c3SJosh Poimboeuf static struct section *find_section_by_index(struct elf *elf, 109442f04c3SJosh Poimboeuf unsigned int idx) 110442f04c3SJosh Poimboeuf { 111442f04c3SJosh Poimboeuf struct section *sec; 112442f04c3SJosh Poimboeuf 11325cf0d8aSPeter Zijlstra elf_hash_for_each_possible(section, sec, hash, idx) { 114442f04c3SJosh Poimboeuf if (sec->idx == idx) 115442f04c3SJosh Poimboeuf return sec; 11625cf0d8aSPeter Zijlstra } 117442f04c3SJosh Poimboeuf 118442f04c3SJosh Poimboeuf return NULL; 119442f04c3SJosh Poimboeuf } 120442f04c3SJosh Poimboeuf 121442f04c3SJosh Poimboeuf static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) 122442f04c3SJosh Poimboeuf { 123442f04c3SJosh Poimboeuf struct symbol *sym; 124442f04c3SJosh Poimboeuf 12525cf0d8aSPeter Zijlstra elf_hash_for_each_possible(symbol, sym, hash, idx) { 126442f04c3SJosh Poimboeuf if (sym->idx == idx) 127442f04c3SJosh Poimboeuf return sym; 12825cf0d8aSPeter Zijlstra } 129442f04c3SJosh Poimboeuf 130442f04c3SJosh Poimboeuf return NULL; 131442f04c3SJosh Poimboeuf } 132442f04c3SJosh Poimboeuf 133442f04c3SJosh Poimboeuf struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) 134442f04c3SJosh Poimboeuf { 1355da6aea3SPeter Zijlstra struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; 1365da6aea3SPeter Zijlstra struct symbol *iter; 137442f04c3SJosh Poimboeuf 1385da6aea3SPeter Zijlstra __sym_for_each(iter, tree, offset, offset) { 1395da6aea3SPeter Zijlstra if (iter->offset == offset && iter->type != STT_SECTION) 1405da6aea3SPeter Zijlstra return iter; 1412a362eccSPeter Zijlstra } 1427acfe531SJosh Poimboeuf 1437acfe531SJosh Poimboeuf return NULL; 1447acfe531SJosh Poimboeuf } 1457acfe531SJosh Poimboeuf 1467acfe531SJosh Poimboeuf struct symbol *find_func_by_offset(struct section *sec, unsigned long offset) 1477acfe531SJosh Poimboeuf { 1485da6aea3SPeter Zijlstra struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; 1495da6aea3SPeter Zijlstra struct symbol *iter; 1507acfe531SJosh Poimboeuf 1515da6aea3SPeter Zijlstra __sym_for_each(iter, tree, offset, offset) { 1525da6aea3SPeter Zijlstra if (iter->offset == offset && iter->type == STT_FUNC) 1535da6aea3SPeter Zijlstra return iter; 1542a362eccSPeter Zijlstra } 1552a362eccSPeter Zijlstra 1562a362eccSPeter Zijlstra return NULL; 1572a362eccSPeter Zijlstra } 1582a362eccSPeter Zijlstra 159b490f453SMiroslav Benes struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset) 1602a362eccSPeter Zijlstra { 1615da6aea3SPeter Zijlstra struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; 1625da6aea3SPeter Zijlstra struct symbol *iter; 1632a362eccSPeter Zijlstra 1645da6aea3SPeter Zijlstra __sym_for_each(iter, tree, offset, offset) { 1655da6aea3SPeter Zijlstra if (iter->type != STT_SECTION) 1665da6aea3SPeter Zijlstra return iter; 1672a362eccSPeter Zijlstra } 1682a362eccSPeter Zijlstra 1692a362eccSPeter Zijlstra return NULL; 1702a362eccSPeter Zijlstra } 1712a362eccSPeter Zijlstra 1724adb2368SPeter Zijlstra /* 1734adb2368SPeter Zijlstra * Returns size of hole starting at @offset. 1744adb2368SPeter Zijlstra */ 1754adb2368SPeter Zijlstra int find_symbol_hole_containing(const struct section *sec, unsigned long offset) 1764adb2368SPeter Zijlstra { 1774adb2368SPeter Zijlstra struct symbol_hole hole = { 1784adb2368SPeter Zijlstra .key = offset, 1794adb2368SPeter Zijlstra .sym = NULL, 1804adb2368SPeter Zijlstra }; 1814adb2368SPeter Zijlstra struct rb_node *n; 1824adb2368SPeter Zijlstra struct symbol *s; 1834adb2368SPeter Zijlstra 1844adb2368SPeter Zijlstra /* 1854adb2368SPeter Zijlstra * Find the rightmost symbol for which @offset is after it. 1864adb2368SPeter Zijlstra */ 1875da6aea3SPeter Zijlstra n = rb_find(&hole, &sec->symbol_tree.rb_root, symbol_hole_by_offset); 1884adb2368SPeter Zijlstra 1894adb2368SPeter Zijlstra /* found a symbol that contains @offset */ 1904adb2368SPeter Zijlstra if (n) 1914adb2368SPeter Zijlstra return 0; /* not a hole */ 1924adb2368SPeter Zijlstra 1934adb2368SPeter Zijlstra /* didn't find a symbol for which @offset is after it */ 1944adb2368SPeter Zijlstra if (!hole.sym) 1954adb2368SPeter Zijlstra return 0; /* not a hole */ 1964adb2368SPeter Zijlstra 1974adb2368SPeter Zijlstra /* @offset >= sym->offset + sym->len, find symbol after it */ 1984adb2368SPeter Zijlstra n = rb_next(&hole.sym->node); 1994adb2368SPeter Zijlstra if (!n) 2004adb2368SPeter Zijlstra return -1; /* until end of address space */ 2014adb2368SPeter Zijlstra 2024adb2368SPeter Zijlstra /* hole until start of next symbol */ 2034adb2368SPeter Zijlstra s = rb_entry(n, struct symbol, node); 2044adb2368SPeter Zijlstra return s->offset - offset; 2054adb2368SPeter Zijlstra } 2064adb2368SPeter Zijlstra 20753d20720SPeter Zijlstra struct symbol *find_func_containing(struct section *sec, unsigned long offset) 2082a362eccSPeter Zijlstra { 2095da6aea3SPeter Zijlstra struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; 2105da6aea3SPeter Zijlstra struct symbol *iter; 2112a362eccSPeter Zijlstra 2125da6aea3SPeter Zijlstra __sym_for_each(iter, tree, offset, offset) { 2135da6aea3SPeter Zijlstra if (iter->type == STT_FUNC) 2145da6aea3SPeter Zijlstra return iter; 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 22425cf0d8aSPeter Zijlstra elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) { 22513810435SJosh Poimboeuf if (!strcmp(sym->name, name)) 22613810435SJosh Poimboeuf return sym; 22725cf0d8aSPeter Zijlstra } 22813810435SJosh Poimboeuf 22913810435SJosh Poimboeuf return NULL; 23013810435SJosh Poimboeuf } 23113810435SJosh Poimboeuf 232f1974222SMatt Helsley struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, 2338b5fa6bcSPeter Zijlstra unsigned long offset, unsigned int len) 234442f04c3SJosh Poimboeuf { 235f1974222SMatt Helsley struct reloc *reloc, *r = NULL; 236042ba73fSJosh Poimboeuf unsigned long o; 237442f04c3SJosh Poimboeuf 238f1974222SMatt Helsley if (!sec->reloc) 239442f04c3SJosh Poimboeuf return NULL; 240442f04c3SJosh Poimboeuf 241f1974222SMatt Helsley sec = sec->reloc; 2428b5fa6bcSPeter Zijlstra 24374b873e4SPeter Zijlstra for_offset_range(o, offset, offset + len) { 24425cf0d8aSPeter Zijlstra elf_hash_for_each_possible(reloc, reloc, hash, 2458b5fa6bcSPeter Zijlstra sec_offset_hash(sec, o)) { 246f1974222SMatt Helsley if (reloc->sec != sec) 24774b873e4SPeter Zijlstra continue; 24874b873e4SPeter Zijlstra 249f1974222SMatt Helsley if (reloc->offset >= offset && reloc->offset < offset + len) { 250f1974222SMatt Helsley if (!r || reloc->offset < r->offset) 251f1974222SMatt Helsley r = reloc; 2528b5fa6bcSPeter Zijlstra } 2538b5fa6bcSPeter Zijlstra } 25474b873e4SPeter Zijlstra if (r) 25574b873e4SPeter Zijlstra return r; 25674b873e4SPeter Zijlstra } 257442f04c3SJosh Poimboeuf 258442f04c3SJosh Poimboeuf return NULL; 259442f04c3SJosh Poimboeuf } 260442f04c3SJosh Poimboeuf 261f1974222SMatt Helsley struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset) 262442f04c3SJosh Poimboeuf { 263f1974222SMatt Helsley return find_reloc_by_dest_range(elf, sec, offset, 1); 264442f04c3SJosh Poimboeuf } 265442f04c3SJosh Poimboeuf 266442f04c3SJosh Poimboeuf static int read_sections(struct elf *elf) 267442f04c3SJosh Poimboeuf { 268442f04c3SJosh Poimboeuf Elf_Scn *s = NULL; 269442f04c3SJosh Poimboeuf struct section *sec; 270442f04c3SJosh Poimboeuf size_t shstrndx, sections_nr; 271442f04c3SJosh Poimboeuf int i; 272442f04c3SJosh Poimboeuf 273442f04c3SJosh Poimboeuf if (elf_getshdrnum(elf->elf, §ions_nr)) { 274baa41469SJosh Poimboeuf WARN_ELF("elf_getshdrnum"); 275442f04c3SJosh Poimboeuf return -1; 276442f04c3SJosh Poimboeuf } 277442f04c3SJosh Poimboeuf 278442f04c3SJosh Poimboeuf if (elf_getshdrstrndx(elf->elf, &shstrndx)) { 279baa41469SJosh Poimboeuf WARN_ELF("elf_getshdrstrndx"); 280442f04c3SJosh Poimboeuf return -1; 281442f04c3SJosh Poimboeuf } 282442f04c3SJosh Poimboeuf 28325cf0d8aSPeter Zijlstra if (!elf_alloc_hash(section, sections_nr) || 28425cf0d8aSPeter Zijlstra !elf_alloc_hash(section_name, sections_nr)) 28525cf0d8aSPeter Zijlstra return -1; 28625cf0d8aSPeter Zijlstra 2878045b8f0SThomas Weißschuh elf->section_data = calloc(sections_nr, sizeof(*sec)); 2888045b8f0SThomas Weißschuh if (!elf->section_data) { 2898045b8f0SThomas Weißschuh perror("calloc"); 290442f04c3SJosh Poimboeuf return -1; 291442f04c3SJosh Poimboeuf } 2928045b8f0SThomas Weißschuh for (i = 0; i < sections_nr; i++) { 2938045b8f0SThomas Weißschuh sec = &elf->section_data[i]; 294442f04c3SJosh Poimboeuf 295a196e171SJosh Poimboeuf INIT_LIST_HEAD(&sec->symbol_list); 296f1974222SMatt Helsley INIT_LIST_HEAD(&sec->reloc_list); 297442f04c3SJosh Poimboeuf 298442f04c3SJosh Poimboeuf s = elf_getscn(elf->elf, i); 299442f04c3SJosh Poimboeuf if (!s) { 300baa41469SJosh Poimboeuf WARN_ELF("elf_getscn"); 301442f04c3SJosh Poimboeuf return -1; 302442f04c3SJosh Poimboeuf } 303442f04c3SJosh Poimboeuf 304442f04c3SJosh Poimboeuf sec->idx = elf_ndxscn(s); 305442f04c3SJosh Poimboeuf 306442f04c3SJosh Poimboeuf if (!gelf_getshdr(s, &sec->sh)) { 307baa41469SJosh Poimboeuf WARN_ELF("gelf_getshdr"); 308442f04c3SJosh Poimboeuf return -1; 309442f04c3SJosh Poimboeuf } 310442f04c3SJosh Poimboeuf 311442f04c3SJosh Poimboeuf sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name); 312442f04c3SJosh Poimboeuf if (!sec->name) { 313baa41469SJosh Poimboeuf WARN_ELF("elf_strptr"); 314442f04c3SJosh Poimboeuf return -1; 315442f04c3SJosh Poimboeuf } 316442f04c3SJosh Poimboeuf 317df968c93SPetr Vandrovec if (sec->sh.sh_size != 0) { 318baa41469SJosh Poimboeuf sec->data = elf_getdata(s, NULL); 319baa41469SJosh Poimboeuf if (!sec->data) { 320baa41469SJosh Poimboeuf WARN_ELF("elf_getdata"); 321442f04c3SJosh Poimboeuf return -1; 322442f04c3SJosh Poimboeuf } 323baa41469SJosh Poimboeuf if (sec->data->d_off != 0 || 324baa41469SJosh Poimboeuf sec->data->d_size != sec->sh.sh_size) { 325df968c93SPetr Vandrovec WARN("unexpected data attributes for %s", 326df968c93SPetr Vandrovec sec->name); 327442f04c3SJosh Poimboeuf return -1; 328442f04c3SJosh Poimboeuf } 329df968c93SPetr Vandrovec } 33053038996SPeter Zijlstra 331d33b9035SPeter Zijlstra if (sec->sh.sh_flags & SHF_EXECINSTR) 332fe255fe6SJoe Lawrence elf->text_size += sec->sh.sh_size; 333d33b9035SPeter Zijlstra 33453038996SPeter Zijlstra list_add_tail(&sec->list, &elf->sections); 33525cf0d8aSPeter Zijlstra elf_hash_add(section, &sec->hash, sec->idx); 33625cf0d8aSPeter Zijlstra elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); 337442f04c3SJosh Poimboeuf } 338442f04c3SJosh Poimboeuf 3392daf7fabSJosh Poimboeuf if (opts.stats) { 3401e11f3fdSPeter Zijlstra printf("nr_sections: %lu\n", (unsigned long)sections_nr); 34125cf0d8aSPeter Zijlstra printf("section_bits: %d\n", elf->section_bits); 34225cf0d8aSPeter Zijlstra } 3431e11f3fdSPeter Zijlstra 344442f04c3SJosh Poimboeuf /* sanity check, one more call to elf_nextscn() should return NULL */ 345442f04c3SJosh Poimboeuf if (elf_nextscn(elf->elf, s)) { 346442f04c3SJosh Poimboeuf WARN("section entry mismatch"); 347442f04c3SJosh Poimboeuf return -1; 348442f04c3SJosh Poimboeuf } 349442f04c3SJosh Poimboeuf 350442f04c3SJosh Poimboeuf return 0; 351442f04c3SJosh Poimboeuf } 352442f04c3SJosh Poimboeuf 3539a7827b7SPeter Zijlstra static void elf_add_symbol(struct elf *elf, struct symbol *sym) 3549a7827b7SPeter Zijlstra { 3559a7827b7SPeter Zijlstra struct list_head *entry; 3569a7827b7SPeter Zijlstra struct rb_node *pnode; 3575da6aea3SPeter Zijlstra struct symbol *iter; 3589a7827b7SPeter Zijlstra 35919526717SPeter Zijlstra INIT_LIST_HEAD(&sym->reloc_list); 360ead165faSPeter Zijlstra INIT_LIST_HEAD(&sym->pv_target); 361ead165faSPeter Zijlstra sym->alias = sym; 362ead165faSPeter Zijlstra 3639a7827b7SPeter Zijlstra sym->type = GELF_ST_TYPE(sym->sym.st_info); 3649a7827b7SPeter Zijlstra sym->bind = GELF_ST_BIND(sym->sym.st_info); 3659a7827b7SPeter Zijlstra 366753da417SJosh Poimboeuf if (sym->type == STT_FILE) 367753da417SJosh Poimboeuf elf->num_files++; 368753da417SJosh Poimboeuf 3699a7827b7SPeter Zijlstra sym->offset = sym->sym.st_value; 3709a7827b7SPeter Zijlstra sym->len = sym->sym.st_size; 3719a7827b7SPeter Zijlstra 3725da6aea3SPeter Zijlstra __sym_for_each(iter, &sym->sec->symbol_tree, sym->offset, sym->offset) { 3735da6aea3SPeter Zijlstra if (iter->offset == sym->offset && iter->type == sym->type) 3745da6aea3SPeter Zijlstra iter->alias = sym; 3755da6aea3SPeter Zijlstra } 3765da6aea3SPeter Zijlstra 3775da6aea3SPeter Zijlstra __sym_insert(sym, &sym->sec->symbol_tree); 3789a7827b7SPeter Zijlstra pnode = rb_prev(&sym->node); 3799a7827b7SPeter Zijlstra if (pnode) 3809a7827b7SPeter Zijlstra entry = &rb_entry(pnode, struct symbol, node)->list; 3819a7827b7SPeter Zijlstra else 3829a7827b7SPeter Zijlstra entry = &sym->sec->symbol_list; 3839a7827b7SPeter Zijlstra list_add(&sym->list, entry); 38425cf0d8aSPeter Zijlstra elf_hash_add(symbol, &sym->hash, sym->idx); 38525cf0d8aSPeter Zijlstra elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name)); 3869a7827b7SPeter Zijlstra 3879a7827b7SPeter Zijlstra /* 3889a7827b7SPeter Zijlstra * Don't store empty STT_NOTYPE symbols in the rbtree. They 3899a7827b7SPeter Zijlstra * can exist within a function, confusing the sorting. 3909a7827b7SPeter Zijlstra */ 3919a7827b7SPeter Zijlstra if (!sym->len) 3925da6aea3SPeter Zijlstra __sym_remove(sym, &sym->sec->symbol_tree); 3939a7827b7SPeter Zijlstra } 3949a7827b7SPeter Zijlstra 395442f04c3SJosh Poimboeuf static int read_symbols(struct elf *elf) 396442f04c3SJosh Poimboeuf { 39728fe1d7bSSami Tolvanen struct section *symtab, *symtab_shndx, *sec; 3982a362eccSPeter Zijlstra struct symbol *sym, *pfunc; 399442f04c3SJosh Poimboeuf int symbols_nr, i; 40013810435SJosh Poimboeuf char *coldstr; 40128fe1d7bSSami Tolvanen Elf_Data *shndx_data = NULL; 40228fe1d7bSSami Tolvanen Elf32_Word shndx; 403442f04c3SJosh Poimboeuf 404442f04c3SJosh Poimboeuf symtab = find_section_by_name(elf, ".symtab"); 40525cf0d8aSPeter Zijlstra if (symtab) { 40628fe1d7bSSami Tolvanen symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); 40728fe1d7bSSami Tolvanen if (symtab_shndx) 40828fe1d7bSSami Tolvanen shndx_data = symtab_shndx->data; 40928fe1d7bSSami Tolvanen 410442f04c3SJosh Poimboeuf symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize; 41125cf0d8aSPeter Zijlstra } else { 41225cf0d8aSPeter Zijlstra /* 41325cf0d8aSPeter Zijlstra * A missing symbol table is actually possible if it's an empty 41425cf0d8aSPeter Zijlstra * .o file. This can happen for thunk_64.o. Make sure to at 41525cf0d8aSPeter Zijlstra * least allocate the symbol hash tables so we can do symbol 41625cf0d8aSPeter Zijlstra * lookups without crashing. 41725cf0d8aSPeter Zijlstra */ 41825cf0d8aSPeter Zijlstra symbols_nr = 0; 41925cf0d8aSPeter Zijlstra } 42025cf0d8aSPeter Zijlstra 42125cf0d8aSPeter Zijlstra if (!elf_alloc_hash(symbol, symbols_nr) || 42225cf0d8aSPeter Zijlstra !elf_alloc_hash(symbol_name, symbols_nr)) 42325cf0d8aSPeter Zijlstra return -1; 424442f04c3SJosh Poimboeuf 4258045b8f0SThomas Weißschuh elf->symbol_data = calloc(symbols_nr, sizeof(*sym)); 4268045b8f0SThomas Weißschuh if (!elf->symbol_data) { 4278045b8f0SThomas Weißschuh perror("calloc"); 428442f04c3SJosh Poimboeuf return -1; 429442f04c3SJosh Poimboeuf } 4308045b8f0SThomas Weißschuh for (i = 0; i < symbols_nr; i++) { 4318045b8f0SThomas Weißschuh sym = &elf->symbol_data[i]; 432442f04c3SJosh Poimboeuf 433442f04c3SJosh Poimboeuf sym->idx = i; 434442f04c3SJosh Poimboeuf 43528fe1d7bSSami Tolvanen if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym, 43628fe1d7bSSami Tolvanen &shndx)) { 43728fe1d7bSSami Tolvanen WARN_ELF("gelf_getsymshndx"); 438442f04c3SJosh Poimboeuf goto err; 439442f04c3SJosh Poimboeuf } 440442f04c3SJosh Poimboeuf 441442f04c3SJosh Poimboeuf sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, 442442f04c3SJosh Poimboeuf sym->sym.st_name); 443442f04c3SJosh Poimboeuf if (!sym->name) { 444baa41469SJosh Poimboeuf WARN_ELF("elf_strptr"); 445442f04c3SJosh Poimboeuf goto err; 446442f04c3SJosh Poimboeuf } 447442f04c3SJosh Poimboeuf 44828fe1d7bSSami Tolvanen if ((sym->sym.st_shndx > SHN_UNDEF && 44928fe1d7bSSami Tolvanen sym->sym.st_shndx < SHN_LORESERVE) || 45028fe1d7bSSami Tolvanen (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) { 45128fe1d7bSSami Tolvanen if (sym->sym.st_shndx != SHN_XINDEX) 45228fe1d7bSSami Tolvanen shndx = sym->sym.st_shndx; 45328fe1d7bSSami Tolvanen 45428fe1d7bSSami Tolvanen sym->sec = find_section_by_index(elf, shndx); 455442f04c3SJosh Poimboeuf if (!sym->sec) { 456442f04c3SJosh Poimboeuf WARN("couldn't find section for symbol %s", 457442f04c3SJosh Poimboeuf sym->name); 458442f04c3SJosh Poimboeuf goto err; 459442f04c3SJosh Poimboeuf } 4609a7827b7SPeter Zijlstra if (GELF_ST_TYPE(sym->sym.st_info) == STT_SECTION) { 461442f04c3SJosh Poimboeuf sym->name = sym->sec->name; 462442f04c3SJosh Poimboeuf sym->sec->sym = sym; 463442f04c3SJosh Poimboeuf } 464442f04c3SJosh Poimboeuf } else 465442f04c3SJosh Poimboeuf sym->sec = find_section_by_index(elf, 0); 466442f04c3SJosh Poimboeuf 4679a7827b7SPeter Zijlstra elf_add_symbol(elf, sym); 468442f04c3SJosh Poimboeuf } 469442f04c3SJosh Poimboeuf 4702daf7fabSJosh Poimboeuf if (opts.stats) { 4711e11f3fdSPeter Zijlstra printf("nr_symbols: %lu\n", (unsigned long)symbols_nr); 47225cf0d8aSPeter Zijlstra printf("symbol_bits: %d\n", elf->symbol_bits); 47325cf0d8aSPeter Zijlstra } 4741e11f3fdSPeter Zijlstra 47513810435SJosh Poimboeuf /* Create parent/child links for any cold subfunctions */ 47613810435SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 4779290e772SJosh Poimboeuf sec_for_each_sym(sec, sym) { 47822566c16SArtem Savkov char pname[MAX_NAME_LEN + 1]; 47922566c16SArtem Savkov size_t pnamelen; 48013810435SJosh Poimboeuf if (sym->type != STT_FUNC) 48113810435SJosh Poimboeuf continue; 482e000acc1SKristen Carlson Accardi 483e000acc1SKristen Carlson Accardi if (sym->pfunc == NULL) 484e000acc1SKristen Carlson Accardi sym->pfunc = sym; 485e000acc1SKristen Carlson Accardi 486e000acc1SKristen Carlson Accardi if (sym->cfunc == NULL) 487e000acc1SKristen Carlson Accardi sym->cfunc = sym; 488e000acc1SKristen Carlson Accardi 489bcb6fb5dSJosh Poimboeuf coldstr = strstr(sym->name, ".cold"); 49008b393d0SJosh Poimboeuf if (!coldstr) 49108b393d0SJosh Poimboeuf continue; 49208b393d0SJosh Poimboeuf 49322566c16SArtem Savkov pnamelen = coldstr - sym->name; 49422566c16SArtem Savkov if (pnamelen > MAX_NAME_LEN) { 49522566c16SArtem Savkov WARN("%s(): parent function name exceeds maximum length of %d characters", 49622566c16SArtem Savkov sym->name, MAX_NAME_LEN); 49722566c16SArtem Savkov return -1; 49822566c16SArtem Savkov } 49922566c16SArtem Savkov 50022566c16SArtem Savkov strncpy(pname, sym->name, pnamelen); 50122566c16SArtem Savkov pname[pnamelen] = '\0'; 50222566c16SArtem Savkov pfunc = find_symbol_by_name(elf, pname); 50313810435SJosh Poimboeuf 50413810435SJosh Poimboeuf if (!pfunc) { 50513810435SJosh Poimboeuf WARN("%s(): can't find parent function", 50613810435SJosh Poimboeuf sym->name); 5070b9301fbSArtem Savkov return -1; 50813810435SJosh Poimboeuf } 50913810435SJosh Poimboeuf 51013810435SJosh Poimboeuf sym->pfunc = pfunc; 51113810435SJosh Poimboeuf pfunc->cfunc = sym; 51208b393d0SJosh Poimboeuf 51308b393d0SJosh Poimboeuf /* 51408b393d0SJosh Poimboeuf * Unfortunately, -fnoreorder-functions puts the child 51508b393d0SJosh Poimboeuf * inside the parent. Remove the overlap so we can 51608b393d0SJosh Poimboeuf * have sane assumptions. 51708b393d0SJosh Poimboeuf * 51808b393d0SJosh Poimboeuf * Note that pfunc->len now no longer matches 51908b393d0SJosh Poimboeuf * pfunc->sym.st_size. 52008b393d0SJosh Poimboeuf */ 52108b393d0SJosh Poimboeuf if (sym->sec == pfunc->sec && 52208b393d0SJosh Poimboeuf sym->offset >= pfunc->offset && 52308b393d0SJosh Poimboeuf sym->offset + sym->len == pfunc->offset + pfunc->len) { 52408b393d0SJosh Poimboeuf pfunc->len -= sym->len; 52513810435SJosh Poimboeuf } 52613810435SJosh Poimboeuf } 52713810435SJosh Poimboeuf } 52813810435SJosh Poimboeuf 529442f04c3SJosh Poimboeuf return 0; 530442f04c3SJosh Poimboeuf 531442f04c3SJosh Poimboeuf err: 532442f04c3SJosh Poimboeuf free(sym); 533442f04c3SJosh Poimboeuf return -1; 534442f04c3SJosh Poimboeuf } 535442f04c3SJosh Poimboeuf 536d0c5c4ccSPeter Zijlstra static struct section *elf_create_reloc_section(struct elf *elf, 537d0c5c4ccSPeter Zijlstra struct section *base, 538d0c5c4ccSPeter Zijlstra int reltype); 539d0c5c4ccSPeter Zijlstra 540ef47cc01SPeter Zijlstra int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset, 54122682a07SMikulas Patocka unsigned int type, struct symbol *sym, s64 addend) 54234f7c96dSPeter Zijlstra { 543ef47cc01SPeter Zijlstra struct reloc *reloc; 54434f7c96dSPeter Zijlstra 545d0c5c4ccSPeter Zijlstra if (!sec->reloc && !elf_create_reloc_section(elf, sec, SHT_RELA)) 546d0c5c4ccSPeter Zijlstra return -1; 547d0c5c4ccSPeter Zijlstra 548ef47cc01SPeter Zijlstra reloc = malloc(sizeof(*reloc)); 549ef47cc01SPeter Zijlstra if (!reloc) { 550ef47cc01SPeter Zijlstra perror("malloc"); 551ef47cc01SPeter Zijlstra return -1; 552ef47cc01SPeter Zijlstra } 553ef47cc01SPeter Zijlstra memset(reloc, 0, sizeof(*reloc)); 554ef47cc01SPeter Zijlstra 555ef47cc01SPeter Zijlstra reloc->sec = sec->reloc; 556ef47cc01SPeter Zijlstra reloc->offset = offset; 557ef47cc01SPeter Zijlstra reloc->type = type; 558ef47cc01SPeter Zijlstra reloc->sym = sym; 559ef47cc01SPeter Zijlstra reloc->addend = addend; 560ef47cc01SPeter Zijlstra 56119526717SPeter Zijlstra list_add_tail(&reloc->sym_reloc_entry, &sym->reloc_list); 562ef47cc01SPeter Zijlstra list_add_tail(&reloc->list, &sec->reloc->reloc_list); 56325cf0d8aSPeter Zijlstra elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); 5643a647607SPeter Zijlstra 56586e1e054SMichael Forney sec->reloc->sh.sh_size += sec->reloc->sh.sh_entsize; 566ef47cc01SPeter Zijlstra sec->reloc->changed = true; 567ef47cc01SPeter Zijlstra 568ef47cc01SPeter Zijlstra return 0; 569ef47cc01SPeter Zijlstra } 570ef47cc01SPeter Zijlstra 5714abff6d4SPeter Zijlstra /* 5724abff6d4SPeter Zijlstra * Ensure that any reloc section containing references to @sym is marked 5734abff6d4SPeter Zijlstra * changed such that it will get re-generated in elf_rebuild_reloc_sections() 5744abff6d4SPeter Zijlstra * with the new symbol index. 5754abff6d4SPeter Zijlstra */ 5764abff6d4SPeter Zijlstra static void elf_dirty_reloc_sym(struct elf *elf, struct symbol *sym) 5774abff6d4SPeter Zijlstra { 5784abff6d4SPeter Zijlstra struct reloc *reloc; 5794abff6d4SPeter Zijlstra 58019526717SPeter Zijlstra list_for_each_entry(reloc, &sym->reloc_list, sym_reloc_entry) 58119526717SPeter Zijlstra reloc->sec->changed = true; 5824abff6d4SPeter Zijlstra } 5834abff6d4SPeter Zijlstra 5844abff6d4SPeter Zijlstra /* 585ead165faSPeter Zijlstra * The libelf API is terrible; gelf_update_sym*() takes a data block relative 586ead165faSPeter Zijlstra * index value, *NOT* the symbol index. As such, iterate the data blocks and 587ead165faSPeter Zijlstra * adjust index until it fits. 588ead165faSPeter Zijlstra * 589ead165faSPeter Zijlstra * If no data block is found, allow adding a new data block provided the index 590ead165faSPeter Zijlstra * is only one past the end. 5914abff6d4SPeter Zijlstra */ 592ead165faSPeter Zijlstra static int elf_update_symbol(struct elf *elf, struct section *symtab, 593ead165faSPeter Zijlstra struct section *symtab_shndx, struct symbol *sym) 5944abff6d4SPeter Zijlstra { 595ead165faSPeter Zijlstra Elf32_Word shndx = sym->sec ? sym->sec->idx : SHN_UNDEF; 596ead165faSPeter Zijlstra Elf_Data *symtab_data = NULL, *shndx_data = NULL; 597ead165faSPeter Zijlstra Elf64_Xword entsize = symtab->sh.sh_entsize; 598ead165faSPeter Zijlstra int max_idx, idx = sym->idx; 599ead165faSPeter Zijlstra Elf_Scn *s, *t = NULL; 6005141d3a0SSami Tolvanen bool is_special_shndx = sym->sym.st_shndx >= SHN_LORESERVE && 6015141d3a0SSami Tolvanen sym->sym.st_shndx != SHN_XINDEX; 6025141d3a0SSami Tolvanen 6035141d3a0SSami Tolvanen if (is_special_shndx) 6045141d3a0SSami Tolvanen shndx = sym->sym.st_shndx; 6054abff6d4SPeter Zijlstra 6064abff6d4SPeter Zijlstra s = elf_getscn(elf->elf, symtab->idx); 6074abff6d4SPeter Zijlstra if (!s) { 6084abff6d4SPeter Zijlstra WARN_ELF("elf_getscn"); 6094abff6d4SPeter Zijlstra return -1; 6104abff6d4SPeter Zijlstra } 6114abff6d4SPeter Zijlstra 6124abff6d4SPeter Zijlstra if (symtab_shndx) { 613ead165faSPeter Zijlstra t = elf_getscn(elf->elf, symtab_shndx->idx); 614ead165faSPeter Zijlstra if (!t) { 6154abff6d4SPeter Zijlstra WARN_ELF("elf_getscn"); 6164abff6d4SPeter Zijlstra return -1; 6174abff6d4SPeter Zijlstra } 618ead165faSPeter Zijlstra } 6194abff6d4SPeter Zijlstra 620ead165faSPeter Zijlstra for (;;) { 621ead165faSPeter Zijlstra /* get next data descriptor for the relevant sections */ 622ead165faSPeter Zijlstra symtab_data = elf_getdata(s, symtab_data); 623ead165faSPeter Zijlstra if (t) 624ead165faSPeter Zijlstra shndx_data = elf_getdata(t, shndx_data); 625ead165faSPeter Zijlstra 626ead165faSPeter Zijlstra /* end-of-list */ 627ead165faSPeter Zijlstra if (!symtab_data) { 62813f60e80SPeter Zijlstra /* 62913f60e80SPeter Zijlstra * Over-allocate to avoid O(n^2) symbol creation 63013f60e80SPeter Zijlstra * behaviour. The down side is that libelf doesn't 63113f60e80SPeter Zijlstra * like this; see elf_truncate_section() for the fixup. 63213f60e80SPeter Zijlstra */ 63313f60e80SPeter Zijlstra int num = max(1U, sym->idx/3); 634ead165faSPeter Zijlstra void *buf; 635ead165faSPeter Zijlstra 636ead165faSPeter Zijlstra if (idx) { 637ead165faSPeter Zijlstra /* we don't do holes in symbol tables */ 638ead165faSPeter Zijlstra WARN("index out of range"); 6394abff6d4SPeter Zijlstra return -1; 6404abff6d4SPeter Zijlstra } 6414abff6d4SPeter Zijlstra 642ead165faSPeter Zijlstra /* if @idx == 0, it's the next contiguous entry, create it */ 643ead165faSPeter Zijlstra symtab_data = elf_newdata(s); 644ead165faSPeter Zijlstra if (t) 645ead165faSPeter Zijlstra shndx_data = elf_newdata(t); 646ead165faSPeter Zijlstra 64713f60e80SPeter Zijlstra buf = calloc(num, entsize); 648ead165faSPeter Zijlstra if (!buf) { 649ead165faSPeter Zijlstra WARN("malloc"); 650ead165faSPeter Zijlstra return -1; 651ead165faSPeter Zijlstra } 652ead165faSPeter Zijlstra 653ead165faSPeter Zijlstra symtab_data->d_buf = buf; 65413f60e80SPeter Zijlstra symtab_data->d_size = num * entsize; 655ead165faSPeter Zijlstra symtab_data->d_align = 1; 656ead165faSPeter Zijlstra symtab_data->d_type = ELF_T_SYM; 657ead165faSPeter Zijlstra 658ead165faSPeter Zijlstra symtab->changed = true; 65913f60e80SPeter Zijlstra symtab->truncate = true; 660ead165faSPeter Zijlstra 661ead165faSPeter Zijlstra if (t) { 66213f60e80SPeter Zijlstra buf = calloc(num, sizeof(Elf32_Word)); 66313f60e80SPeter Zijlstra if (!buf) { 66413f60e80SPeter Zijlstra WARN("malloc"); 66513f60e80SPeter Zijlstra return -1; 66613f60e80SPeter Zijlstra } 66713f60e80SPeter Zijlstra 66813f60e80SPeter Zijlstra shndx_data->d_buf = buf; 66913f60e80SPeter Zijlstra shndx_data->d_size = num * sizeof(Elf32_Word); 670ead165faSPeter Zijlstra shndx_data->d_align = sizeof(Elf32_Word); 6714abff6d4SPeter Zijlstra shndx_data->d_type = ELF_T_WORD; 6724abff6d4SPeter Zijlstra 6734abff6d4SPeter Zijlstra symtab_shndx->changed = true; 67413f60e80SPeter Zijlstra symtab_shndx->truncate = true; 6754abff6d4SPeter Zijlstra } 6764abff6d4SPeter Zijlstra 677ead165faSPeter Zijlstra break; 678ead165faSPeter Zijlstra } 679ead165faSPeter Zijlstra 680ead165faSPeter Zijlstra /* empty blocks should not happen */ 681ead165faSPeter Zijlstra if (!symtab_data->d_size) { 682ead165faSPeter Zijlstra WARN("zero size data"); 683ead165faSPeter Zijlstra return -1; 684ead165faSPeter Zijlstra } 685ead165faSPeter Zijlstra 686ead165faSPeter Zijlstra /* is this the right block? */ 687ead165faSPeter Zijlstra max_idx = symtab_data->d_size / entsize; 688ead165faSPeter Zijlstra if (idx < max_idx) 689ead165faSPeter Zijlstra break; 690ead165faSPeter Zijlstra 691ead165faSPeter Zijlstra /* adjust index and try again */ 692ead165faSPeter Zijlstra idx -= max_idx; 693ead165faSPeter Zijlstra } 694ead165faSPeter Zijlstra 695ead165faSPeter Zijlstra /* something went side-ways */ 696ead165faSPeter Zijlstra if (idx < 0) { 697ead165faSPeter Zijlstra WARN("negative index"); 698ead165faSPeter Zijlstra return -1; 699ead165faSPeter Zijlstra } 700ead165faSPeter Zijlstra 701ead165faSPeter Zijlstra /* setup extended section index magic and write the symbol */ 7025141d3a0SSami Tolvanen if ((shndx >= SHN_UNDEF && shndx < SHN_LORESERVE) || is_special_shndx) { 703ead165faSPeter Zijlstra sym->sym.st_shndx = shndx; 704ead165faSPeter Zijlstra if (!shndx_data) 705ead165faSPeter Zijlstra shndx = 0; 706ead165faSPeter Zijlstra } else { 707ead165faSPeter Zijlstra sym->sym.st_shndx = SHN_XINDEX; 708ead165faSPeter Zijlstra if (!shndx_data) { 709ead165faSPeter Zijlstra WARN("no .symtab_shndx"); 710ead165faSPeter Zijlstra return -1; 711ead165faSPeter Zijlstra } 712ead165faSPeter Zijlstra } 713ead165faSPeter Zijlstra 714ead165faSPeter Zijlstra if (!gelf_update_symshndx(symtab_data, shndx_data, idx, &sym->sym, shndx)) { 715ead165faSPeter Zijlstra WARN_ELF("gelf_update_symshndx"); 716ead165faSPeter Zijlstra return -1; 717ead165faSPeter Zijlstra } 718ead165faSPeter Zijlstra 719ead165faSPeter Zijlstra return 0; 7204abff6d4SPeter Zijlstra } 7214abff6d4SPeter Zijlstra 7224abff6d4SPeter Zijlstra static struct symbol * 7234c91be8eSPeter Zijlstra __elf_create_symbol(struct elf *elf, struct symbol *sym) 7244abff6d4SPeter Zijlstra { 7254abff6d4SPeter Zijlstra struct section *symtab, *symtab_shndx; 726ead165faSPeter Zijlstra Elf32_Word first_non_local, new_idx; 7274c91be8eSPeter Zijlstra struct symbol *old; 7284abff6d4SPeter Zijlstra 7294abff6d4SPeter Zijlstra symtab = find_section_by_name(elf, ".symtab"); 7304abff6d4SPeter Zijlstra if (symtab) { 7314abff6d4SPeter Zijlstra symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); 7324abff6d4SPeter Zijlstra } else { 7334abff6d4SPeter Zijlstra WARN("no .symtab"); 7344abff6d4SPeter Zijlstra return NULL; 7354abff6d4SPeter Zijlstra } 7364abff6d4SPeter Zijlstra 7374c91be8eSPeter Zijlstra new_idx = symtab->sh.sh_size / symtab->sh.sh_entsize; 7384abff6d4SPeter Zijlstra 7394c91be8eSPeter Zijlstra if (GELF_ST_BIND(sym->sym.st_info) != STB_LOCAL) 7404c91be8eSPeter Zijlstra goto non_local; 741ead165faSPeter Zijlstra 742ead165faSPeter Zijlstra /* 743ead165faSPeter Zijlstra * Move the first global symbol, as per sh_info, into a new, higher 744ead165faSPeter Zijlstra * symbol index. This fees up a spot for a new local symbol. 745ead165faSPeter Zijlstra */ 746ead165faSPeter Zijlstra first_non_local = symtab->sh.sh_info; 747ead165faSPeter Zijlstra old = find_symbol_by_index(elf, first_non_local); 748ead165faSPeter Zijlstra if (old) { 749ead165faSPeter Zijlstra old->idx = new_idx; 750ead165faSPeter Zijlstra 751ead165faSPeter Zijlstra hlist_del(&old->hash); 752ead165faSPeter Zijlstra elf_hash_add(symbol, &old->hash, old->idx); 753ead165faSPeter Zijlstra 754ead165faSPeter Zijlstra elf_dirty_reloc_sym(elf, old); 755ead165faSPeter Zijlstra 756ead165faSPeter Zijlstra if (elf_update_symbol(elf, symtab, symtab_shndx, old)) { 757ead165faSPeter Zijlstra WARN("elf_update_symbol move"); 7584abff6d4SPeter Zijlstra return NULL; 7594abff6d4SPeter Zijlstra } 7604abff6d4SPeter Zijlstra 761ead165faSPeter Zijlstra new_idx = first_non_local; 762ead165faSPeter Zijlstra } 763ead165faSPeter Zijlstra 7644c91be8eSPeter Zijlstra /* 7654c91be8eSPeter Zijlstra * Either way, we will add a LOCAL symbol. 7664c91be8eSPeter Zijlstra */ 7674c91be8eSPeter Zijlstra symtab->sh.sh_info += 1; 7684c91be8eSPeter Zijlstra 7694c91be8eSPeter Zijlstra non_local: 770ead165faSPeter Zijlstra sym->idx = new_idx; 771ead165faSPeter Zijlstra if (elf_update_symbol(elf, symtab, symtab_shndx, sym)) { 772ead165faSPeter Zijlstra WARN("elf_update_symbol"); 7734abff6d4SPeter Zijlstra return NULL; 7744abff6d4SPeter Zijlstra } 7754abff6d4SPeter Zijlstra 77613f60e80SPeter Zijlstra symtab->sh.sh_size += symtab->sh.sh_entsize; 77713f60e80SPeter Zijlstra symtab->changed = true; 77813f60e80SPeter Zijlstra 77913f60e80SPeter Zijlstra if (symtab_shndx) { 78013f60e80SPeter Zijlstra symtab_shndx->sh.sh_size += sizeof(Elf32_Word); 78113f60e80SPeter Zijlstra symtab_shndx->changed = true; 78213f60e80SPeter Zijlstra } 78313f60e80SPeter Zijlstra 7844c91be8eSPeter Zijlstra return sym; 7854c91be8eSPeter Zijlstra } 786ead165faSPeter Zijlstra 7874c91be8eSPeter Zijlstra static struct symbol * 7884c91be8eSPeter Zijlstra elf_create_section_symbol(struct elf *elf, struct section *sec) 7894c91be8eSPeter Zijlstra { 7904c91be8eSPeter Zijlstra struct symbol *sym = calloc(1, sizeof(*sym)); 7914c91be8eSPeter Zijlstra 7924c91be8eSPeter Zijlstra if (!sym) { 7934c91be8eSPeter Zijlstra perror("malloc"); 7944c91be8eSPeter Zijlstra return NULL; 7954c91be8eSPeter Zijlstra } 7964c91be8eSPeter Zijlstra 7974c91be8eSPeter Zijlstra sym->name = sec->name; 7984c91be8eSPeter Zijlstra sym->sec = sec; 7994c91be8eSPeter Zijlstra 8004c91be8eSPeter Zijlstra // st_name 0 8014c91be8eSPeter Zijlstra sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION); 8024c91be8eSPeter Zijlstra // st_other 0 8034c91be8eSPeter Zijlstra // st_value 0 8044c91be8eSPeter Zijlstra // st_size 0 8054c91be8eSPeter Zijlstra 8064c91be8eSPeter Zijlstra sym = __elf_create_symbol(elf, sym); 8074c91be8eSPeter Zijlstra if (sym) 8084abff6d4SPeter Zijlstra elf_add_symbol(elf, sym); 8094abff6d4SPeter Zijlstra 8104abff6d4SPeter Zijlstra return sym; 8114abff6d4SPeter Zijlstra } 8124abff6d4SPeter Zijlstra 8139f2899feSPeter Zijlstra static int elf_add_string(struct elf *elf, struct section *strtab, char *str); 8149f2899feSPeter Zijlstra 8159f2899feSPeter Zijlstra struct symbol * 8169f2899feSPeter Zijlstra elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size) 8179f2899feSPeter Zijlstra { 8189f2899feSPeter Zijlstra struct symbol *sym = calloc(1, sizeof(*sym)); 8199f2899feSPeter Zijlstra size_t namelen = strlen(orig->name) + sizeof("__pfx_"); 8209f2899feSPeter Zijlstra char *name = malloc(namelen); 8219f2899feSPeter Zijlstra 8229f2899feSPeter Zijlstra if (!sym || !name) { 8239f2899feSPeter Zijlstra perror("malloc"); 8249f2899feSPeter Zijlstra return NULL; 8259f2899feSPeter Zijlstra } 8269f2899feSPeter Zijlstra 8279f2899feSPeter Zijlstra snprintf(name, namelen, "__pfx_%s", orig->name); 8289f2899feSPeter Zijlstra 8299f2899feSPeter Zijlstra sym->name = name; 8309f2899feSPeter Zijlstra sym->sec = orig->sec; 8319f2899feSPeter Zijlstra 8329f2899feSPeter Zijlstra sym->sym.st_name = elf_add_string(elf, NULL, name); 8339f2899feSPeter Zijlstra sym->sym.st_info = orig->sym.st_info; 8349f2899feSPeter Zijlstra sym->sym.st_value = orig->sym.st_value - size; 8359f2899feSPeter Zijlstra sym->sym.st_size = size; 8369f2899feSPeter Zijlstra 8379f2899feSPeter Zijlstra sym = __elf_create_symbol(elf, sym); 8389f2899feSPeter Zijlstra if (sym) 8399f2899feSPeter Zijlstra elf_add_symbol(elf, sym); 8409f2899feSPeter Zijlstra 8419f2899feSPeter Zijlstra return sym; 8429f2899feSPeter Zijlstra } 8439f2899feSPeter Zijlstra 844ef47cc01SPeter Zijlstra int elf_add_reloc_to_insn(struct elf *elf, struct section *sec, 845ef47cc01SPeter Zijlstra unsigned long offset, unsigned int type, 846ef47cc01SPeter Zijlstra struct section *insn_sec, unsigned long insn_off) 847ef47cc01SPeter Zijlstra { 8484abff6d4SPeter Zijlstra struct symbol *sym = insn_sec->sym; 8494abff6d4SPeter Zijlstra int addend = insn_off; 850ef47cc01SPeter Zijlstra 851ef47cc01SPeter Zijlstra if (!sym) { 8524abff6d4SPeter Zijlstra /* 8534abff6d4SPeter Zijlstra * Due to how weak functions work, we must use section based 8544abff6d4SPeter Zijlstra * relocations. Symbol based relocations would result in the 8554abff6d4SPeter Zijlstra * weak and non-weak function annotations being overlaid on the 8564abff6d4SPeter Zijlstra * non-weak function after linking. 8574abff6d4SPeter Zijlstra */ 8584abff6d4SPeter Zijlstra sym = elf_create_section_symbol(elf, insn_sec); 8594abff6d4SPeter Zijlstra if (!sym) 860ef47cc01SPeter Zijlstra return -1; 861ef47cc01SPeter Zijlstra 8624abff6d4SPeter Zijlstra insn_sec->sym = sym; 863ef47cc01SPeter Zijlstra } 864ef47cc01SPeter Zijlstra 865ef47cc01SPeter Zijlstra return elf_add_reloc(elf, sec, offset, type, sym, addend); 86634f7c96dSPeter Zijlstra } 86734f7c96dSPeter Zijlstra 868fb414783SMatt Helsley static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) 869fb414783SMatt Helsley { 870fb414783SMatt Helsley if (!gelf_getrel(sec->data, i, &reloc->rel)) { 871fb414783SMatt Helsley WARN_ELF("gelf_getrel"); 872fb414783SMatt Helsley return -1; 873fb414783SMatt Helsley } 874fb414783SMatt Helsley reloc->type = GELF_R_TYPE(reloc->rel.r_info); 875fb414783SMatt Helsley reloc->addend = 0; 876fb414783SMatt Helsley reloc->offset = reloc->rel.r_offset; 877fb414783SMatt Helsley *symndx = GELF_R_SYM(reloc->rel.r_info); 878fb414783SMatt Helsley return 0; 879fb414783SMatt Helsley } 880fb414783SMatt Helsley 881fb414783SMatt Helsley static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) 882fb414783SMatt Helsley { 883fb414783SMatt Helsley if (!gelf_getrela(sec->data, i, &reloc->rela)) { 884fb414783SMatt Helsley WARN_ELF("gelf_getrela"); 885fb414783SMatt Helsley return -1; 886fb414783SMatt Helsley } 887fb414783SMatt Helsley reloc->type = GELF_R_TYPE(reloc->rela.r_info); 888fb414783SMatt Helsley reloc->addend = reloc->rela.r_addend; 889fb414783SMatt Helsley reloc->offset = reloc->rela.r_offset; 890fb414783SMatt Helsley *symndx = GELF_R_SYM(reloc->rela.r_info); 891fb414783SMatt Helsley return 0; 892fb414783SMatt Helsley } 893fb414783SMatt Helsley 894f1974222SMatt Helsley static int read_relocs(struct elf *elf) 895442f04c3SJosh Poimboeuf { 89619526717SPeter Zijlstra unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0; 897442f04c3SJosh Poimboeuf struct section *sec; 898f1974222SMatt Helsley struct reloc *reloc; 899442f04c3SJosh Poimboeuf unsigned int symndx; 90019526717SPeter Zijlstra struct symbol *sym; 90119526717SPeter Zijlstra int i; 902442f04c3SJosh Poimboeuf 903d33b9035SPeter Zijlstra if (!elf_alloc_hash(reloc, elf->text_size / 16)) 90425cf0d8aSPeter Zijlstra return -1; 90525cf0d8aSPeter Zijlstra 906442f04c3SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 907fb414783SMatt Helsley if ((sec->sh.sh_type != SHT_RELA) && 908fb414783SMatt Helsley (sec->sh.sh_type != SHT_REL)) 909442f04c3SJosh Poimboeuf continue; 910442f04c3SJosh Poimboeuf 9111e968bf5SSami Tolvanen sec->base = find_section_by_index(elf, sec->sh.sh_info); 912442f04c3SJosh Poimboeuf if (!sec->base) { 913f1974222SMatt Helsley WARN("can't find base section for reloc section %s", 914442f04c3SJosh Poimboeuf sec->name); 915442f04c3SJosh Poimboeuf return -1; 916442f04c3SJosh Poimboeuf } 917442f04c3SJosh Poimboeuf 918f1974222SMatt Helsley sec->base->reloc = sec; 919442f04c3SJosh Poimboeuf 920f1974222SMatt Helsley nr_reloc = 0; 9218045b8f0SThomas Weißschuh sec->reloc_data = calloc(sec->sh.sh_size / sec->sh.sh_entsize, sizeof(*reloc)); 9228045b8f0SThomas Weißschuh if (!sec->reloc_data) { 9238045b8f0SThomas Weißschuh perror("calloc"); 924442f04c3SJosh Poimboeuf return -1; 925442f04c3SJosh Poimboeuf } 9268045b8f0SThomas Weißschuh for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { 9278045b8f0SThomas Weißschuh reloc = &sec->reloc_data[i]; 928fb414783SMatt Helsley switch (sec->sh.sh_type) { 929fb414783SMatt Helsley case SHT_REL: 930fb414783SMatt Helsley if (read_rel_reloc(sec, i, reloc, &symndx)) 931442f04c3SJosh Poimboeuf return -1; 932fb414783SMatt Helsley break; 933fb414783SMatt Helsley case SHT_RELA: 934fb414783SMatt Helsley if (read_rela_reloc(sec, i, reloc, &symndx)) 935fb414783SMatt Helsley return -1; 936fb414783SMatt Helsley break; 937fb414783SMatt Helsley default: return -1; 938442f04c3SJosh Poimboeuf } 939442f04c3SJosh Poimboeuf 940f1974222SMatt Helsley reloc->sec = sec; 941d832c005SPeter Zijlstra reloc->idx = i; 94219526717SPeter Zijlstra reloc->sym = sym = find_symbol_by_index(elf, symndx); 943f1974222SMatt Helsley if (!reloc->sym) { 944f1974222SMatt Helsley WARN("can't find reloc entry symbol %d for %s", 945442f04c3SJosh Poimboeuf symndx, sec->name); 946442f04c3SJosh Poimboeuf return -1; 947442f04c3SJosh Poimboeuf } 948042ba73fSJosh Poimboeuf 94919526717SPeter Zijlstra list_add_tail(&reloc->sym_reloc_entry, &sym->reloc_list); 9503a647607SPeter Zijlstra list_add_tail(&reloc->list, &sec->reloc_list); 95125cf0d8aSPeter Zijlstra elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); 9523a647607SPeter Zijlstra 953f1974222SMatt Helsley nr_reloc++; 954442f04c3SJosh Poimboeuf } 955f1974222SMatt Helsley max_reloc = max(max_reloc, nr_reloc); 956f1974222SMatt Helsley tot_reloc += nr_reloc; 9571e11f3fdSPeter Zijlstra } 9581e11f3fdSPeter Zijlstra 9592daf7fabSJosh Poimboeuf if (opts.stats) { 960f1974222SMatt Helsley printf("max_reloc: %lu\n", max_reloc); 961f1974222SMatt Helsley printf("tot_reloc: %lu\n", tot_reloc); 96225cf0d8aSPeter Zijlstra printf("reloc_bits: %d\n", elf->reloc_bits); 963442f04c3SJosh Poimboeuf } 964442f04c3SJosh Poimboeuf 965442f04c3SJosh Poimboeuf return 0; 966442f04c3SJosh Poimboeuf } 967442f04c3SJosh Poimboeuf 968bc359ff2SIngo Molnar struct elf *elf_open_read(const char *name, int flags) 969442f04c3SJosh Poimboeuf { 970442f04c3SJosh Poimboeuf struct elf *elf; 971627fce14SJosh Poimboeuf Elf_Cmd cmd; 972442f04c3SJosh Poimboeuf 973442f04c3SJosh Poimboeuf elf_version(EV_CURRENT); 974442f04c3SJosh Poimboeuf 975442f04c3SJosh Poimboeuf elf = malloc(sizeof(*elf)); 976442f04c3SJosh Poimboeuf if (!elf) { 977442f04c3SJosh Poimboeuf perror("malloc"); 978442f04c3SJosh Poimboeuf return NULL; 979442f04c3SJosh Poimboeuf } 98034f7c96dSPeter Zijlstra memset(elf, 0, offsetof(struct elf, sections)); 981442f04c3SJosh Poimboeuf 982442f04c3SJosh Poimboeuf INIT_LIST_HEAD(&elf->sections); 983442f04c3SJosh Poimboeuf 984627fce14SJosh Poimboeuf elf->fd = open(name, flags); 985442f04c3SJosh Poimboeuf if (elf->fd == -1) { 986385d11b1SJosh Poimboeuf fprintf(stderr, "objtool: Can't open '%s': %s\n", 987385d11b1SJosh Poimboeuf name, strerror(errno)); 988442f04c3SJosh Poimboeuf goto err; 989442f04c3SJosh Poimboeuf } 990442f04c3SJosh Poimboeuf 991627fce14SJosh Poimboeuf if ((flags & O_ACCMODE) == O_RDONLY) 992627fce14SJosh Poimboeuf cmd = ELF_C_READ_MMAP; 993627fce14SJosh Poimboeuf else if ((flags & O_ACCMODE) == O_RDWR) 994627fce14SJosh Poimboeuf cmd = ELF_C_RDWR; 995627fce14SJosh Poimboeuf else /* O_WRONLY */ 996627fce14SJosh Poimboeuf cmd = ELF_C_WRITE; 997627fce14SJosh Poimboeuf 998627fce14SJosh Poimboeuf elf->elf = elf_begin(elf->fd, cmd, NULL); 999442f04c3SJosh Poimboeuf if (!elf->elf) { 1000baa41469SJosh Poimboeuf WARN_ELF("elf_begin"); 1001442f04c3SJosh Poimboeuf goto err; 1002442f04c3SJosh Poimboeuf } 1003442f04c3SJosh Poimboeuf 1004442f04c3SJosh Poimboeuf if (!gelf_getehdr(elf->elf, &elf->ehdr)) { 1005baa41469SJosh Poimboeuf WARN_ELF("gelf_getehdr"); 1006442f04c3SJosh Poimboeuf goto err; 1007442f04c3SJosh Poimboeuf } 1008442f04c3SJosh Poimboeuf 1009442f04c3SJosh Poimboeuf if (read_sections(elf)) 1010442f04c3SJosh Poimboeuf goto err; 1011442f04c3SJosh Poimboeuf 1012442f04c3SJosh Poimboeuf if (read_symbols(elf)) 1013442f04c3SJosh Poimboeuf goto err; 1014442f04c3SJosh Poimboeuf 1015f1974222SMatt Helsley if (read_relocs(elf)) 1016442f04c3SJosh Poimboeuf goto err; 1017442f04c3SJosh Poimboeuf 1018442f04c3SJosh Poimboeuf return elf; 1019442f04c3SJosh Poimboeuf 1020442f04c3SJosh Poimboeuf err: 1021442f04c3SJosh Poimboeuf elf_close(elf); 1022442f04c3SJosh Poimboeuf return NULL; 1023442f04c3SJosh Poimboeuf } 1024442f04c3SJosh Poimboeuf 1025417a4dc9SPeter Zijlstra static int elf_add_string(struct elf *elf, struct section *strtab, char *str) 1026417a4dc9SPeter Zijlstra { 1027417a4dc9SPeter Zijlstra Elf_Data *data; 1028417a4dc9SPeter Zijlstra Elf_Scn *s; 1029417a4dc9SPeter Zijlstra int len; 1030417a4dc9SPeter Zijlstra 1031417a4dc9SPeter Zijlstra if (!strtab) 1032417a4dc9SPeter Zijlstra strtab = find_section_by_name(elf, ".strtab"); 1033417a4dc9SPeter Zijlstra if (!strtab) { 1034417a4dc9SPeter Zijlstra WARN("can't find .strtab section"); 1035417a4dc9SPeter Zijlstra return -1; 1036417a4dc9SPeter Zijlstra } 1037417a4dc9SPeter Zijlstra 1038417a4dc9SPeter Zijlstra s = elf_getscn(elf->elf, strtab->idx); 1039417a4dc9SPeter Zijlstra if (!s) { 1040417a4dc9SPeter Zijlstra WARN_ELF("elf_getscn"); 1041417a4dc9SPeter Zijlstra return -1; 1042417a4dc9SPeter Zijlstra } 1043417a4dc9SPeter Zijlstra 1044417a4dc9SPeter Zijlstra data = elf_newdata(s); 1045417a4dc9SPeter Zijlstra if (!data) { 1046417a4dc9SPeter Zijlstra WARN_ELF("elf_newdata"); 1047417a4dc9SPeter Zijlstra return -1; 1048417a4dc9SPeter Zijlstra } 1049417a4dc9SPeter Zijlstra 1050417a4dc9SPeter Zijlstra data->d_buf = str; 1051417a4dc9SPeter Zijlstra data->d_size = strlen(str) + 1; 1052417a4dc9SPeter Zijlstra data->d_align = 1; 1053417a4dc9SPeter Zijlstra 1054fe255fe6SJoe Lawrence len = strtab->sh.sh_size; 1055fe255fe6SJoe Lawrence strtab->sh.sh_size += data->d_size; 1056417a4dc9SPeter Zijlstra strtab->changed = true; 1057417a4dc9SPeter Zijlstra 1058417a4dc9SPeter Zijlstra return len; 1059417a4dc9SPeter Zijlstra } 1060417a4dc9SPeter Zijlstra 1061627fce14SJosh Poimboeuf struct section *elf_create_section(struct elf *elf, const char *name, 1062*2707579dSJosh Poimboeuf size_t entsize, int nr) 1063627fce14SJosh Poimboeuf { 1064627fce14SJosh Poimboeuf struct section *sec, *shstrtab; 1065627fce14SJosh Poimboeuf size_t size = entsize * nr; 10663c3ea503SMichael Forney Elf_Scn *s; 1067627fce14SJosh Poimboeuf 1068627fce14SJosh Poimboeuf sec = malloc(sizeof(*sec)); 1069627fce14SJosh Poimboeuf if (!sec) { 1070627fce14SJosh Poimboeuf perror("malloc"); 1071627fce14SJosh Poimboeuf return NULL; 1072627fce14SJosh Poimboeuf } 1073627fce14SJosh Poimboeuf memset(sec, 0, sizeof(*sec)); 1074627fce14SJosh Poimboeuf 1075627fce14SJosh Poimboeuf INIT_LIST_HEAD(&sec->symbol_list); 1076f1974222SMatt Helsley INIT_LIST_HEAD(&sec->reloc_list); 1077627fce14SJosh Poimboeuf 1078627fce14SJosh Poimboeuf s = elf_newscn(elf->elf); 1079627fce14SJosh Poimboeuf if (!s) { 1080627fce14SJosh Poimboeuf WARN_ELF("elf_newscn"); 1081627fce14SJosh Poimboeuf return NULL; 1082627fce14SJosh Poimboeuf } 1083627fce14SJosh Poimboeuf 1084627fce14SJosh Poimboeuf sec->name = strdup(name); 1085627fce14SJosh Poimboeuf if (!sec->name) { 1086627fce14SJosh Poimboeuf perror("strdup"); 1087627fce14SJosh Poimboeuf return NULL; 1088627fce14SJosh Poimboeuf } 1089627fce14SJosh Poimboeuf 1090627fce14SJosh Poimboeuf sec->idx = elf_ndxscn(s); 1091627fce14SJosh Poimboeuf sec->changed = true; 1092627fce14SJosh Poimboeuf 1093627fce14SJosh Poimboeuf sec->data = elf_newdata(s); 1094627fce14SJosh Poimboeuf if (!sec->data) { 1095627fce14SJosh Poimboeuf WARN_ELF("elf_newdata"); 1096627fce14SJosh Poimboeuf return NULL; 1097627fce14SJosh Poimboeuf } 1098627fce14SJosh Poimboeuf 1099627fce14SJosh Poimboeuf sec->data->d_size = size; 1100627fce14SJosh Poimboeuf sec->data->d_align = 1; 1101627fce14SJosh Poimboeuf 1102627fce14SJosh Poimboeuf if (size) { 1103627fce14SJosh Poimboeuf sec->data->d_buf = malloc(size); 1104627fce14SJosh Poimboeuf if (!sec->data->d_buf) { 1105627fce14SJosh Poimboeuf perror("malloc"); 1106627fce14SJosh Poimboeuf return NULL; 1107627fce14SJosh Poimboeuf } 1108627fce14SJosh Poimboeuf memset(sec->data->d_buf, 0, size); 1109627fce14SJosh Poimboeuf } 1110627fce14SJosh Poimboeuf 1111627fce14SJosh Poimboeuf if (!gelf_getshdr(s, &sec->sh)) { 1112627fce14SJosh Poimboeuf WARN_ELF("gelf_getshdr"); 1113627fce14SJosh Poimboeuf return NULL; 1114627fce14SJosh Poimboeuf } 1115627fce14SJosh Poimboeuf 1116627fce14SJosh Poimboeuf sec->sh.sh_size = size; 1117627fce14SJosh Poimboeuf sec->sh.sh_entsize = entsize; 1118627fce14SJosh Poimboeuf sec->sh.sh_type = SHT_PROGBITS; 1119627fce14SJosh Poimboeuf sec->sh.sh_addralign = 1; 1120*2707579dSJosh Poimboeuf sec->sh.sh_flags = SHF_ALLOC; 1121627fce14SJosh Poimboeuf 11226d77d3b4SSimon Ser /* Add section name to .shstrtab (or .strtab for Clang) */ 1123627fce14SJosh Poimboeuf shstrtab = find_section_by_name(elf, ".shstrtab"); 11246d77d3b4SSimon Ser if (!shstrtab) 11256d77d3b4SSimon Ser shstrtab = find_section_by_name(elf, ".strtab"); 1126627fce14SJosh Poimboeuf if (!shstrtab) { 11276d77d3b4SSimon Ser WARN("can't find .shstrtab or .strtab section"); 1128627fce14SJosh Poimboeuf return NULL; 1129627fce14SJosh Poimboeuf } 1130417a4dc9SPeter Zijlstra sec->sh.sh_name = elf_add_string(elf, shstrtab, sec->name); 1131417a4dc9SPeter Zijlstra if (sec->sh.sh_name == -1) 1132627fce14SJosh Poimboeuf return NULL; 1133627fce14SJosh Poimboeuf 113453038996SPeter Zijlstra list_add_tail(&sec->list, &elf->sections); 113525cf0d8aSPeter Zijlstra elf_hash_add(section, &sec->hash, sec->idx); 113625cf0d8aSPeter Zijlstra elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); 113753038996SPeter Zijlstra 11382b10be23SPeter Zijlstra elf->changed = true; 11392b10be23SPeter Zijlstra 1140627fce14SJosh Poimboeuf return sec; 1141627fce14SJosh Poimboeuf } 1142627fce14SJosh Poimboeuf 1143fb414783SMatt Helsley static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base) 1144fb414783SMatt Helsley { 1145fb414783SMatt Helsley char *relocname; 1146fb414783SMatt Helsley struct section *sec; 1147fb414783SMatt Helsley 1148fb414783SMatt Helsley relocname = malloc(strlen(base->name) + strlen(".rel") + 1); 1149fb414783SMatt Helsley if (!relocname) { 1150fb414783SMatt Helsley perror("malloc"); 1151fb414783SMatt Helsley return NULL; 1152fb414783SMatt Helsley } 1153fb414783SMatt Helsley strcpy(relocname, ".rel"); 1154fb414783SMatt Helsley strcat(relocname, base->name); 1155fb414783SMatt Helsley 1156*2707579dSJosh Poimboeuf sec = elf_create_section(elf, relocname, sizeof(GElf_Rel), 0); 1157fb414783SMatt Helsley free(relocname); 1158fb414783SMatt Helsley if (!sec) 1159fb414783SMatt Helsley return NULL; 1160fb414783SMatt Helsley 1161fb414783SMatt Helsley base->reloc = sec; 1162fb414783SMatt Helsley sec->base = base; 1163fb414783SMatt Helsley 1164fb414783SMatt Helsley sec->sh.sh_type = SHT_REL; 1165fb414783SMatt Helsley sec->sh.sh_addralign = 8; 1166fb414783SMatt Helsley sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; 1167fb414783SMatt Helsley sec->sh.sh_info = base->idx; 1168fb414783SMatt Helsley sec->sh.sh_flags = SHF_INFO_LINK; 1169fb414783SMatt Helsley 1170fb414783SMatt Helsley return sec; 1171fb414783SMatt Helsley } 1172fb414783SMatt Helsley 1173fb414783SMatt Helsley static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base) 1174627fce14SJosh Poimboeuf { 1175f1974222SMatt Helsley char *relocname; 1176627fce14SJosh Poimboeuf struct section *sec; 117786ea7f36SChristophe Leroy int addrsize = elf_class_addrsize(elf); 1178627fce14SJosh Poimboeuf 1179f1974222SMatt Helsley relocname = malloc(strlen(base->name) + strlen(".rela") + 1); 1180f1974222SMatt Helsley if (!relocname) { 1181627fce14SJosh Poimboeuf perror("malloc"); 1182627fce14SJosh Poimboeuf return NULL; 1183627fce14SJosh Poimboeuf } 1184f1974222SMatt Helsley strcpy(relocname, ".rela"); 1185f1974222SMatt Helsley strcat(relocname, base->name); 1186627fce14SJosh Poimboeuf 118786ea7f36SChristophe Leroy if (addrsize == sizeof(u32)) 1188*2707579dSJosh Poimboeuf sec = elf_create_section(elf, relocname, sizeof(Elf32_Rela), 0); 118986ea7f36SChristophe Leroy else 1190*2707579dSJosh Poimboeuf sec = elf_create_section(elf, relocname, sizeof(GElf_Rela), 0); 1191f1974222SMatt Helsley free(relocname); 1192627fce14SJosh Poimboeuf if (!sec) 1193627fce14SJosh Poimboeuf return NULL; 1194627fce14SJosh Poimboeuf 1195f1974222SMatt Helsley base->reloc = sec; 1196627fce14SJosh Poimboeuf sec->base = base; 1197627fce14SJosh Poimboeuf 1198627fce14SJosh Poimboeuf sec->sh.sh_type = SHT_RELA; 119986ea7f36SChristophe Leroy sec->sh.sh_addralign = addrsize; 1200627fce14SJosh Poimboeuf sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; 1201627fce14SJosh Poimboeuf sec->sh.sh_info = base->idx; 1202627fce14SJosh Poimboeuf sec->sh.sh_flags = SHF_INFO_LINK; 1203627fce14SJosh Poimboeuf 1204627fce14SJosh Poimboeuf return sec; 1205627fce14SJosh Poimboeuf } 1206627fce14SJosh Poimboeuf 1207d0c5c4ccSPeter Zijlstra static struct section *elf_create_reloc_section(struct elf *elf, 1208fb414783SMatt Helsley struct section *base, 1209fb414783SMatt Helsley int reltype) 1210fb414783SMatt Helsley { 1211fb414783SMatt Helsley switch (reltype) { 1212fb414783SMatt Helsley case SHT_REL: return elf_create_rel_reloc_section(elf, base); 1213fb414783SMatt Helsley case SHT_RELA: return elf_create_rela_reloc_section(elf, base); 1214fb414783SMatt Helsley default: return NULL; 1215fb414783SMatt Helsley } 1216fb414783SMatt Helsley } 1217fb414783SMatt Helsley 121886e1e054SMichael Forney static int elf_rebuild_rel_reloc_section(struct section *sec) 1219627fce14SJosh Poimboeuf { 1220f1974222SMatt Helsley struct reloc *reloc; 122186e1e054SMichael Forney int idx = 0; 1222a1a664ecSMartin Schwidefsky void *buf; 1223fb414783SMatt Helsley 1224fb414783SMatt Helsley /* Allocate a buffer for relocations */ 122586e1e054SMichael Forney buf = malloc(sec->sh.sh_size); 1226a1a664ecSMartin Schwidefsky if (!buf) { 1227fb414783SMatt Helsley perror("malloc"); 1228fb414783SMatt Helsley return -1; 1229fb414783SMatt Helsley } 1230fb414783SMatt Helsley 1231a1a664ecSMartin Schwidefsky sec->data->d_buf = buf; 123286e1e054SMichael Forney sec->data->d_size = sec->sh.sh_size; 1233a1a664ecSMartin Schwidefsky sec->data->d_type = ELF_T_REL; 1234fb414783SMatt Helsley 1235fb414783SMatt Helsley idx = 0; 1236fb414783SMatt Helsley list_for_each_entry(reloc, &sec->reloc_list, list) { 1237a1a664ecSMartin Schwidefsky reloc->rel.r_offset = reloc->offset; 1238a1a664ecSMartin Schwidefsky reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 1239b46179d6SMichael Forney if (!gelf_update_rel(sec->data, idx, &reloc->rel)) { 1240b46179d6SMichael Forney WARN_ELF("gelf_update_rel"); 1241b46179d6SMichael Forney return -1; 1242b46179d6SMichael Forney } 1243fb414783SMatt Helsley idx++; 1244fb414783SMatt Helsley } 1245fb414783SMatt Helsley 1246fb414783SMatt Helsley return 0; 1247fb414783SMatt Helsley } 1248fb414783SMatt Helsley 124986e1e054SMichael Forney static int elf_rebuild_rela_reloc_section(struct section *sec) 1250fb414783SMatt Helsley { 1251fb414783SMatt Helsley struct reloc *reloc; 125286e1e054SMichael Forney int idx = 0; 1253a1a664ecSMartin Schwidefsky void *buf; 1254627fce14SJosh Poimboeuf 1255fb414783SMatt Helsley /* Allocate a buffer for relocations with addends */ 125686e1e054SMichael Forney buf = malloc(sec->sh.sh_size); 1257a1a664ecSMartin Schwidefsky if (!buf) { 1258627fce14SJosh Poimboeuf perror("malloc"); 1259627fce14SJosh Poimboeuf return -1; 1260627fce14SJosh Poimboeuf } 1261627fce14SJosh Poimboeuf 1262a1a664ecSMartin Schwidefsky sec->data->d_buf = buf; 126386e1e054SMichael Forney sec->data->d_size = sec->sh.sh_size; 1264a1a664ecSMartin Schwidefsky sec->data->d_type = ELF_T_RELA; 1265627fce14SJosh Poimboeuf 1266627fce14SJosh Poimboeuf idx = 0; 1267f1974222SMatt Helsley list_for_each_entry(reloc, &sec->reloc_list, list) { 1268a1a664ecSMartin Schwidefsky reloc->rela.r_offset = reloc->offset; 1269a1a664ecSMartin Schwidefsky reloc->rela.r_addend = reloc->addend; 1270a1a664ecSMartin Schwidefsky reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 1271b46179d6SMichael Forney if (!gelf_update_rela(sec->data, idx, &reloc->rela)) { 1272b46179d6SMichael Forney WARN_ELF("gelf_update_rela"); 1273b46179d6SMichael Forney return -1; 1274b46179d6SMichael Forney } 1275627fce14SJosh Poimboeuf idx++; 1276627fce14SJosh Poimboeuf } 1277627fce14SJosh Poimboeuf 1278627fce14SJosh Poimboeuf return 0; 1279627fce14SJosh Poimboeuf } 1280627fce14SJosh Poimboeuf 12813a647607SPeter Zijlstra static int elf_rebuild_reloc_section(struct elf *elf, struct section *sec) 1282fb414783SMatt Helsley { 1283fb414783SMatt Helsley switch (sec->sh.sh_type) { 128486e1e054SMichael Forney case SHT_REL: return elf_rebuild_rel_reloc_section(sec); 128586e1e054SMichael Forney case SHT_RELA: return elf_rebuild_rela_reloc_section(sec); 1286fb414783SMatt Helsley default: return -1; 1287fb414783SMatt Helsley } 1288fb414783SMatt Helsley } 1289fb414783SMatt Helsley 1290fdabdd0bSPeter Zijlstra int elf_write_insn(struct elf *elf, struct section *sec, 1291fdabdd0bSPeter Zijlstra unsigned long offset, unsigned int len, 1292fdabdd0bSPeter Zijlstra const char *insn) 1293fdabdd0bSPeter Zijlstra { 1294fdabdd0bSPeter Zijlstra Elf_Data *data = sec->data; 1295fdabdd0bSPeter Zijlstra 1296fdabdd0bSPeter Zijlstra if (data->d_type != ELF_T_BYTE || data->d_off) { 1297fdabdd0bSPeter Zijlstra WARN("write to unexpected data for section: %s", sec->name); 1298fdabdd0bSPeter Zijlstra return -1; 1299fdabdd0bSPeter Zijlstra } 1300fdabdd0bSPeter Zijlstra 1301fdabdd0bSPeter Zijlstra memcpy(data->d_buf + offset, insn, len); 1302fdabdd0bSPeter Zijlstra elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY); 1303fdabdd0bSPeter Zijlstra 1304fdabdd0bSPeter Zijlstra elf->changed = true; 1305fdabdd0bSPeter Zijlstra 1306fdabdd0bSPeter Zijlstra return 0; 1307fdabdd0bSPeter Zijlstra } 1308fdabdd0bSPeter Zijlstra 1309d832c005SPeter Zijlstra int elf_write_reloc(struct elf *elf, struct reloc *reloc) 1310fdabdd0bSPeter Zijlstra { 1311d832c005SPeter Zijlstra struct section *sec = reloc->sec; 1312fdabdd0bSPeter Zijlstra 1313d832c005SPeter Zijlstra if (sec->sh.sh_type == SHT_REL) { 1314d832c005SPeter Zijlstra reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 1315d832c005SPeter Zijlstra reloc->rel.r_offset = reloc->offset; 1316fdabdd0bSPeter Zijlstra 1317d832c005SPeter Zijlstra if (!gelf_update_rel(sec->data, reloc->idx, &reloc->rel)) { 1318d832c005SPeter Zijlstra WARN_ELF("gelf_update_rel"); 1319d832c005SPeter Zijlstra return -1; 1320d832c005SPeter Zijlstra } 1321d832c005SPeter Zijlstra } else { 1322d832c005SPeter Zijlstra reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 1323d832c005SPeter Zijlstra reloc->rela.r_addend = reloc->addend; 1324d832c005SPeter Zijlstra reloc->rela.r_offset = reloc->offset; 1325d832c005SPeter Zijlstra 1326d832c005SPeter Zijlstra if (!gelf_update_rela(sec->data, reloc->idx, &reloc->rela)) { 1327fdabdd0bSPeter Zijlstra WARN_ELF("gelf_update_rela"); 1328fdabdd0bSPeter Zijlstra return -1; 1329fdabdd0bSPeter Zijlstra } 1330d832c005SPeter Zijlstra } 1331fdabdd0bSPeter Zijlstra 1332fdabdd0bSPeter Zijlstra elf->changed = true; 1333fdabdd0bSPeter Zijlstra 1334fdabdd0bSPeter Zijlstra return 0; 1335fdabdd0bSPeter Zijlstra } 1336fdabdd0bSPeter Zijlstra 133713f60e80SPeter Zijlstra /* 133813f60e80SPeter Zijlstra * When Elf_Scn::sh_size is smaller than the combined Elf_Data::d_size 133913f60e80SPeter Zijlstra * do you: 134013f60e80SPeter Zijlstra * 134113f60e80SPeter Zijlstra * A) adhere to the section header and truncate the data, or 134213f60e80SPeter Zijlstra * B) ignore the section header and write out all the data you've got? 134313f60e80SPeter Zijlstra * 134413f60e80SPeter Zijlstra * Yes, libelf sucks and we need to manually truncate if we over-allocate data. 134513f60e80SPeter Zijlstra */ 134613f60e80SPeter Zijlstra static int elf_truncate_section(struct elf *elf, struct section *sec) 134713f60e80SPeter Zijlstra { 134813f60e80SPeter Zijlstra u64 size = sec->sh.sh_size; 134913f60e80SPeter Zijlstra bool truncated = false; 135013f60e80SPeter Zijlstra Elf_Data *data = NULL; 135113f60e80SPeter Zijlstra Elf_Scn *s; 135213f60e80SPeter Zijlstra 135313f60e80SPeter Zijlstra s = elf_getscn(elf->elf, sec->idx); 135413f60e80SPeter Zijlstra if (!s) { 135513f60e80SPeter Zijlstra WARN_ELF("elf_getscn"); 135613f60e80SPeter Zijlstra return -1; 135713f60e80SPeter Zijlstra } 135813f60e80SPeter Zijlstra 135913f60e80SPeter Zijlstra for (;;) { 136013f60e80SPeter Zijlstra /* get next data descriptor for the relevant section */ 136113f60e80SPeter Zijlstra data = elf_getdata(s, data); 136213f60e80SPeter Zijlstra 136313f60e80SPeter Zijlstra if (!data) { 136413f60e80SPeter Zijlstra if (size) { 136513f60e80SPeter Zijlstra WARN("end of section data but non-zero size left\n"); 136613f60e80SPeter Zijlstra return -1; 136713f60e80SPeter Zijlstra } 136813f60e80SPeter Zijlstra return 0; 136913f60e80SPeter Zijlstra } 137013f60e80SPeter Zijlstra 137113f60e80SPeter Zijlstra if (truncated) { 137213f60e80SPeter Zijlstra /* when we remove symbols */ 137313f60e80SPeter Zijlstra WARN("truncated; but more data\n"); 137413f60e80SPeter Zijlstra return -1; 137513f60e80SPeter Zijlstra } 137613f60e80SPeter Zijlstra 137713f60e80SPeter Zijlstra if (!data->d_size) { 137813f60e80SPeter Zijlstra WARN("zero size data"); 137913f60e80SPeter Zijlstra return -1; 138013f60e80SPeter Zijlstra } 138113f60e80SPeter Zijlstra 138213f60e80SPeter Zijlstra if (data->d_size > size) { 138313f60e80SPeter Zijlstra truncated = true; 138413f60e80SPeter Zijlstra data->d_size = size; 138513f60e80SPeter Zijlstra } 138613f60e80SPeter Zijlstra 138713f60e80SPeter Zijlstra size -= data->d_size; 138813f60e80SPeter Zijlstra } 138913f60e80SPeter Zijlstra } 139013f60e80SPeter Zijlstra 13912b10be23SPeter Zijlstra int elf_write(struct elf *elf) 1392627fce14SJosh Poimboeuf { 1393627fce14SJosh Poimboeuf struct section *sec; 1394627fce14SJosh Poimboeuf Elf_Scn *s; 1395627fce14SJosh Poimboeuf 13962daf7fabSJosh Poimboeuf if (opts.dryrun) 1397f2d3a250SPeter Zijlstra return 0; 1398f2d3a250SPeter Zijlstra 13993a647607SPeter Zijlstra /* Update changed relocation sections and section headers: */ 1400627fce14SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 140113f60e80SPeter Zijlstra if (sec->truncate) 140213f60e80SPeter Zijlstra elf_truncate_section(elf, sec); 140313f60e80SPeter Zijlstra 1404627fce14SJosh Poimboeuf if (sec->changed) { 1405627fce14SJosh Poimboeuf s = elf_getscn(elf->elf, sec->idx); 1406627fce14SJosh Poimboeuf if (!s) { 1407627fce14SJosh Poimboeuf WARN_ELF("elf_getscn"); 1408627fce14SJosh Poimboeuf return -1; 1409627fce14SJosh Poimboeuf } 1410627fce14SJosh Poimboeuf if (!gelf_update_shdr(s, &sec->sh)) { 1411627fce14SJosh Poimboeuf WARN_ELF("gelf_update_shdr"); 1412627fce14SJosh Poimboeuf return -1; 1413627fce14SJosh Poimboeuf } 14142b10be23SPeter Zijlstra 141586e1e054SMichael Forney if (sec->base && 141686e1e054SMichael Forney elf_rebuild_reloc_section(elf, sec)) { 141786e1e054SMichael Forney WARN("elf_rebuild_reloc_section"); 141886e1e054SMichael Forney return -1; 141986e1e054SMichael Forney } 142086e1e054SMichael Forney 14212b10be23SPeter Zijlstra sec->changed = false; 14223a647607SPeter Zijlstra elf->changed = true; 1423627fce14SJosh Poimboeuf } 1424627fce14SJosh Poimboeuf } 1425627fce14SJosh Poimboeuf 142697dab2aeSJosh Poimboeuf /* Make sure the new section header entries get updated properly. */ 142797dab2aeSJosh Poimboeuf elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY); 142897dab2aeSJosh Poimboeuf 142997dab2aeSJosh Poimboeuf /* Write all changes to the file. */ 1430627fce14SJosh Poimboeuf if (elf_update(elf->elf, ELF_C_WRITE) < 0) { 1431627fce14SJosh Poimboeuf WARN_ELF("elf_update"); 1432627fce14SJosh Poimboeuf return -1; 1433627fce14SJosh Poimboeuf } 1434627fce14SJosh Poimboeuf 14352b10be23SPeter Zijlstra elf->changed = false; 14362b10be23SPeter Zijlstra 1437627fce14SJosh Poimboeuf return 0; 1438627fce14SJosh Poimboeuf } 1439627fce14SJosh Poimboeuf 1440442f04c3SJosh Poimboeuf void elf_close(struct elf *elf) 1441442f04c3SJosh Poimboeuf { 1442442f04c3SJosh Poimboeuf struct section *sec, *tmpsec; 1443442f04c3SJosh Poimboeuf struct symbol *sym, *tmpsym; 1444f1974222SMatt Helsley struct reloc *reloc, *tmpreloc; 1445442f04c3SJosh Poimboeuf 1446baa41469SJosh Poimboeuf if (elf->elf) 1447baa41469SJosh Poimboeuf elf_end(elf->elf); 1448baa41469SJosh Poimboeuf 1449baa41469SJosh Poimboeuf if (elf->fd > 0) 1450baa41469SJosh Poimboeuf close(elf->fd); 1451baa41469SJosh Poimboeuf 1452442f04c3SJosh Poimboeuf list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { 1453a196e171SJosh Poimboeuf list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { 1454442f04c3SJosh Poimboeuf list_del(&sym->list); 1455042ba73fSJosh Poimboeuf hash_del(&sym->hash); 1456442f04c3SJosh Poimboeuf } 1457f1974222SMatt Helsley list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) { 1458f1974222SMatt Helsley list_del(&reloc->list); 1459f1974222SMatt Helsley hash_del(&reloc->hash); 1460442f04c3SJosh Poimboeuf } 1461442f04c3SJosh Poimboeuf list_del(&sec->list); 14628045b8f0SThomas Weißschuh free(sec->reloc_data); 1463442f04c3SJosh Poimboeuf } 1464baa41469SJosh Poimboeuf 14658045b8f0SThomas Weißschuh free(elf->symbol_data); 14668045b8f0SThomas Weißschuh free(elf->section_data); 1467442f04c3SJosh Poimboeuf free(elf); 1468442f04c3SJosh Poimboeuf } 1469