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 287442f04c3SJosh Poimboeuf for (i = 0; i < sections_nr; i++) { 288442f04c3SJosh Poimboeuf sec = malloc(sizeof(*sec)); 289442f04c3SJosh Poimboeuf if (!sec) { 290442f04c3SJosh Poimboeuf perror("malloc"); 291442f04c3SJosh Poimboeuf return -1; 292442f04c3SJosh Poimboeuf } 293442f04c3SJosh Poimboeuf memset(sec, 0, sizeof(*sec)); 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 359ead165faSPeter Zijlstra INIT_LIST_HEAD(&sym->pv_target); 360ead165faSPeter Zijlstra sym->alias = sym; 361ead165faSPeter Zijlstra 3629a7827b7SPeter Zijlstra sym->type = GELF_ST_TYPE(sym->sym.st_info); 3639a7827b7SPeter Zijlstra sym->bind = GELF_ST_BIND(sym->sym.st_info); 3649a7827b7SPeter Zijlstra 365753da417SJosh Poimboeuf if (sym->type == STT_FILE) 366753da417SJosh Poimboeuf elf->num_files++; 367753da417SJosh Poimboeuf 3689a7827b7SPeter Zijlstra sym->offset = sym->sym.st_value; 3699a7827b7SPeter Zijlstra sym->len = sym->sym.st_size; 3709a7827b7SPeter Zijlstra 3715da6aea3SPeter Zijlstra __sym_for_each(iter, &sym->sec->symbol_tree, sym->offset, sym->offset) { 3725da6aea3SPeter Zijlstra if (iter->offset == sym->offset && iter->type == sym->type) 3735da6aea3SPeter Zijlstra iter->alias = sym; 3745da6aea3SPeter Zijlstra } 3755da6aea3SPeter Zijlstra 3765da6aea3SPeter Zijlstra __sym_insert(sym, &sym->sec->symbol_tree); 3779a7827b7SPeter Zijlstra pnode = rb_prev(&sym->node); 3789a7827b7SPeter Zijlstra if (pnode) 3799a7827b7SPeter Zijlstra entry = &rb_entry(pnode, struct symbol, node)->list; 3809a7827b7SPeter Zijlstra else 3819a7827b7SPeter Zijlstra entry = &sym->sec->symbol_list; 3829a7827b7SPeter Zijlstra list_add(&sym->list, entry); 38325cf0d8aSPeter Zijlstra elf_hash_add(symbol, &sym->hash, sym->idx); 38425cf0d8aSPeter Zijlstra elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name)); 3859a7827b7SPeter Zijlstra 3869a7827b7SPeter Zijlstra /* 3879a7827b7SPeter Zijlstra * Don't store empty STT_NOTYPE symbols in the rbtree. They 3889a7827b7SPeter Zijlstra * can exist within a function, confusing the sorting. 3899a7827b7SPeter Zijlstra */ 3909a7827b7SPeter Zijlstra if (!sym->len) 3915da6aea3SPeter Zijlstra __sym_remove(sym, &sym->sec->symbol_tree); 3929a7827b7SPeter Zijlstra } 3939a7827b7SPeter Zijlstra 394442f04c3SJosh Poimboeuf static int read_symbols(struct elf *elf) 395442f04c3SJosh Poimboeuf { 39628fe1d7bSSami Tolvanen struct section *symtab, *symtab_shndx, *sec; 3972a362eccSPeter Zijlstra struct symbol *sym, *pfunc; 398442f04c3SJosh Poimboeuf int symbols_nr, i; 39913810435SJosh Poimboeuf char *coldstr; 40028fe1d7bSSami Tolvanen Elf_Data *shndx_data = NULL; 40128fe1d7bSSami Tolvanen Elf32_Word shndx; 402442f04c3SJosh Poimboeuf 403442f04c3SJosh Poimboeuf symtab = find_section_by_name(elf, ".symtab"); 40425cf0d8aSPeter Zijlstra if (symtab) { 40528fe1d7bSSami Tolvanen symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); 40628fe1d7bSSami Tolvanen if (symtab_shndx) 40728fe1d7bSSami Tolvanen shndx_data = symtab_shndx->data; 40828fe1d7bSSami Tolvanen 409442f04c3SJosh Poimboeuf symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize; 41025cf0d8aSPeter Zijlstra } else { 41125cf0d8aSPeter Zijlstra /* 41225cf0d8aSPeter Zijlstra * A missing symbol table is actually possible if it's an empty 41325cf0d8aSPeter Zijlstra * .o file. This can happen for thunk_64.o. Make sure to at 41425cf0d8aSPeter Zijlstra * least allocate the symbol hash tables so we can do symbol 41525cf0d8aSPeter Zijlstra * lookups without crashing. 41625cf0d8aSPeter Zijlstra */ 41725cf0d8aSPeter Zijlstra symbols_nr = 0; 41825cf0d8aSPeter Zijlstra } 41925cf0d8aSPeter Zijlstra 42025cf0d8aSPeter Zijlstra if (!elf_alloc_hash(symbol, symbols_nr) || 42125cf0d8aSPeter Zijlstra !elf_alloc_hash(symbol_name, symbols_nr)) 42225cf0d8aSPeter Zijlstra return -1; 423442f04c3SJosh Poimboeuf 424442f04c3SJosh Poimboeuf for (i = 0; i < symbols_nr; i++) { 425442f04c3SJosh Poimboeuf sym = malloc(sizeof(*sym)); 426442f04c3SJosh Poimboeuf if (!sym) { 427442f04c3SJosh Poimboeuf perror("malloc"); 428442f04c3SJosh Poimboeuf return -1; 429442f04c3SJosh Poimboeuf } 430442f04c3SJosh Poimboeuf memset(sym, 0, sizeof(*sym)); 431442f04c3SJosh Poimboeuf 432442f04c3SJosh Poimboeuf sym->idx = i; 433442f04c3SJosh Poimboeuf 43428fe1d7bSSami Tolvanen if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym, 43528fe1d7bSSami Tolvanen &shndx)) { 43628fe1d7bSSami Tolvanen WARN_ELF("gelf_getsymshndx"); 437442f04c3SJosh Poimboeuf goto err; 438442f04c3SJosh Poimboeuf } 439442f04c3SJosh Poimboeuf 440442f04c3SJosh Poimboeuf sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, 441442f04c3SJosh Poimboeuf sym->sym.st_name); 442442f04c3SJosh Poimboeuf if (!sym->name) { 443baa41469SJosh Poimboeuf WARN_ELF("elf_strptr"); 444442f04c3SJosh Poimboeuf goto err; 445442f04c3SJosh Poimboeuf } 446442f04c3SJosh Poimboeuf 44728fe1d7bSSami Tolvanen if ((sym->sym.st_shndx > SHN_UNDEF && 44828fe1d7bSSami Tolvanen sym->sym.st_shndx < SHN_LORESERVE) || 44928fe1d7bSSami Tolvanen (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) { 45028fe1d7bSSami Tolvanen if (sym->sym.st_shndx != SHN_XINDEX) 45128fe1d7bSSami Tolvanen shndx = sym->sym.st_shndx; 45228fe1d7bSSami Tolvanen 45328fe1d7bSSami Tolvanen sym->sec = find_section_by_index(elf, shndx); 454442f04c3SJosh Poimboeuf if (!sym->sec) { 455442f04c3SJosh Poimboeuf WARN("couldn't find section for symbol %s", 456442f04c3SJosh Poimboeuf sym->name); 457442f04c3SJosh Poimboeuf goto err; 458442f04c3SJosh Poimboeuf } 4599a7827b7SPeter Zijlstra if (GELF_ST_TYPE(sym->sym.st_info) == STT_SECTION) { 460442f04c3SJosh Poimboeuf sym->name = sym->sec->name; 461442f04c3SJosh Poimboeuf sym->sec->sym = sym; 462442f04c3SJosh Poimboeuf } 463442f04c3SJosh Poimboeuf } else 464442f04c3SJosh Poimboeuf sym->sec = find_section_by_index(elf, 0); 465442f04c3SJosh Poimboeuf 4669a7827b7SPeter Zijlstra elf_add_symbol(elf, sym); 467442f04c3SJosh Poimboeuf } 468442f04c3SJosh Poimboeuf 4692daf7fabSJosh Poimboeuf if (opts.stats) { 4701e11f3fdSPeter Zijlstra printf("nr_symbols: %lu\n", (unsigned long)symbols_nr); 47125cf0d8aSPeter Zijlstra printf("symbol_bits: %d\n", elf->symbol_bits); 47225cf0d8aSPeter Zijlstra } 4731e11f3fdSPeter Zijlstra 47413810435SJosh Poimboeuf /* Create parent/child links for any cold subfunctions */ 47513810435SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 47613810435SJosh Poimboeuf list_for_each_entry(sym, &sec->symbol_list, list) { 47722566c16SArtem Savkov char pname[MAX_NAME_LEN + 1]; 47822566c16SArtem Savkov size_t pnamelen; 47913810435SJosh Poimboeuf if (sym->type != STT_FUNC) 48013810435SJosh Poimboeuf continue; 481e000acc1SKristen Carlson Accardi 482e000acc1SKristen Carlson Accardi if (sym->pfunc == NULL) 483e000acc1SKristen Carlson Accardi sym->pfunc = sym; 484e000acc1SKristen Carlson Accardi 485e000acc1SKristen Carlson Accardi if (sym->cfunc == NULL) 486e000acc1SKristen Carlson Accardi sym->cfunc = sym; 487e000acc1SKristen Carlson Accardi 488bcb6fb5dSJosh Poimboeuf coldstr = strstr(sym->name, ".cold"); 48908b393d0SJosh Poimboeuf if (!coldstr) 49008b393d0SJosh Poimboeuf continue; 49108b393d0SJosh Poimboeuf 49222566c16SArtem Savkov pnamelen = coldstr - sym->name; 49322566c16SArtem Savkov if (pnamelen > MAX_NAME_LEN) { 49422566c16SArtem Savkov WARN("%s(): parent function name exceeds maximum length of %d characters", 49522566c16SArtem Savkov sym->name, MAX_NAME_LEN); 49622566c16SArtem Savkov return -1; 49722566c16SArtem Savkov } 49822566c16SArtem Savkov 49922566c16SArtem Savkov strncpy(pname, sym->name, pnamelen); 50022566c16SArtem Savkov pname[pnamelen] = '\0'; 50122566c16SArtem Savkov pfunc = find_symbol_by_name(elf, pname); 50213810435SJosh Poimboeuf 50313810435SJosh Poimboeuf if (!pfunc) { 50413810435SJosh Poimboeuf WARN("%s(): can't find parent function", 50513810435SJosh Poimboeuf sym->name); 5060b9301fbSArtem Savkov return -1; 50713810435SJosh Poimboeuf } 50813810435SJosh Poimboeuf 50913810435SJosh Poimboeuf sym->pfunc = pfunc; 51013810435SJosh Poimboeuf pfunc->cfunc = sym; 51108b393d0SJosh Poimboeuf 51208b393d0SJosh Poimboeuf /* 51308b393d0SJosh Poimboeuf * Unfortunately, -fnoreorder-functions puts the child 51408b393d0SJosh Poimboeuf * inside the parent. Remove the overlap so we can 51508b393d0SJosh Poimboeuf * have sane assumptions. 51608b393d0SJosh Poimboeuf * 51708b393d0SJosh Poimboeuf * Note that pfunc->len now no longer matches 51808b393d0SJosh Poimboeuf * pfunc->sym.st_size. 51908b393d0SJosh Poimboeuf */ 52008b393d0SJosh Poimboeuf if (sym->sec == pfunc->sec && 52108b393d0SJosh Poimboeuf sym->offset >= pfunc->offset && 52208b393d0SJosh Poimboeuf sym->offset + sym->len == pfunc->offset + pfunc->len) { 52308b393d0SJosh Poimboeuf pfunc->len -= sym->len; 52413810435SJosh Poimboeuf } 52513810435SJosh Poimboeuf } 52613810435SJosh Poimboeuf } 52713810435SJosh Poimboeuf 528442f04c3SJosh Poimboeuf return 0; 529442f04c3SJosh Poimboeuf 530442f04c3SJosh Poimboeuf err: 531442f04c3SJosh Poimboeuf free(sym); 532442f04c3SJosh Poimboeuf return -1; 533442f04c3SJosh Poimboeuf } 534442f04c3SJosh Poimboeuf 535d0c5c4ccSPeter Zijlstra static struct section *elf_create_reloc_section(struct elf *elf, 536d0c5c4ccSPeter Zijlstra struct section *base, 537d0c5c4ccSPeter Zijlstra int reltype); 538d0c5c4ccSPeter Zijlstra 539ef47cc01SPeter Zijlstra int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset, 54022682a07SMikulas Patocka unsigned int type, struct symbol *sym, s64 addend) 54134f7c96dSPeter Zijlstra { 542ef47cc01SPeter Zijlstra struct reloc *reloc; 54334f7c96dSPeter Zijlstra 544d0c5c4ccSPeter Zijlstra if (!sec->reloc && !elf_create_reloc_section(elf, sec, SHT_RELA)) 545d0c5c4ccSPeter Zijlstra return -1; 546d0c5c4ccSPeter Zijlstra 547ef47cc01SPeter Zijlstra reloc = malloc(sizeof(*reloc)); 548ef47cc01SPeter Zijlstra if (!reloc) { 549ef47cc01SPeter Zijlstra perror("malloc"); 550ef47cc01SPeter Zijlstra return -1; 551ef47cc01SPeter Zijlstra } 552ef47cc01SPeter Zijlstra memset(reloc, 0, sizeof(*reloc)); 553ef47cc01SPeter Zijlstra 554ef47cc01SPeter Zijlstra reloc->sec = sec->reloc; 555ef47cc01SPeter Zijlstra reloc->offset = offset; 556ef47cc01SPeter Zijlstra reloc->type = type; 557ef47cc01SPeter Zijlstra reloc->sym = sym; 558ef47cc01SPeter Zijlstra reloc->addend = addend; 559ef47cc01SPeter Zijlstra 560ef47cc01SPeter Zijlstra list_add_tail(&reloc->list, &sec->reloc->reloc_list); 56125cf0d8aSPeter Zijlstra elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); 5623a647607SPeter Zijlstra 56386e1e054SMichael Forney sec->reloc->sh.sh_size += sec->reloc->sh.sh_entsize; 564ef47cc01SPeter Zijlstra sec->reloc->changed = true; 565ef47cc01SPeter Zijlstra 566ef47cc01SPeter Zijlstra return 0; 567ef47cc01SPeter Zijlstra } 568ef47cc01SPeter Zijlstra 5694abff6d4SPeter Zijlstra /* 5704abff6d4SPeter Zijlstra * Ensure that any reloc section containing references to @sym is marked 5714abff6d4SPeter Zijlstra * changed such that it will get re-generated in elf_rebuild_reloc_sections() 5724abff6d4SPeter Zijlstra * with the new symbol index. 5734abff6d4SPeter Zijlstra */ 5744abff6d4SPeter Zijlstra static void elf_dirty_reloc_sym(struct elf *elf, struct symbol *sym) 5754abff6d4SPeter Zijlstra { 5764abff6d4SPeter Zijlstra struct section *sec; 5774abff6d4SPeter Zijlstra 5784abff6d4SPeter Zijlstra list_for_each_entry(sec, &elf->sections, list) { 5794abff6d4SPeter Zijlstra struct reloc *reloc; 5804abff6d4SPeter Zijlstra 5814abff6d4SPeter Zijlstra if (sec->changed) 5824abff6d4SPeter Zijlstra continue; 5834abff6d4SPeter Zijlstra 5844abff6d4SPeter Zijlstra list_for_each_entry(reloc, &sec->reloc_list, list) { 5854abff6d4SPeter Zijlstra if (reloc->sym == sym) { 5864abff6d4SPeter Zijlstra sec->changed = true; 5874abff6d4SPeter Zijlstra break; 5884abff6d4SPeter Zijlstra } 5894abff6d4SPeter Zijlstra } 5904abff6d4SPeter Zijlstra } 5914abff6d4SPeter Zijlstra } 5924abff6d4SPeter Zijlstra 5934abff6d4SPeter Zijlstra /* 594ead165faSPeter Zijlstra * The libelf API is terrible; gelf_update_sym*() takes a data block relative 595ead165faSPeter Zijlstra * index value, *NOT* the symbol index. As such, iterate the data blocks and 596ead165faSPeter Zijlstra * adjust index until it fits. 597ead165faSPeter Zijlstra * 598ead165faSPeter Zijlstra * If no data block is found, allow adding a new data block provided the index 599ead165faSPeter Zijlstra * is only one past the end. 6004abff6d4SPeter Zijlstra */ 601ead165faSPeter Zijlstra static int elf_update_symbol(struct elf *elf, struct section *symtab, 602ead165faSPeter Zijlstra struct section *symtab_shndx, struct symbol *sym) 6034abff6d4SPeter Zijlstra { 604ead165faSPeter Zijlstra Elf32_Word shndx = sym->sec ? sym->sec->idx : SHN_UNDEF; 605ead165faSPeter Zijlstra Elf_Data *symtab_data = NULL, *shndx_data = NULL; 606ead165faSPeter Zijlstra Elf64_Xword entsize = symtab->sh.sh_entsize; 607ead165faSPeter Zijlstra int max_idx, idx = sym->idx; 608ead165faSPeter Zijlstra Elf_Scn *s, *t = NULL; 6095141d3a0SSami Tolvanen bool is_special_shndx = sym->sym.st_shndx >= SHN_LORESERVE && 6105141d3a0SSami Tolvanen sym->sym.st_shndx != SHN_XINDEX; 6115141d3a0SSami Tolvanen 6125141d3a0SSami Tolvanen if (is_special_shndx) 6135141d3a0SSami Tolvanen shndx = sym->sym.st_shndx; 6144abff6d4SPeter Zijlstra 6154abff6d4SPeter Zijlstra s = elf_getscn(elf->elf, symtab->idx); 6164abff6d4SPeter Zijlstra if (!s) { 6174abff6d4SPeter Zijlstra WARN_ELF("elf_getscn"); 6184abff6d4SPeter Zijlstra return -1; 6194abff6d4SPeter Zijlstra } 6204abff6d4SPeter Zijlstra 6214abff6d4SPeter Zijlstra if (symtab_shndx) { 622ead165faSPeter Zijlstra t = elf_getscn(elf->elf, symtab_shndx->idx); 623ead165faSPeter Zijlstra if (!t) { 6244abff6d4SPeter Zijlstra WARN_ELF("elf_getscn"); 6254abff6d4SPeter Zijlstra return -1; 6264abff6d4SPeter Zijlstra } 627ead165faSPeter Zijlstra } 6284abff6d4SPeter Zijlstra 629ead165faSPeter Zijlstra for (;;) { 630ead165faSPeter Zijlstra /* get next data descriptor for the relevant sections */ 631ead165faSPeter Zijlstra symtab_data = elf_getdata(s, symtab_data); 632ead165faSPeter Zijlstra if (t) 633ead165faSPeter Zijlstra shndx_data = elf_getdata(t, shndx_data); 634ead165faSPeter Zijlstra 635ead165faSPeter Zijlstra /* end-of-list */ 636ead165faSPeter Zijlstra if (!symtab_data) { 637ead165faSPeter Zijlstra void *buf; 638ead165faSPeter Zijlstra 639ead165faSPeter Zijlstra if (idx) { 640ead165faSPeter Zijlstra /* we don't do holes in symbol tables */ 641ead165faSPeter Zijlstra WARN("index out of range"); 6424abff6d4SPeter Zijlstra return -1; 6434abff6d4SPeter Zijlstra } 6444abff6d4SPeter Zijlstra 645ead165faSPeter Zijlstra /* if @idx == 0, it's the next contiguous entry, create it */ 646ead165faSPeter Zijlstra symtab_data = elf_newdata(s); 647ead165faSPeter Zijlstra if (t) 648ead165faSPeter Zijlstra shndx_data = elf_newdata(t); 649ead165faSPeter Zijlstra 650ead165faSPeter Zijlstra buf = calloc(1, entsize); 651ead165faSPeter Zijlstra if (!buf) { 652ead165faSPeter Zijlstra WARN("malloc"); 653ead165faSPeter Zijlstra return -1; 654ead165faSPeter Zijlstra } 655ead165faSPeter Zijlstra 656ead165faSPeter Zijlstra symtab_data->d_buf = buf; 657ead165faSPeter Zijlstra symtab_data->d_size = entsize; 658ead165faSPeter Zijlstra symtab_data->d_align = 1; 659ead165faSPeter Zijlstra symtab_data->d_type = ELF_T_SYM; 660ead165faSPeter Zijlstra 661ead165faSPeter Zijlstra symtab->sh.sh_size += entsize; 662ead165faSPeter Zijlstra symtab->changed = true; 663ead165faSPeter Zijlstra 664ead165faSPeter Zijlstra if (t) { 6654abff6d4SPeter Zijlstra shndx_data->d_buf = &sym->sec->idx; 6664abff6d4SPeter Zijlstra shndx_data->d_size = sizeof(Elf32_Word); 667ead165faSPeter Zijlstra shndx_data->d_align = sizeof(Elf32_Word); 6684abff6d4SPeter Zijlstra shndx_data->d_type = ELF_T_WORD; 6694abff6d4SPeter Zijlstra 670ead165faSPeter Zijlstra symtab_shndx->sh.sh_size += sizeof(Elf32_Word); 6714abff6d4SPeter Zijlstra symtab_shndx->changed = true; 6724abff6d4SPeter Zijlstra } 6734abff6d4SPeter Zijlstra 674ead165faSPeter Zijlstra break; 675ead165faSPeter Zijlstra } 676ead165faSPeter Zijlstra 677ead165faSPeter Zijlstra /* empty blocks should not happen */ 678ead165faSPeter Zijlstra if (!symtab_data->d_size) { 679ead165faSPeter Zijlstra WARN("zero size data"); 680ead165faSPeter Zijlstra return -1; 681ead165faSPeter Zijlstra } 682ead165faSPeter Zijlstra 683ead165faSPeter Zijlstra /* is this the right block? */ 684ead165faSPeter Zijlstra max_idx = symtab_data->d_size / entsize; 685ead165faSPeter Zijlstra if (idx < max_idx) 686ead165faSPeter Zijlstra break; 687ead165faSPeter Zijlstra 688ead165faSPeter Zijlstra /* adjust index and try again */ 689ead165faSPeter Zijlstra idx -= max_idx; 690ead165faSPeter Zijlstra } 691ead165faSPeter Zijlstra 692ead165faSPeter Zijlstra /* something went side-ways */ 693ead165faSPeter Zijlstra if (idx < 0) { 694ead165faSPeter Zijlstra WARN("negative index"); 695ead165faSPeter Zijlstra return -1; 696ead165faSPeter Zijlstra } 697ead165faSPeter Zijlstra 698ead165faSPeter Zijlstra /* setup extended section index magic and write the symbol */ 6995141d3a0SSami Tolvanen if ((shndx >= SHN_UNDEF && shndx < SHN_LORESERVE) || is_special_shndx) { 700ead165faSPeter Zijlstra sym->sym.st_shndx = shndx; 701ead165faSPeter Zijlstra if (!shndx_data) 702ead165faSPeter Zijlstra shndx = 0; 703ead165faSPeter Zijlstra } else { 704ead165faSPeter Zijlstra sym->sym.st_shndx = SHN_XINDEX; 705ead165faSPeter Zijlstra if (!shndx_data) { 706ead165faSPeter Zijlstra WARN("no .symtab_shndx"); 707ead165faSPeter Zijlstra return -1; 708ead165faSPeter Zijlstra } 709ead165faSPeter Zijlstra } 710ead165faSPeter Zijlstra 711ead165faSPeter Zijlstra if (!gelf_update_symshndx(symtab_data, shndx_data, idx, &sym->sym, shndx)) { 712ead165faSPeter Zijlstra WARN_ELF("gelf_update_symshndx"); 713ead165faSPeter Zijlstra return -1; 714ead165faSPeter Zijlstra } 715ead165faSPeter Zijlstra 716ead165faSPeter Zijlstra return 0; 7174abff6d4SPeter Zijlstra } 7184abff6d4SPeter Zijlstra 7194abff6d4SPeter Zijlstra static struct symbol * 720*4c91be8eSPeter Zijlstra __elf_create_symbol(struct elf *elf, struct symbol *sym) 7214abff6d4SPeter Zijlstra { 7224abff6d4SPeter Zijlstra struct section *symtab, *symtab_shndx; 723ead165faSPeter Zijlstra Elf32_Word first_non_local, new_idx; 724*4c91be8eSPeter Zijlstra struct symbol *old; 7254abff6d4SPeter Zijlstra 7264abff6d4SPeter Zijlstra symtab = find_section_by_name(elf, ".symtab"); 7274abff6d4SPeter Zijlstra if (symtab) { 7284abff6d4SPeter Zijlstra symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); 7294abff6d4SPeter Zijlstra } else { 7304abff6d4SPeter Zijlstra WARN("no .symtab"); 7314abff6d4SPeter Zijlstra return NULL; 7324abff6d4SPeter Zijlstra } 7334abff6d4SPeter Zijlstra 734*4c91be8eSPeter Zijlstra new_idx = symtab->sh.sh_size / symtab->sh.sh_entsize; 7354abff6d4SPeter Zijlstra 736*4c91be8eSPeter Zijlstra if (GELF_ST_BIND(sym->sym.st_info) != STB_LOCAL) 737*4c91be8eSPeter Zijlstra goto non_local; 738ead165faSPeter Zijlstra 739ead165faSPeter Zijlstra /* 740ead165faSPeter Zijlstra * Move the first global symbol, as per sh_info, into a new, higher 741ead165faSPeter Zijlstra * symbol index. This fees up a spot for a new local symbol. 742ead165faSPeter Zijlstra */ 743ead165faSPeter Zijlstra first_non_local = symtab->sh.sh_info; 744ead165faSPeter Zijlstra old = find_symbol_by_index(elf, first_non_local); 745ead165faSPeter Zijlstra if (old) { 746ead165faSPeter Zijlstra old->idx = new_idx; 747ead165faSPeter Zijlstra 748ead165faSPeter Zijlstra hlist_del(&old->hash); 749ead165faSPeter Zijlstra elf_hash_add(symbol, &old->hash, old->idx); 750ead165faSPeter Zijlstra 751ead165faSPeter Zijlstra elf_dirty_reloc_sym(elf, old); 752ead165faSPeter Zijlstra 753ead165faSPeter Zijlstra if (elf_update_symbol(elf, symtab, symtab_shndx, old)) { 754ead165faSPeter Zijlstra WARN("elf_update_symbol move"); 7554abff6d4SPeter Zijlstra return NULL; 7564abff6d4SPeter Zijlstra } 7574abff6d4SPeter Zijlstra 758ead165faSPeter Zijlstra new_idx = first_non_local; 759ead165faSPeter Zijlstra } 760ead165faSPeter Zijlstra 761*4c91be8eSPeter Zijlstra /* 762*4c91be8eSPeter Zijlstra * Either way, we will add a LOCAL symbol. 763*4c91be8eSPeter Zijlstra */ 764*4c91be8eSPeter Zijlstra symtab->sh.sh_info += 1; 765*4c91be8eSPeter Zijlstra 766*4c91be8eSPeter Zijlstra non_local: 767ead165faSPeter Zijlstra sym->idx = new_idx; 768ead165faSPeter Zijlstra if (elf_update_symbol(elf, symtab, symtab_shndx, sym)) { 769ead165faSPeter Zijlstra WARN("elf_update_symbol"); 7704abff6d4SPeter Zijlstra return NULL; 7714abff6d4SPeter Zijlstra } 7724abff6d4SPeter Zijlstra 773*4c91be8eSPeter Zijlstra return sym; 774*4c91be8eSPeter Zijlstra } 775ead165faSPeter Zijlstra 776*4c91be8eSPeter Zijlstra static struct symbol * 777*4c91be8eSPeter Zijlstra elf_create_section_symbol(struct elf *elf, struct section *sec) 778*4c91be8eSPeter Zijlstra { 779*4c91be8eSPeter Zijlstra struct symbol *sym = calloc(1, sizeof(*sym)); 780*4c91be8eSPeter Zijlstra 781*4c91be8eSPeter Zijlstra if (!sym) { 782*4c91be8eSPeter Zijlstra perror("malloc"); 783*4c91be8eSPeter Zijlstra return NULL; 784*4c91be8eSPeter Zijlstra } 785*4c91be8eSPeter Zijlstra 786*4c91be8eSPeter Zijlstra sym->name = sec->name; 787*4c91be8eSPeter Zijlstra sym->sec = sec; 788*4c91be8eSPeter Zijlstra 789*4c91be8eSPeter Zijlstra // st_name 0 790*4c91be8eSPeter Zijlstra sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION); 791*4c91be8eSPeter Zijlstra // st_other 0 792*4c91be8eSPeter Zijlstra // st_value 0 793*4c91be8eSPeter Zijlstra // st_size 0 794*4c91be8eSPeter Zijlstra 795*4c91be8eSPeter Zijlstra sym = __elf_create_symbol(elf, sym); 796*4c91be8eSPeter Zijlstra if (sym) 7974abff6d4SPeter Zijlstra elf_add_symbol(elf, sym); 7984abff6d4SPeter Zijlstra 7994abff6d4SPeter Zijlstra return sym; 8004abff6d4SPeter Zijlstra } 8014abff6d4SPeter Zijlstra 802ef47cc01SPeter Zijlstra int elf_add_reloc_to_insn(struct elf *elf, struct section *sec, 803ef47cc01SPeter Zijlstra unsigned long offset, unsigned int type, 804ef47cc01SPeter Zijlstra struct section *insn_sec, unsigned long insn_off) 805ef47cc01SPeter Zijlstra { 8064abff6d4SPeter Zijlstra struct symbol *sym = insn_sec->sym; 8074abff6d4SPeter Zijlstra int addend = insn_off; 808ef47cc01SPeter Zijlstra 809ef47cc01SPeter Zijlstra if (!sym) { 8104abff6d4SPeter Zijlstra /* 8114abff6d4SPeter Zijlstra * Due to how weak functions work, we must use section based 8124abff6d4SPeter Zijlstra * relocations. Symbol based relocations would result in the 8134abff6d4SPeter Zijlstra * weak and non-weak function annotations being overlaid on the 8144abff6d4SPeter Zijlstra * non-weak function after linking. 8154abff6d4SPeter Zijlstra */ 8164abff6d4SPeter Zijlstra sym = elf_create_section_symbol(elf, insn_sec); 8174abff6d4SPeter Zijlstra if (!sym) 818ef47cc01SPeter Zijlstra return -1; 819ef47cc01SPeter Zijlstra 8204abff6d4SPeter Zijlstra insn_sec->sym = sym; 821ef47cc01SPeter Zijlstra } 822ef47cc01SPeter Zijlstra 823ef47cc01SPeter Zijlstra return elf_add_reloc(elf, sec, offset, type, sym, addend); 82434f7c96dSPeter Zijlstra } 82534f7c96dSPeter Zijlstra 826fb414783SMatt Helsley static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) 827fb414783SMatt Helsley { 828fb414783SMatt Helsley if (!gelf_getrel(sec->data, i, &reloc->rel)) { 829fb414783SMatt Helsley WARN_ELF("gelf_getrel"); 830fb414783SMatt Helsley return -1; 831fb414783SMatt Helsley } 832fb414783SMatt Helsley reloc->type = GELF_R_TYPE(reloc->rel.r_info); 833fb414783SMatt Helsley reloc->addend = 0; 834fb414783SMatt Helsley reloc->offset = reloc->rel.r_offset; 835fb414783SMatt Helsley *symndx = GELF_R_SYM(reloc->rel.r_info); 836fb414783SMatt Helsley return 0; 837fb414783SMatt Helsley } 838fb414783SMatt Helsley 839fb414783SMatt Helsley static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) 840fb414783SMatt Helsley { 841fb414783SMatt Helsley if (!gelf_getrela(sec->data, i, &reloc->rela)) { 842fb414783SMatt Helsley WARN_ELF("gelf_getrela"); 843fb414783SMatt Helsley return -1; 844fb414783SMatt Helsley } 845fb414783SMatt Helsley reloc->type = GELF_R_TYPE(reloc->rela.r_info); 846fb414783SMatt Helsley reloc->addend = reloc->rela.r_addend; 847fb414783SMatt Helsley reloc->offset = reloc->rela.r_offset; 848fb414783SMatt Helsley *symndx = GELF_R_SYM(reloc->rela.r_info); 849fb414783SMatt Helsley return 0; 850fb414783SMatt Helsley } 851fb414783SMatt Helsley 852f1974222SMatt Helsley static int read_relocs(struct elf *elf) 853442f04c3SJosh Poimboeuf { 854442f04c3SJosh Poimboeuf struct section *sec; 855f1974222SMatt Helsley struct reloc *reloc; 856442f04c3SJosh Poimboeuf int i; 857442f04c3SJosh Poimboeuf unsigned int symndx; 858f1974222SMatt Helsley unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0; 859442f04c3SJosh Poimboeuf 860d33b9035SPeter Zijlstra if (!elf_alloc_hash(reloc, elf->text_size / 16)) 86125cf0d8aSPeter Zijlstra return -1; 86225cf0d8aSPeter Zijlstra 863442f04c3SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 864fb414783SMatt Helsley if ((sec->sh.sh_type != SHT_RELA) && 865fb414783SMatt Helsley (sec->sh.sh_type != SHT_REL)) 866442f04c3SJosh Poimboeuf continue; 867442f04c3SJosh Poimboeuf 8681e968bf5SSami Tolvanen sec->base = find_section_by_index(elf, sec->sh.sh_info); 869442f04c3SJosh Poimboeuf if (!sec->base) { 870f1974222SMatt Helsley WARN("can't find base section for reloc section %s", 871442f04c3SJosh Poimboeuf sec->name); 872442f04c3SJosh Poimboeuf return -1; 873442f04c3SJosh Poimboeuf } 874442f04c3SJosh Poimboeuf 875f1974222SMatt Helsley sec->base->reloc = sec; 876442f04c3SJosh Poimboeuf 877f1974222SMatt Helsley nr_reloc = 0; 878442f04c3SJosh Poimboeuf for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { 879f1974222SMatt Helsley reloc = malloc(sizeof(*reloc)); 880f1974222SMatt Helsley if (!reloc) { 881442f04c3SJosh Poimboeuf perror("malloc"); 882442f04c3SJosh Poimboeuf return -1; 883442f04c3SJosh Poimboeuf } 884f1974222SMatt Helsley memset(reloc, 0, sizeof(*reloc)); 885fb414783SMatt Helsley switch (sec->sh.sh_type) { 886fb414783SMatt Helsley case SHT_REL: 887fb414783SMatt Helsley if (read_rel_reloc(sec, i, reloc, &symndx)) 888442f04c3SJosh Poimboeuf return -1; 889fb414783SMatt Helsley break; 890fb414783SMatt Helsley case SHT_RELA: 891fb414783SMatt Helsley if (read_rela_reloc(sec, i, reloc, &symndx)) 892fb414783SMatt Helsley return -1; 893fb414783SMatt Helsley break; 894fb414783SMatt Helsley default: return -1; 895442f04c3SJosh Poimboeuf } 896442f04c3SJosh Poimboeuf 897f1974222SMatt Helsley reloc->sec = sec; 898d832c005SPeter Zijlstra reloc->idx = i; 899d832c005SPeter Zijlstra reloc->sym = find_symbol_by_index(elf, symndx); 900f1974222SMatt Helsley if (!reloc->sym) { 901f1974222SMatt Helsley WARN("can't find reloc entry symbol %d for %s", 902442f04c3SJosh Poimboeuf symndx, sec->name); 903442f04c3SJosh Poimboeuf return -1; 904442f04c3SJosh Poimboeuf } 905042ba73fSJosh Poimboeuf 9063a647607SPeter Zijlstra list_add_tail(&reloc->list, &sec->reloc_list); 90725cf0d8aSPeter Zijlstra elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); 9083a647607SPeter Zijlstra 909f1974222SMatt Helsley nr_reloc++; 910442f04c3SJosh Poimboeuf } 911f1974222SMatt Helsley max_reloc = max(max_reloc, nr_reloc); 912f1974222SMatt Helsley tot_reloc += nr_reloc; 9131e11f3fdSPeter Zijlstra } 9141e11f3fdSPeter Zijlstra 9152daf7fabSJosh Poimboeuf if (opts.stats) { 916f1974222SMatt Helsley printf("max_reloc: %lu\n", max_reloc); 917f1974222SMatt Helsley printf("tot_reloc: %lu\n", tot_reloc); 91825cf0d8aSPeter Zijlstra printf("reloc_bits: %d\n", elf->reloc_bits); 919442f04c3SJosh Poimboeuf } 920442f04c3SJosh Poimboeuf 921442f04c3SJosh Poimboeuf return 0; 922442f04c3SJosh Poimboeuf } 923442f04c3SJosh Poimboeuf 924bc359ff2SIngo Molnar struct elf *elf_open_read(const char *name, int flags) 925442f04c3SJosh Poimboeuf { 926442f04c3SJosh Poimboeuf struct elf *elf; 927627fce14SJosh Poimboeuf Elf_Cmd cmd; 928442f04c3SJosh Poimboeuf 929442f04c3SJosh Poimboeuf elf_version(EV_CURRENT); 930442f04c3SJosh Poimboeuf 931442f04c3SJosh Poimboeuf elf = malloc(sizeof(*elf)); 932442f04c3SJosh Poimboeuf if (!elf) { 933442f04c3SJosh Poimboeuf perror("malloc"); 934442f04c3SJosh Poimboeuf return NULL; 935442f04c3SJosh Poimboeuf } 93634f7c96dSPeter Zijlstra memset(elf, 0, offsetof(struct elf, sections)); 937442f04c3SJosh Poimboeuf 938442f04c3SJosh Poimboeuf INIT_LIST_HEAD(&elf->sections); 939442f04c3SJosh Poimboeuf 940627fce14SJosh Poimboeuf elf->fd = open(name, flags); 941442f04c3SJosh Poimboeuf if (elf->fd == -1) { 942385d11b1SJosh Poimboeuf fprintf(stderr, "objtool: Can't open '%s': %s\n", 943385d11b1SJosh Poimboeuf name, strerror(errno)); 944442f04c3SJosh Poimboeuf goto err; 945442f04c3SJosh Poimboeuf } 946442f04c3SJosh Poimboeuf 947627fce14SJosh Poimboeuf if ((flags & O_ACCMODE) == O_RDONLY) 948627fce14SJosh Poimboeuf cmd = ELF_C_READ_MMAP; 949627fce14SJosh Poimboeuf else if ((flags & O_ACCMODE) == O_RDWR) 950627fce14SJosh Poimboeuf cmd = ELF_C_RDWR; 951627fce14SJosh Poimboeuf else /* O_WRONLY */ 952627fce14SJosh Poimboeuf cmd = ELF_C_WRITE; 953627fce14SJosh Poimboeuf 954627fce14SJosh Poimboeuf elf->elf = elf_begin(elf->fd, cmd, NULL); 955442f04c3SJosh Poimboeuf if (!elf->elf) { 956baa41469SJosh Poimboeuf WARN_ELF("elf_begin"); 957442f04c3SJosh Poimboeuf goto err; 958442f04c3SJosh Poimboeuf } 959442f04c3SJosh Poimboeuf 960442f04c3SJosh Poimboeuf if (!gelf_getehdr(elf->elf, &elf->ehdr)) { 961baa41469SJosh Poimboeuf WARN_ELF("gelf_getehdr"); 962442f04c3SJosh Poimboeuf goto err; 963442f04c3SJosh Poimboeuf } 964442f04c3SJosh Poimboeuf 965442f04c3SJosh Poimboeuf if (read_sections(elf)) 966442f04c3SJosh Poimboeuf goto err; 967442f04c3SJosh Poimboeuf 968442f04c3SJosh Poimboeuf if (read_symbols(elf)) 969442f04c3SJosh Poimboeuf goto err; 970442f04c3SJosh Poimboeuf 971f1974222SMatt Helsley if (read_relocs(elf)) 972442f04c3SJosh Poimboeuf goto err; 973442f04c3SJosh Poimboeuf 974442f04c3SJosh Poimboeuf return elf; 975442f04c3SJosh Poimboeuf 976442f04c3SJosh Poimboeuf err: 977442f04c3SJosh Poimboeuf elf_close(elf); 978442f04c3SJosh Poimboeuf return NULL; 979442f04c3SJosh Poimboeuf } 980442f04c3SJosh Poimboeuf 981417a4dc9SPeter Zijlstra static int elf_add_string(struct elf *elf, struct section *strtab, char *str) 982417a4dc9SPeter Zijlstra { 983417a4dc9SPeter Zijlstra Elf_Data *data; 984417a4dc9SPeter Zijlstra Elf_Scn *s; 985417a4dc9SPeter Zijlstra int len; 986417a4dc9SPeter Zijlstra 987417a4dc9SPeter Zijlstra if (!strtab) 988417a4dc9SPeter Zijlstra strtab = find_section_by_name(elf, ".strtab"); 989417a4dc9SPeter Zijlstra if (!strtab) { 990417a4dc9SPeter Zijlstra WARN("can't find .strtab section"); 991417a4dc9SPeter Zijlstra return -1; 992417a4dc9SPeter Zijlstra } 993417a4dc9SPeter Zijlstra 994417a4dc9SPeter Zijlstra s = elf_getscn(elf->elf, strtab->idx); 995417a4dc9SPeter Zijlstra if (!s) { 996417a4dc9SPeter Zijlstra WARN_ELF("elf_getscn"); 997417a4dc9SPeter Zijlstra return -1; 998417a4dc9SPeter Zijlstra } 999417a4dc9SPeter Zijlstra 1000417a4dc9SPeter Zijlstra data = elf_newdata(s); 1001417a4dc9SPeter Zijlstra if (!data) { 1002417a4dc9SPeter Zijlstra WARN_ELF("elf_newdata"); 1003417a4dc9SPeter Zijlstra return -1; 1004417a4dc9SPeter Zijlstra } 1005417a4dc9SPeter Zijlstra 1006417a4dc9SPeter Zijlstra data->d_buf = str; 1007417a4dc9SPeter Zijlstra data->d_size = strlen(str) + 1; 1008417a4dc9SPeter Zijlstra data->d_align = 1; 1009417a4dc9SPeter Zijlstra 1010fe255fe6SJoe Lawrence len = strtab->sh.sh_size; 1011fe255fe6SJoe Lawrence strtab->sh.sh_size += data->d_size; 1012417a4dc9SPeter Zijlstra strtab->changed = true; 1013417a4dc9SPeter Zijlstra 1014417a4dc9SPeter Zijlstra return len; 1015417a4dc9SPeter Zijlstra } 1016417a4dc9SPeter Zijlstra 1017627fce14SJosh Poimboeuf struct section *elf_create_section(struct elf *elf, const char *name, 10181e7e4788SJosh Poimboeuf unsigned int sh_flags, size_t entsize, int nr) 1019627fce14SJosh Poimboeuf { 1020627fce14SJosh Poimboeuf struct section *sec, *shstrtab; 1021627fce14SJosh Poimboeuf size_t size = entsize * nr; 10223c3ea503SMichael Forney Elf_Scn *s; 1023627fce14SJosh Poimboeuf 1024627fce14SJosh Poimboeuf sec = malloc(sizeof(*sec)); 1025627fce14SJosh Poimboeuf if (!sec) { 1026627fce14SJosh Poimboeuf perror("malloc"); 1027627fce14SJosh Poimboeuf return NULL; 1028627fce14SJosh Poimboeuf } 1029627fce14SJosh Poimboeuf memset(sec, 0, sizeof(*sec)); 1030627fce14SJosh Poimboeuf 1031627fce14SJosh Poimboeuf INIT_LIST_HEAD(&sec->symbol_list); 1032f1974222SMatt Helsley INIT_LIST_HEAD(&sec->reloc_list); 1033627fce14SJosh Poimboeuf 1034627fce14SJosh Poimboeuf s = elf_newscn(elf->elf); 1035627fce14SJosh Poimboeuf if (!s) { 1036627fce14SJosh Poimboeuf WARN_ELF("elf_newscn"); 1037627fce14SJosh Poimboeuf return NULL; 1038627fce14SJosh Poimboeuf } 1039627fce14SJosh Poimboeuf 1040627fce14SJosh Poimboeuf sec->name = strdup(name); 1041627fce14SJosh Poimboeuf if (!sec->name) { 1042627fce14SJosh Poimboeuf perror("strdup"); 1043627fce14SJosh Poimboeuf return NULL; 1044627fce14SJosh Poimboeuf } 1045627fce14SJosh Poimboeuf 1046627fce14SJosh Poimboeuf sec->idx = elf_ndxscn(s); 1047627fce14SJosh Poimboeuf sec->changed = true; 1048627fce14SJosh Poimboeuf 1049627fce14SJosh Poimboeuf sec->data = elf_newdata(s); 1050627fce14SJosh Poimboeuf if (!sec->data) { 1051627fce14SJosh Poimboeuf WARN_ELF("elf_newdata"); 1052627fce14SJosh Poimboeuf return NULL; 1053627fce14SJosh Poimboeuf } 1054627fce14SJosh Poimboeuf 1055627fce14SJosh Poimboeuf sec->data->d_size = size; 1056627fce14SJosh Poimboeuf sec->data->d_align = 1; 1057627fce14SJosh Poimboeuf 1058627fce14SJosh Poimboeuf if (size) { 1059627fce14SJosh Poimboeuf sec->data->d_buf = malloc(size); 1060627fce14SJosh Poimboeuf if (!sec->data->d_buf) { 1061627fce14SJosh Poimboeuf perror("malloc"); 1062627fce14SJosh Poimboeuf return NULL; 1063627fce14SJosh Poimboeuf } 1064627fce14SJosh Poimboeuf memset(sec->data->d_buf, 0, size); 1065627fce14SJosh Poimboeuf } 1066627fce14SJosh Poimboeuf 1067627fce14SJosh Poimboeuf if (!gelf_getshdr(s, &sec->sh)) { 1068627fce14SJosh Poimboeuf WARN_ELF("gelf_getshdr"); 1069627fce14SJosh Poimboeuf return NULL; 1070627fce14SJosh Poimboeuf } 1071627fce14SJosh Poimboeuf 1072627fce14SJosh Poimboeuf sec->sh.sh_size = size; 1073627fce14SJosh Poimboeuf sec->sh.sh_entsize = entsize; 1074627fce14SJosh Poimboeuf sec->sh.sh_type = SHT_PROGBITS; 1075627fce14SJosh Poimboeuf sec->sh.sh_addralign = 1; 10761e7e4788SJosh Poimboeuf sec->sh.sh_flags = SHF_ALLOC | sh_flags; 1077627fce14SJosh Poimboeuf 10786d77d3b4SSimon Ser /* Add section name to .shstrtab (or .strtab for Clang) */ 1079627fce14SJosh Poimboeuf shstrtab = find_section_by_name(elf, ".shstrtab"); 10806d77d3b4SSimon Ser if (!shstrtab) 10816d77d3b4SSimon Ser shstrtab = find_section_by_name(elf, ".strtab"); 1082627fce14SJosh Poimboeuf if (!shstrtab) { 10836d77d3b4SSimon Ser WARN("can't find .shstrtab or .strtab section"); 1084627fce14SJosh Poimboeuf return NULL; 1085627fce14SJosh Poimboeuf } 1086417a4dc9SPeter Zijlstra sec->sh.sh_name = elf_add_string(elf, shstrtab, sec->name); 1087417a4dc9SPeter Zijlstra if (sec->sh.sh_name == -1) 1088627fce14SJosh Poimboeuf return NULL; 1089627fce14SJosh Poimboeuf 109053038996SPeter Zijlstra list_add_tail(&sec->list, &elf->sections); 109125cf0d8aSPeter Zijlstra elf_hash_add(section, &sec->hash, sec->idx); 109225cf0d8aSPeter Zijlstra elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); 109353038996SPeter Zijlstra 10942b10be23SPeter Zijlstra elf->changed = true; 10952b10be23SPeter Zijlstra 1096627fce14SJosh Poimboeuf return sec; 1097627fce14SJosh Poimboeuf } 1098627fce14SJosh Poimboeuf 1099fb414783SMatt Helsley static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base) 1100fb414783SMatt Helsley { 1101fb414783SMatt Helsley char *relocname; 1102fb414783SMatt Helsley struct section *sec; 1103fb414783SMatt Helsley 1104fb414783SMatt Helsley relocname = malloc(strlen(base->name) + strlen(".rel") + 1); 1105fb414783SMatt Helsley if (!relocname) { 1106fb414783SMatt Helsley perror("malloc"); 1107fb414783SMatt Helsley return NULL; 1108fb414783SMatt Helsley } 1109fb414783SMatt Helsley strcpy(relocname, ".rel"); 1110fb414783SMatt Helsley strcat(relocname, base->name); 1111fb414783SMatt Helsley 11121e7e4788SJosh Poimboeuf sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rel), 0); 1113fb414783SMatt Helsley free(relocname); 1114fb414783SMatt Helsley if (!sec) 1115fb414783SMatt Helsley return NULL; 1116fb414783SMatt Helsley 1117fb414783SMatt Helsley base->reloc = sec; 1118fb414783SMatt Helsley sec->base = base; 1119fb414783SMatt Helsley 1120fb414783SMatt Helsley sec->sh.sh_type = SHT_REL; 1121fb414783SMatt Helsley sec->sh.sh_addralign = 8; 1122fb414783SMatt Helsley sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; 1123fb414783SMatt Helsley sec->sh.sh_info = base->idx; 1124fb414783SMatt Helsley sec->sh.sh_flags = SHF_INFO_LINK; 1125fb414783SMatt Helsley 1126fb414783SMatt Helsley return sec; 1127fb414783SMatt Helsley } 1128fb414783SMatt Helsley 1129fb414783SMatt Helsley static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base) 1130627fce14SJosh Poimboeuf { 1131f1974222SMatt Helsley char *relocname; 1132627fce14SJosh Poimboeuf struct section *sec; 1133627fce14SJosh Poimboeuf 1134f1974222SMatt Helsley relocname = malloc(strlen(base->name) + strlen(".rela") + 1); 1135f1974222SMatt Helsley if (!relocname) { 1136627fce14SJosh Poimboeuf perror("malloc"); 1137627fce14SJosh Poimboeuf return NULL; 1138627fce14SJosh Poimboeuf } 1139f1974222SMatt Helsley strcpy(relocname, ".rela"); 1140f1974222SMatt Helsley strcat(relocname, base->name); 1141627fce14SJosh Poimboeuf 11421e7e4788SJosh Poimboeuf sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rela), 0); 1143f1974222SMatt Helsley free(relocname); 1144627fce14SJosh Poimboeuf if (!sec) 1145627fce14SJosh Poimboeuf return NULL; 1146627fce14SJosh Poimboeuf 1147f1974222SMatt Helsley base->reloc = sec; 1148627fce14SJosh Poimboeuf sec->base = base; 1149627fce14SJosh Poimboeuf 1150627fce14SJosh Poimboeuf sec->sh.sh_type = SHT_RELA; 1151627fce14SJosh Poimboeuf sec->sh.sh_addralign = 8; 1152627fce14SJosh Poimboeuf sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; 1153627fce14SJosh Poimboeuf sec->sh.sh_info = base->idx; 1154627fce14SJosh Poimboeuf sec->sh.sh_flags = SHF_INFO_LINK; 1155627fce14SJosh Poimboeuf 1156627fce14SJosh Poimboeuf return sec; 1157627fce14SJosh Poimboeuf } 1158627fce14SJosh Poimboeuf 1159d0c5c4ccSPeter Zijlstra static struct section *elf_create_reloc_section(struct elf *elf, 1160fb414783SMatt Helsley struct section *base, 1161fb414783SMatt Helsley int reltype) 1162fb414783SMatt Helsley { 1163fb414783SMatt Helsley switch (reltype) { 1164fb414783SMatt Helsley case SHT_REL: return elf_create_rel_reloc_section(elf, base); 1165fb414783SMatt Helsley case SHT_RELA: return elf_create_rela_reloc_section(elf, base); 1166fb414783SMatt Helsley default: return NULL; 1167fb414783SMatt Helsley } 1168fb414783SMatt Helsley } 1169fb414783SMatt Helsley 117086e1e054SMichael Forney static int elf_rebuild_rel_reloc_section(struct section *sec) 1171627fce14SJosh Poimboeuf { 1172f1974222SMatt Helsley struct reloc *reloc; 117386e1e054SMichael Forney int idx = 0; 1174a1a664ecSMartin Schwidefsky void *buf; 1175fb414783SMatt Helsley 1176fb414783SMatt Helsley /* Allocate a buffer for relocations */ 117786e1e054SMichael Forney buf = malloc(sec->sh.sh_size); 1178a1a664ecSMartin Schwidefsky if (!buf) { 1179fb414783SMatt Helsley perror("malloc"); 1180fb414783SMatt Helsley return -1; 1181fb414783SMatt Helsley } 1182fb414783SMatt Helsley 1183a1a664ecSMartin Schwidefsky sec->data->d_buf = buf; 118486e1e054SMichael Forney sec->data->d_size = sec->sh.sh_size; 1185a1a664ecSMartin Schwidefsky sec->data->d_type = ELF_T_REL; 1186fb414783SMatt Helsley 1187fb414783SMatt Helsley idx = 0; 1188fb414783SMatt Helsley list_for_each_entry(reloc, &sec->reloc_list, list) { 1189a1a664ecSMartin Schwidefsky reloc->rel.r_offset = reloc->offset; 1190a1a664ecSMartin Schwidefsky reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 1191b46179d6SMichael Forney if (!gelf_update_rel(sec->data, idx, &reloc->rel)) { 1192b46179d6SMichael Forney WARN_ELF("gelf_update_rel"); 1193b46179d6SMichael Forney return -1; 1194b46179d6SMichael Forney } 1195fb414783SMatt Helsley idx++; 1196fb414783SMatt Helsley } 1197fb414783SMatt Helsley 1198fb414783SMatt Helsley return 0; 1199fb414783SMatt Helsley } 1200fb414783SMatt Helsley 120186e1e054SMichael Forney static int elf_rebuild_rela_reloc_section(struct section *sec) 1202fb414783SMatt Helsley { 1203fb414783SMatt Helsley struct reloc *reloc; 120486e1e054SMichael Forney int idx = 0; 1205a1a664ecSMartin Schwidefsky void *buf; 1206627fce14SJosh Poimboeuf 1207fb414783SMatt Helsley /* Allocate a buffer for relocations with addends */ 120886e1e054SMichael Forney buf = malloc(sec->sh.sh_size); 1209a1a664ecSMartin Schwidefsky if (!buf) { 1210627fce14SJosh Poimboeuf perror("malloc"); 1211627fce14SJosh Poimboeuf return -1; 1212627fce14SJosh Poimboeuf } 1213627fce14SJosh Poimboeuf 1214a1a664ecSMartin Schwidefsky sec->data->d_buf = buf; 121586e1e054SMichael Forney sec->data->d_size = sec->sh.sh_size; 1216a1a664ecSMartin Schwidefsky sec->data->d_type = ELF_T_RELA; 1217627fce14SJosh Poimboeuf 1218627fce14SJosh Poimboeuf idx = 0; 1219f1974222SMatt Helsley list_for_each_entry(reloc, &sec->reloc_list, list) { 1220a1a664ecSMartin Schwidefsky reloc->rela.r_offset = reloc->offset; 1221a1a664ecSMartin Schwidefsky reloc->rela.r_addend = reloc->addend; 1222a1a664ecSMartin Schwidefsky reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 1223b46179d6SMichael Forney if (!gelf_update_rela(sec->data, idx, &reloc->rela)) { 1224b46179d6SMichael Forney WARN_ELF("gelf_update_rela"); 1225b46179d6SMichael Forney return -1; 1226b46179d6SMichael Forney } 1227627fce14SJosh Poimboeuf idx++; 1228627fce14SJosh Poimboeuf } 1229627fce14SJosh Poimboeuf 1230627fce14SJosh Poimboeuf return 0; 1231627fce14SJosh Poimboeuf } 1232627fce14SJosh Poimboeuf 12333a647607SPeter Zijlstra static int elf_rebuild_reloc_section(struct elf *elf, struct section *sec) 1234fb414783SMatt Helsley { 1235fb414783SMatt Helsley switch (sec->sh.sh_type) { 123686e1e054SMichael Forney case SHT_REL: return elf_rebuild_rel_reloc_section(sec); 123786e1e054SMichael Forney case SHT_RELA: return elf_rebuild_rela_reloc_section(sec); 1238fb414783SMatt Helsley default: return -1; 1239fb414783SMatt Helsley } 1240fb414783SMatt Helsley } 1241fb414783SMatt Helsley 1242fdabdd0bSPeter Zijlstra int elf_write_insn(struct elf *elf, struct section *sec, 1243fdabdd0bSPeter Zijlstra unsigned long offset, unsigned int len, 1244fdabdd0bSPeter Zijlstra const char *insn) 1245fdabdd0bSPeter Zijlstra { 1246fdabdd0bSPeter Zijlstra Elf_Data *data = sec->data; 1247fdabdd0bSPeter Zijlstra 1248fdabdd0bSPeter Zijlstra if (data->d_type != ELF_T_BYTE || data->d_off) { 1249fdabdd0bSPeter Zijlstra WARN("write to unexpected data for section: %s", sec->name); 1250fdabdd0bSPeter Zijlstra return -1; 1251fdabdd0bSPeter Zijlstra } 1252fdabdd0bSPeter Zijlstra 1253fdabdd0bSPeter Zijlstra memcpy(data->d_buf + offset, insn, len); 1254fdabdd0bSPeter Zijlstra elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY); 1255fdabdd0bSPeter Zijlstra 1256fdabdd0bSPeter Zijlstra elf->changed = true; 1257fdabdd0bSPeter Zijlstra 1258fdabdd0bSPeter Zijlstra return 0; 1259fdabdd0bSPeter Zijlstra } 1260fdabdd0bSPeter Zijlstra 1261d832c005SPeter Zijlstra int elf_write_reloc(struct elf *elf, struct reloc *reloc) 1262fdabdd0bSPeter Zijlstra { 1263d832c005SPeter Zijlstra struct section *sec = reloc->sec; 1264fdabdd0bSPeter Zijlstra 1265d832c005SPeter Zijlstra if (sec->sh.sh_type == SHT_REL) { 1266d832c005SPeter Zijlstra reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 1267d832c005SPeter Zijlstra reloc->rel.r_offset = reloc->offset; 1268fdabdd0bSPeter Zijlstra 1269d832c005SPeter Zijlstra if (!gelf_update_rel(sec->data, reloc->idx, &reloc->rel)) { 1270d832c005SPeter Zijlstra WARN_ELF("gelf_update_rel"); 1271d832c005SPeter Zijlstra return -1; 1272d832c005SPeter Zijlstra } 1273d832c005SPeter Zijlstra } else { 1274d832c005SPeter Zijlstra reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 1275d832c005SPeter Zijlstra reloc->rela.r_addend = reloc->addend; 1276d832c005SPeter Zijlstra reloc->rela.r_offset = reloc->offset; 1277d832c005SPeter Zijlstra 1278d832c005SPeter Zijlstra if (!gelf_update_rela(sec->data, reloc->idx, &reloc->rela)) { 1279fdabdd0bSPeter Zijlstra WARN_ELF("gelf_update_rela"); 1280fdabdd0bSPeter Zijlstra return -1; 1281fdabdd0bSPeter Zijlstra } 1282d832c005SPeter Zijlstra } 1283fdabdd0bSPeter Zijlstra 1284fdabdd0bSPeter Zijlstra elf->changed = true; 1285fdabdd0bSPeter Zijlstra 1286fdabdd0bSPeter Zijlstra return 0; 1287fdabdd0bSPeter Zijlstra } 1288fdabdd0bSPeter Zijlstra 12892b10be23SPeter Zijlstra int elf_write(struct elf *elf) 1290627fce14SJosh Poimboeuf { 1291627fce14SJosh Poimboeuf struct section *sec; 1292627fce14SJosh Poimboeuf Elf_Scn *s; 1293627fce14SJosh Poimboeuf 12942daf7fabSJosh Poimboeuf if (opts.dryrun) 1295f2d3a250SPeter Zijlstra return 0; 1296f2d3a250SPeter Zijlstra 12973a647607SPeter Zijlstra /* Update changed relocation sections and section headers: */ 1298627fce14SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 1299627fce14SJosh Poimboeuf if (sec->changed) { 1300627fce14SJosh Poimboeuf s = elf_getscn(elf->elf, sec->idx); 1301627fce14SJosh Poimboeuf if (!s) { 1302627fce14SJosh Poimboeuf WARN_ELF("elf_getscn"); 1303627fce14SJosh Poimboeuf return -1; 1304627fce14SJosh Poimboeuf } 1305627fce14SJosh Poimboeuf if (!gelf_update_shdr(s, &sec->sh)) { 1306627fce14SJosh Poimboeuf WARN_ELF("gelf_update_shdr"); 1307627fce14SJosh Poimboeuf return -1; 1308627fce14SJosh Poimboeuf } 13092b10be23SPeter Zijlstra 131086e1e054SMichael Forney if (sec->base && 131186e1e054SMichael Forney elf_rebuild_reloc_section(elf, sec)) { 131286e1e054SMichael Forney WARN("elf_rebuild_reloc_section"); 131386e1e054SMichael Forney return -1; 131486e1e054SMichael Forney } 131586e1e054SMichael Forney 13162b10be23SPeter Zijlstra sec->changed = false; 13173a647607SPeter Zijlstra elf->changed = true; 1318627fce14SJosh Poimboeuf } 1319627fce14SJosh Poimboeuf } 1320627fce14SJosh Poimboeuf 132197dab2aeSJosh Poimboeuf /* Make sure the new section header entries get updated properly. */ 132297dab2aeSJosh Poimboeuf elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY); 132397dab2aeSJosh Poimboeuf 132497dab2aeSJosh Poimboeuf /* Write all changes to the file. */ 1325627fce14SJosh Poimboeuf if (elf_update(elf->elf, ELF_C_WRITE) < 0) { 1326627fce14SJosh Poimboeuf WARN_ELF("elf_update"); 1327627fce14SJosh Poimboeuf return -1; 1328627fce14SJosh Poimboeuf } 1329627fce14SJosh Poimboeuf 13302b10be23SPeter Zijlstra elf->changed = false; 13312b10be23SPeter Zijlstra 1332627fce14SJosh Poimboeuf return 0; 1333627fce14SJosh Poimboeuf } 1334627fce14SJosh Poimboeuf 1335442f04c3SJosh Poimboeuf void elf_close(struct elf *elf) 1336442f04c3SJosh Poimboeuf { 1337442f04c3SJosh Poimboeuf struct section *sec, *tmpsec; 1338442f04c3SJosh Poimboeuf struct symbol *sym, *tmpsym; 1339f1974222SMatt Helsley struct reloc *reloc, *tmpreloc; 1340442f04c3SJosh Poimboeuf 1341baa41469SJosh Poimboeuf if (elf->elf) 1342baa41469SJosh Poimboeuf elf_end(elf->elf); 1343baa41469SJosh Poimboeuf 1344baa41469SJosh Poimboeuf if (elf->fd > 0) 1345baa41469SJosh Poimboeuf close(elf->fd); 1346baa41469SJosh Poimboeuf 1347442f04c3SJosh Poimboeuf list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { 1348a196e171SJosh Poimboeuf list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { 1349442f04c3SJosh Poimboeuf list_del(&sym->list); 1350042ba73fSJosh Poimboeuf hash_del(&sym->hash); 1351442f04c3SJosh Poimboeuf free(sym); 1352442f04c3SJosh Poimboeuf } 1353f1974222SMatt Helsley list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) { 1354f1974222SMatt Helsley list_del(&reloc->list); 1355f1974222SMatt Helsley hash_del(&reloc->hash); 1356f1974222SMatt Helsley free(reloc); 1357442f04c3SJosh Poimboeuf } 1358442f04c3SJosh Poimboeuf list_del(&sec->list); 1359442f04c3SJosh Poimboeuf free(sec); 1360442f04c3SJosh Poimboeuf } 1361baa41469SJosh Poimboeuf 1362442f04c3SJosh Poimboeuf free(elf); 1363442f04c3SJosh Poimboeuf } 1364