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> 197786032eSVasily Gorbik #include <objtool/builtin.h> 20442f04c3SJosh Poimboeuf 217786032eSVasily Gorbik #include <objtool/elf.h> 227786032eSVasily Gorbik #include <objtool/warn.h> 23442f04c3SJosh Poimboeuf 2422566c16SArtem Savkov #define MAX_NAME_LEN 128 2522566c16SArtem Savkov 26ae358196SPeter Zijlstra static inline u32 str_hash(const char *str) 27ae358196SPeter Zijlstra { 28ae358196SPeter Zijlstra return jhash(str, strlen(str), 0); 29ae358196SPeter Zijlstra } 30ae358196SPeter Zijlstra 3125cf0d8aSPeter Zijlstra #define __elf_table(name) (elf->name##_hash) 3225cf0d8aSPeter Zijlstra #define __elf_bits(name) (elf->name##_bits) 3334f7c96dSPeter Zijlstra 3425cf0d8aSPeter Zijlstra #define elf_hash_add(name, node, key) \ 3525cf0d8aSPeter Zijlstra hlist_add_head(node, &__elf_table(name)[hash_min(key, __elf_bits(name))]) 3634f7c96dSPeter Zijlstra 3734f7c96dSPeter Zijlstra #define elf_hash_for_each_possible(name, obj, member, key) \ 3825cf0d8aSPeter Zijlstra hlist_for_each_entry(obj, &__elf_table(name)[hash_min(key, __elf_bits(name))], member) 3925cf0d8aSPeter Zijlstra 4025cf0d8aSPeter Zijlstra #define elf_alloc_hash(name, size) \ 4125cf0d8aSPeter Zijlstra ({ \ 4225cf0d8aSPeter Zijlstra __elf_bits(name) = max(10, ilog2(size)); \ 4325cf0d8aSPeter Zijlstra __elf_table(name) = mmap(NULL, sizeof(struct hlist_head) << __elf_bits(name), \ 4425cf0d8aSPeter Zijlstra PROT_READ|PROT_WRITE, \ 4525cf0d8aSPeter Zijlstra MAP_PRIVATE|MAP_ANON, -1, 0); \ 4625cf0d8aSPeter Zijlstra if (__elf_table(name) == (void *)-1L) { \ 4725cf0d8aSPeter Zijlstra WARN("mmap fail " #name); \ 4825cf0d8aSPeter Zijlstra __elf_table(name) = NULL; \ 4925cf0d8aSPeter Zijlstra } \ 5025cf0d8aSPeter Zijlstra __elf_table(name); \ 5125cf0d8aSPeter Zijlstra }) 5234f7c96dSPeter Zijlstra 532d24dd57SPeter Zijlstra static bool symbol_to_offset(struct rb_node *a, const struct rb_node *b) 542a362eccSPeter Zijlstra { 552a362eccSPeter Zijlstra struct symbol *sa = rb_entry(a, struct symbol, node); 562a362eccSPeter Zijlstra struct symbol *sb = rb_entry(b, struct symbol, node); 572a362eccSPeter Zijlstra 582a362eccSPeter Zijlstra if (sa->offset < sb->offset) 592d24dd57SPeter Zijlstra return true; 602a362eccSPeter Zijlstra if (sa->offset > sb->offset) 612d24dd57SPeter Zijlstra return false; 622a362eccSPeter Zijlstra 632a362eccSPeter Zijlstra if (sa->len < sb->len) 642d24dd57SPeter Zijlstra return true; 652a362eccSPeter Zijlstra if (sa->len > sb->len) 662d24dd57SPeter Zijlstra return false; 672a362eccSPeter Zijlstra 682a362eccSPeter Zijlstra sa->alias = sb; 692a362eccSPeter Zijlstra 702d24dd57SPeter Zijlstra return false; 712a362eccSPeter Zijlstra } 722a362eccSPeter Zijlstra 732a362eccSPeter Zijlstra static int symbol_by_offset(const void *key, const struct rb_node *node) 742a362eccSPeter Zijlstra { 752a362eccSPeter Zijlstra const struct symbol *s = rb_entry(node, struct symbol, node); 762a362eccSPeter Zijlstra const unsigned long *o = key; 772a362eccSPeter Zijlstra 782a362eccSPeter Zijlstra if (*o < s->offset) 792a362eccSPeter Zijlstra return -1; 805377cae9SJulien Thierry if (*o >= s->offset + s->len) 812a362eccSPeter Zijlstra return 1; 822a362eccSPeter Zijlstra 832a362eccSPeter Zijlstra return 0; 842a362eccSPeter Zijlstra } 852a362eccSPeter Zijlstra 86894e48caSIngo Molnar struct section *find_section_by_name(const struct elf *elf, const char *name) 87442f04c3SJosh Poimboeuf { 88442f04c3SJosh Poimboeuf struct section *sec; 89442f04c3SJosh Poimboeuf 9025cf0d8aSPeter Zijlstra elf_hash_for_each_possible(section_name, sec, name_hash, str_hash(name)) { 91442f04c3SJosh Poimboeuf if (!strcmp(sec->name, name)) 92442f04c3SJosh Poimboeuf return sec; 9325cf0d8aSPeter Zijlstra } 94442f04c3SJosh Poimboeuf 95442f04c3SJosh Poimboeuf return NULL; 96442f04c3SJosh Poimboeuf } 97442f04c3SJosh Poimboeuf 98442f04c3SJosh Poimboeuf static struct section *find_section_by_index(struct elf *elf, 99442f04c3SJosh Poimboeuf unsigned int idx) 100442f04c3SJosh Poimboeuf { 101442f04c3SJosh Poimboeuf struct section *sec; 102442f04c3SJosh Poimboeuf 10325cf0d8aSPeter Zijlstra elf_hash_for_each_possible(section, sec, hash, idx) { 104442f04c3SJosh Poimboeuf if (sec->idx == idx) 105442f04c3SJosh Poimboeuf return sec; 10625cf0d8aSPeter Zijlstra } 107442f04c3SJosh Poimboeuf 108442f04c3SJosh Poimboeuf return NULL; 109442f04c3SJosh Poimboeuf } 110442f04c3SJosh Poimboeuf 111442f04c3SJosh Poimboeuf static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) 112442f04c3SJosh Poimboeuf { 113442f04c3SJosh Poimboeuf struct symbol *sym; 114442f04c3SJosh Poimboeuf 11525cf0d8aSPeter Zijlstra elf_hash_for_each_possible(symbol, sym, hash, idx) { 116442f04c3SJosh Poimboeuf if (sym->idx == idx) 117442f04c3SJosh Poimboeuf return sym; 11825cf0d8aSPeter Zijlstra } 119442f04c3SJosh Poimboeuf 120442f04c3SJosh Poimboeuf return NULL; 121442f04c3SJosh Poimboeuf } 122442f04c3SJosh Poimboeuf 123442f04c3SJosh Poimboeuf struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) 124442f04c3SJosh Poimboeuf { 1252a362eccSPeter Zijlstra struct rb_node *node; 126442f04c3SJosh Poimboeuf 1272d24dd57SPeter Zijlstra rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { 1282a362eccSPeter Zijlstra struct symbol *s = rb_entry(node, struct symbol, node); 1292a362eccSPeter Zijlstra 1302a362eccSPeter Zijlstra if (s->offset == offset && s->type != STT_SECTION) 1312a362eccSPeter Zijlstra return s; 1322a362eccSPeter Zijlstra } 1337acfe531SJosh Poimboeuf 1347acfe531SJosh Poimboeuf return NULL; 1357acfe531SJosh Poimboeuf } 1367acfe531SJosh Poimboeuf 1377acfe531SJosh Poimboeuf struct symbol *find_func_by_offset(struct section *sec, unsigned long offset) 1387acfe531SJosh Poimboeuf { 1392a362eccSPeter Zijlstra struct rb_node *node; 1407acfe531SJosh Poimboeuf 1412d24dd57SPeter Zijlstra rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { 1422a362eccSPeter Zijlstra struct symbol *s = rb_entry(node, struct symbol, node); 1432a362eccSPeter Zijlstra 1442a362eccSPeter Zijlstra if (s->offset == offset && s->type == STT_FUNC) 1452a362eccSPeter Zijlstra return s; 1462a362eccSPeter Zijlstra } 1472a362eccSPeter Zijlstra 1482a362eccSPeter Zijlstra return NULL; 1492a362eccSPeter Zijlstra } 1502a362eccSPeter Zijlstra 151b490f453SMiroslav Benes struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset) 1522a362eccSPeter Zijlstra { 1532a362eccSPeter Zijlstra struct rb_node *node; 1542a362eccSPeter Zijlstra 1552d24dd57SPeter Zijlstra rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { 1562a362eccSPeter Zijlstra struct symbol *s = rb_entry(node, struct symbol, node); 1572a362eccSPeter Zijlstra 1582a362eccSPeter Zijlstra if (s->type != STT_SECTION) 1592a362eccSPeter Zijlstra return s; 1602a362eccSPeter Zijlstra } 1612a362eccSPeter Zijlstra 1622a362eccSPeter Zijlstra return NULL; 1632a362eccSPeter Zijlstra } 1642a362eccSPeter Zijlstra 16553d20720SPeter Zijlstra struct symbol *find_func_containing(struct section *sec, unsigned long offset) 1662a362eccSPeter Zijlstra { 1672a362eccSPeter Zijlstra struct rb_node *node; 1682a362eccSPeter Zijlstra 1692d24dd57SPeter Zijlstra rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { 1702a362eccSPeter Zijlstra struct symbol *s = rb_entry(node, struct symbol, node); 1712a362eccSPeter Zijlstra 1722a362eccSPeter Zijlstra if (s->type == STT_FUNC) 1732a362eccSPeter Zijlstra return s; 1742a362eccSPeter Zijlstra } 175442f04c3SJosh Poimboeuf 176442f04c3SJosh Poimboeuf return NULL; 177442f04c3SJosh Poimboeuf } 178442f04c3SJosh Poimboeuf 179894e48caSIngo Molnar struct symbol *find_symbol_by_name(const struct elf *elf, const char *name) 18013810435SJosh Poimboeuf { 18113810435SJosh Poimboeuf struct symbol *sym; 18213810435SJosh Poimboeuf 18325cf0d8aSPeter Zijlstra elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) { 18413810435SJosh Poimboeuf if (!strcmp(sym->name, name)) 18513810435SJosh Poimboeuf return sym; 18625cf0d8aSPeter Zijlstra } 18713810435SJosh Poimboeuf 18813810435SJosh Poimboeuf return NULL; 18913810435SJosh Poimboeuf } 19013810435SJosh Poimboeuf 191f1974222SMatt Helsley struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, 1928b5fa6bcSPeter Zijlstra unsigned long offset, unsigned int len) 193442f04c3SJosh Poimboeuf { 194f1974222SMatt Helsley struct reloc *reloc, *r = NULL; 195042ba73fSJosh Poimboeuf unsigned long o; 196442f04c3SJosh Poimboeuf 197f1974222SMatt Helsley if (!sec->reloc) 198442f04c3SJosh Poimboeuf return NULL; 199442f04c3SJosh Poimboeuf 200f1974222SMatt Helsley sec = sec->reloc; 2018b5fa6bcSPeter Zijlstra 20274b873e4SPeter Zijlstra for_offset_range(o, offset, offset + len) { 20325cf0d8aSPeter Zijlstra elf_hash_for_each_possible(reloc, reloc, hash, 2048b5fa6bcSPeter Zijlstra sec_offset_hash(sec, o)) { 205f1974222SMatt Helsley if (reloc->sec != sec) 20674b873e4SPeter Zijlstra continue; 20774b873e4SPeter Zijlstra 208f1974222SMatt Helsley if (reloc->offset >= offset && reloc->offset < offset + len) { 209f1974222SMatt Helsley if (!r || reloc->offset < r->offset) 210f1974222SMatt Helsley r = reloc; 2118b5fa6bcSPeter Zijlstra } 2128b5fa6bcSPeter Zijlstra } 21374b873e4SPeter Zijlstra if (r) 21474b873e4SPeter Zijlstra return r; 21574b873e4SPeter Zijlstra } 216442f04c3SJosh Poimboeuf 217442f04c3SJosh Poimboeuf return NULL; 218442f04c3SJosh Poimboeuf } 219442f04c3SJosh Poimboeuf 220f1974222SMatt Helsley struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset) 221442f04c3SJosh Poimboeuf { 222f1974222SMatt Helsley return find_reloc_by_dest_range(elf, sec, offset, 1); 223442f04c3SJosh Poimboeuf } 224442f04c3SJosh Poimboeuf 225442f04c3SJosh Poimboeuf static int read_sections(struct elf *elf) 226442f04c3SJosh Poimboeuf { 227442f04c3SJosh Poimboeuf Elf_Scn *s = NULL; 228442f04c3SJosh Poimboeuf struct section *sec; 229442f04c3SJosh Poimboeuf size_t shstrndx, sections_nr; 230442f04c3SJosh Poimboeuf int i; 231442f04c3SJosh Poimboeuf 232442f04c3SJosh Poimboeuf if (elf_getshdrnum(elf->elf, §ions_nr)) { 233baa41469SJosh Poimboeuf WARN_ELF("elf_getshdrnum"); 234442f04c3SJosh Poimboeuf return -1; 235442f04c3SJosh Poimboeuf } 236442f04c3SJosh Poimboeuf 237442f04c3SJosh Poimboeuf if (elf_getshdrstrndx(elf->elf, &shstrndx)) { 238baa41469SJosh Poimboeuf WARN_ELF("elf_getshdrstrndx"); 239442f04c3SJosh Poimboeuf return -1; 240442f04c3SJosh Poimboeuf } 241442f04c3SJosh Poimboeuf 24225cf0d8aSPeter Zijlstra if (!elf_alloc_hash(section, sections_nr) || 24325cf0d8aSPeter Zijlstra !elf_alloc_hash(section_name, sections_nr)) 24425cf0d8aSPeter Zijlstra return -1; 24525cf0d8aSPeter Zijlstra 246442f04c3SJosh Poimboeuf for (i = 0; i < sections_nr; i++) { 247442f04c3SJosh Poimboeuf sec = malloc(sizeof(*sec)); 248442f04c3SJosh Poimboeuf if (!sec) { 249442f04c3SJosh Poimboeuf perror("malloc"); 250442f04c3SJosh Poimboeuf return -1; 251442f04c3SJosh Poimboeuf } 252442f04c3SJosh Poimboeuf memset(sec, 0, sizeof(*sec)); 253442f04c3SJosh Poimboeuf 254a196e171SJosh Poimboeuf INIT_LIST_HEAD(&sec->symbol_list); 255f1974222SMatt Helsley INIT_LIST_HEAD(&sec->reloc_list); 256442f04c3SJosh Poimboeuf 257442f04c3SJosh Poimboeuf s = elf_getscn(elf->elf, i); 258442f04c3SJosh Poimboeuf if (!s) { 259baa41469SJosh Poimboeuf WARN_ELF("elf_getscn"); 260442f04c3SJosh Poimboeuf return -1; 261442f04c3SJosh Poimboeuf } 262442f04c3SJosh Poimboeuf 263442f04c3SJosh Poimboeuf sec->idx = elf_ndxscn(s); 264442f04c3SJosh Poimboeuf 265442f04c3SJosh Poimboeuf if (!gelf_getshdr(s, &sec->sh)) { 266baa41469SJosh Poimboeuf WARN_ELF("gelf_getshdr"); 267442f04c3SJosh Poimboeuf return -1; 268442f04c3SJosh Poimboeuf } 269442f04c3SJosh Poimboeuf 270442f04c3SJosh Poimboeuf sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name); 271442f04c3SJosh Poimboeuf if (!sec->name) { 272baa41469SJosh Poimboeuf WARN_ELF("elf_strptr"); 273442f04c3SJosh Poimboeuf return -1; 274442f04c3SJosh Poimboeuf } 275442f04c3SJosh Poimboeuf 276df968c93SPetr Vandrovec if (sec->sh.sh_size != 0) { 277baa41469SJosh Poimboeuf sec->data = elf_getdata(s, NULL); 278baa41469SJosh Poimboeuf if (!sec->data) { 279baa41469SJosh Poimboeuf WARN_ELF("elf_getdata"); 280442f04c3SJosh Poimboeuf return -1; 281442f04c3SJosh Poimboeuf } 282baa41469SJosh Poimboeuf if (sec->data->d_off != 0 || 283baa41469SJosh Poimboeuf sec->data->d_size != sec->sh.sh_size) { 284df968c93SPetr Vandrovec WARN("unexpected data attributes for %s", 285df968c93SPetr Vandrovec sec->name); 286442f04c3SJosh Poimboeuf return -1; 287442f04c3SJosh Poimboeuf } 288df968c93SPetr Vandrovec } 289df968c93SPetr Vandrovec sec->len = sec->sh.sh_size; 29053038996SPeter Zijlstra 291*d33b9035SPeter Zijlstra if (sec->sh.sh_flags & SHF_EXECINSTR) 292*d33b9035SPeter Zijlstra elf->text_size += sec->len; 293*d33b9035SPeter Zijlstra 29453038996SPeter Zijlstra list_add_tail(&sec->list, &elf->sections); 29525cf0d8aSPeter Zijlstra elf_hash_add(section, &sec->hash, sec->idx); 29625cf0d8aSPeter Zijlstra elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); 297442f04c3SJosh Poimboeuf } 298442f04c3SJosh Poimboeuf 29925cf0d8aSPeter Zijlstra if (stats) { 3001e11f3fdSPeter Zijlstra printf("nr_sections: %lu\n", (unsigned long)sections_nr); 30125cf0d8aSPeter Zijlstra printf("section_bits: %d\n", elf->section_bits); 30225cf0d8aSPeter Zijlstra } 3031e11f3fdSPeter Zijlstra 304442f04c3SJosh Poimboeuf /* sanity check, one more call to elf_nextscn() should return NULL */ 305442f04c3SJosh Poimboeuf if (elf_nextscn(elf->elf, s)) { 306442f04c3SJosh Poimboeuf WARN("section entry mismatch"); 307442f04c3SJosh Poimboeuf return -1; 308442f04c3SJosh Poimboeuf } 309442f04c3SJosh Poimboeuf 310442f04c3SJosh Poimboeuf return 0; 311442f04c3SJosh Poimboeuf } 312442f04c3SJosh Poimboeuf 3139a7827b7SPeter Zijlstra static void elf_add_symbol(struct elf *elf, struct symbol *sym) 3149a7827b7SPeter Zijlstra { 3159a7827b7SPeter Zijlstra struct list_head *entry; 3169a7827b7SPeter Zijlstra struct rb_node *pnode; 3179a7827b7SPeter Zijlstra 3189a7827b7SPeter Zijlstra sym->type = GELF_ST_TYPE(sym->sym.st_info); 3199a7827b7SPeter Zijlstra sym->bind = GELF_ST_BIND(sym->sym.st_info); 3209a7827b7SPeter Zijlstra 3219a7827b7SPeter Zijlstra sym->offset = sym->sym.st_value; 3229a7827b7SPeter Zijlstra sym->len = sym->sym.st_size; 3239a7827b7SPeter Zijlstra 3249a7827b7SPeter Zijlstra rb_add(&sym->node, &sym->sec->symbol_tree, symbol_to_offset); 3259a7827b7SPeter Zijlstra pnode = rb_prev(&sym->node); 3269a7827b7SPeter Zijlstra if (pnode) 3279a7827b7SPeter Zijlstra entry = &rb_entry(pnode, struct symbol, node)->list; 3289a7827b7SPeter Zijlstra else 3299a7827b7SPeter Zijlstra entry = &sym->sec->symbol_list; 3309a7827b7SPeter Zijlstra list_add(&sym->list, entry); 33125cf0d8aSPeter Zijlstra elf_hash_add(symbol, &sym->hash, sym->idx); 33225cf0d8aSPeter Zijlstra elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name)); 3339a7827b7SPeter Zijlstra 3349a7827b7SPeter Zijlstra /* 3359a7827b7SPeter Zijlstra * Don't store empty STT_NOTYPE symbols in the rbtree. They 3369a7827b7SPeter Zijlstra * can exist within a function, confusing the sorting. 3379a7827b7SPeter Zijlstra */ 3389a7827b7SPeter Zijlstra if (!sym->len) 3399a7827b7SPeter Zijlstra rb_erase(&sym->node, &sym->sec->symbol_tree); 3409a7827b7SPeter Zijlstra } 3419a7827b7SPeter Zijlstra 342442f04c3SJosh Poimboeuf static int read_symbols(struct elf *elf) 343442f04c3SJosh Poimboeuf { 34428fe1d7bSSami Tolvanen struct section *symtab, *symtab_shndx, *sec; 3452a362eccSPeter Zijlstra struct symbol *sym, *pfunc; 346442f04c3SJosh Poimboeuf int symbols_nr, i; 34713810435SJosh Poimboeuf char *coldstr; 34828fe1d7bSSami Tolvanen Elf_Data *shndx_data = NULL; 34928fe1d7bSSami Tolvanen Elf32_Word shndx; 350442f04c3SJosh Poimboeuf 351442f04c3SJosh Poimboeuf symtab = find_section_by_name(elf, ".symtab"); 35225cf0d8aSPeter Zijlstra if (symtab) { 35328fe1d7bSSami Tolvanen symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); 35428fe1d7bSSami Tolvanen if (symtab_shndx) 35528fe1d7bSSami Tolvanen shndx_data = symtab_shndx->data; 35628fe1d7bSSami Tolvanen 357442f04c3SJosh Poimboeuf symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize; 35825cf0d8aSPeter Zijlstra } else { 35925cf0d8aSPeter Zijlstra /* 36025cf0d8aSPeter Zijlstra * A missing symbol table is actually possible if it's an empty 36125cf0d8aSPeter Zijlstra * .o file. This can happen for thunk_64.o. Make sure to at 36225cf0d8aSPeter Zijlstra * least allocate the symbol hash tables so we can do symbol 36325cf0d8aSPeter Zijlstra * lookups without crashing. 36425cf0d8aSPeter Zijlstra */ 36525cf0d8aSPeter Zijlstra symbols_nr = 0; 36625cf0d8aSPeter Zijlstra } 36725cf0d8aSPeter Zijlstra 36825cf0d8aSPeter Zijlstra if (!elf_alloc_hash(symbol, symbols_nr) || 36925cf0d8aSPeter Zijlstra !elf_alloc_hash(symbol_name, symbols_nr)) 37025cf0d8aSPeter Zijlstra return -1; 371442f04c3SJosh Poimboeuf 372442f04c3SJosh Poimboeuf for (i = 0; i < symbols_nr; i++) { 373442f04c3SJosh Poimboeuf sym = malloc(sizeof(*sym)); 374442f04c3SJosh Poimboeuf if (!sym) { 375442f04c3SJosh Poimboeuf perror("malloc"); 376442f04c3SJosh Poimboeuf return -1; 377442f04c3SJosh Poimboeuf } 378442f04c3SJosh Poimboeuf memset(sym, 0, sizeof(*sym)); 3792a362eccSPeter Zijlstra sym->alias = sym; 380442f04c3SJosh Poimboeuf 381442f04c3SJosh Poimboeuf sym->idx = i; 382442f04c3SJosh Poimboeuf 38328fe1d7bSSami Tolvanen if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym, 38428fe1d7bSSami Tolvanen &shndx)) { 38528fe1d7bSSami Tolvanen WARN_ELF("gelf_getsymshndx"); 386442f04c3SJosh Poimboeuf goto err; 387442f04c3SJosh Poimboeuf } 388442f04c3SJosh Poimboeuf 389442f04c3SJosh Poimboeuf sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, 390442f04c3SJosh Poimboeuf sym->sym.st_name); 391442f04c3SJosh Poimboeuf if (!sym->name) { 392baa41469SJosh Poimboeuf WARN_ELF("elf_strptr"); 393442f04c3SJosh Poimboeuf goto err; 394442f04c3SJosh Poimboeuf } 395442f04c3SJosh Poimboeuf 39628fe1d7bSSami Tolvanen if ((sym->sym.st_shndx > SHN_UNDEF && 39728fe1d7bSSami Tolvanen sym->sym.st_shndx < SHN_LORESERVE) || 39828fe1d7bSSami Tolvanen (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) { 39928fe1d7bSSami Tolvanen if (sym->sym.st_shndx != SHN_XINDEX) 40028fe1d7bSSami Tolvanen shndx = sym->sym.st_shndx; 40128fe1d7bSSami Tolvanen 40228fe1d7bSSami Tolvanen sym->sec = find_section_by_index(elf, shndx); 403442f04c3SJosh Poimboeuf if (!sym->sec) { 404442f04c3SJosh Poimboeuf WARN("couldn't find section for symbol %s", 405442f04c3SJosh Poimboeuf sym->name); 406442f04c3SJosh Poimboeuf goto err; 407442f04c3SJosh Poimboeuf } 4089a7827b7SPeter Zijlstra if (GELF_ST_TYPE(sym->sym.st_info) == STT_SECTION) { 409442f04c3SJosh Poimboeuf sym->name = sym->sec->name; 410442f04c3SJosh Poimboeuf sym->sec->sym = sym; 411442f04c3SJosh Poimboeuf } 412442f04c3SJosh Poimboeuf } else 413442f04c3SJosh Poimboeuf sym->sec = find_section_by_index(elf, 0); 414442f04c3SJosh Poimboeuf 4159a7827b7SPeter Zijlstra elf_add_symbol(elf, sym); 416442f04c3SJosh Poimboeuf } 417442f04c3SJosh Poimboeuf 41825cf0d8aSPeter Zijlstra if (stats) { 4191e11f3fdSPeter Zijlstra printf("nr_symbols: %lu\n", (unsigned long)symbols_nr); 42025cf0d8aSPeter Zijlstra printf("symbol_bits: %d\n", elf->symbol_bits); 42125cf0d8aSPeter Zijlstra } 4221e11f3fdSPeter Zijlstra 42313810435SJosh Poimboeuf /* Create parent/child links for any cold subfunctions */ 42413810435SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 42513810435SJosh Poimboeuf list_for_each_entry(sym, &sec->symbol_list, list) { 42622566c16SArtem Savkov char pname[MAX_NAME_LEN + 1]; 42722566c16SArtem Savkov size_t pnamelen; 42813810435SJosh Poimboeuf if (sym->type != STT_FUNC) 42913810435SJosh Poimboeuf continue; 430e000acc1SKristen Carlson Accardi 431e000acc1SKristen Carlson Accardi if (sym->pfunc == NULL) 432e000acc1SKristen Carlson Accardi sym->pfunc = sym; 433e000acc1SKristen Carlson Accardi 434e000acc1SKristen Carlson Accardi if (sym->cfunc == NULL) 435e000acc1SKristen Carlson Accardi sym->cfunc = sym; 436e000acc1SKristen Carlson Accardi 437bcb6fb5dSJosh Poimboeuf coldstr = strstr(sym->name, ".cold"); 43808b393d0SJosh Poimboeuf if (!coldstr) 43908b393d0SJosh Poimboeuf continue; 44008b393d0SJosh Poimboeuf 44122566c16SArtem Savkov pnamelen = coldstr - sym->name; 44222566c16SArtem Savkov if (pnamelen > MAX_NAME_LEN) { 44322566c16SArtem Savkov WARN("%s(): parent function name exceeds maximum length of %d characters", 44422566c16SArtem Savkov sym->name, MAX_NAME_LEN); 44522566c16SArtem Savkov return -1; 44622566c16SArtem Savkov } 44722566c16SArtem Savkov 44822566c16SArtem Savkov strncpy(pname, sym->name, pnamelen); 44922566c16SArtem Savkov pname[pnamelen] = '\0'; 45022566c16SArtem Savkov pfunc = find_symbol_by_name(elf, pname); 45113810435SJosh Poimboeuf 45213810435SJosh Poimboeuf if (!pfunc) { 45313810435SJosh Poimboeuf WARN("%s(): can't find parent function", 45413810435SJosh Poimboeuf sym->name); 4550b9301fbSArtem Savkov return -1; 45613810435SJosh Poimboeuf } 45713810435SJosh Poimboeuf 45813810435SJosh Poimboeuf sym->pfunc = pfunc; 45913810435SJosh Poimboeuf pfunc->cfunc = sym; 46008b393d0SJosh Poimboeuf 46108b393d0SJosh Poimboeuf /* 46208b393d0SJosh Poimboeuf * Unfortunately, -fnoreorder-functions puts the child 46308b393d0SJosh Poimboeuf * inside the parent. Remove the overlap so we can 46408b393d0SJosh Poimboeuf * have sane assumptions. 46508b393d0SJosh Poimboeuf * 46608b393d0SJosh Poimboeuf * Note that pfunc->len now no longer matches 46708b393d0SJosh Poimboeuf * pfunc->sym.st_size. 46808b393d0SJosh Poimboeuf */ 46908b393d0SJosh Poimboeuf if (sym->sec == pfunc->sec && 47008b393d0SJosh Poimboeuf sym->offset >= pfunc->offset && 47108b393d0SJosh Poimboeuf sym->offset + sym->len == pfunc->offset + pfunc->len) { 47208b393d0SJosh Poimboeuf pfunc->len -= sym->len; 47313810435SJosh Poimboeuf } 47413810435SJosh Poimboeuf } 47513810435SJosh Poimboeuf } 47613810435SJosh Poimboeuf 477442f04c3SJosh Poimboeuf return 0; 478442f04c3SJosh Poimboeuf 479442f04c3SJosh Poimboeuf err: 480442f04c3SJosh Poimboeuf free(sym); 481442f04c3SJosh Poimboeuf return -1; 482442f04c3SJosh Poimboeuf } 483442f04c3SJosh Poimboeuf 484d0c5c4ccSPeter Zijlstra static struct section *elf_create_reloc_section(struct elf *elf, 485d0c5c4ccSPeter Zijlstra struct section *base, 486d0c5c4ccSPeter Zijlstra int reltype); 487d0c5c4ccSPeter Zijlstra 488ef47cc01SPeter Zijlstra int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset, 489ef47cc01SPeter Zijlstra unsigned int type, struct symbol *sym, int addend) 49034f7c96dSPeter Zijlstra { 491ef47cc01SPeter Zijlstra struct reloc *reloc; 49234f7c96dSPeter Zijlstra 493d0c5c4ccSPeter Zijlstra if (!sec->reloc && !elf_create_reloc_section(elf, sec, SHT_RELA)) 494d0c5c4ccSPeter Zijlstra return -1; 495d0c5c4ccSPeter Zijlstra 496ef47cc01SPeter Zijlstra reloc = malloc(sizeof(*reloc)); 497ef47cc01SPeter Zijlstra if (!reloc) { 498ef47cc01SPeter Zijlstra perror("malloc"); 499ef47cc01SPeter Zijlstra return -1; 500ef47cc01SPeter Zijlstra } 501ef47cc01SPeter Zijlstra memset(reloc, 0, sizeof(*reloc)); 502ef47cc01SPeter Zijlstra 503ef47cc01SPeter Zijlstra reloc->sec = sec->reloc; 504ef47cc01SPeter Zijlstra reloc->offset = offset; 505ef47cc01SPeter Zijlstra reloc->type = type; 506ef47cc01SPeter Zijlstra reloc->sym = sym; 507ef47cc01SPeter Zijlstra reloc->addend = addend; 508ef47cc01SPeter Zijlstra 509ef47cc01SPeter Zijlstra list_add_tail(&reloc->list, &sec->reloc->reloc_list); 51025cf0d8aSPeter Zijlstra elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); 5113a647607SPeter Zijlstra 512ef47cc01SPeter Zijlstra sec->reloc->changed = true; 513ef47cc01SPeter Zijlstra 514ef47cc01SPeter Zijlstra return 0; 515ef47cc01SPeter Zijlstra } 516ef47cc01SPeter Zijlstra 517ef47cc01SPeter Zijlstra int elf_add_reloc_to_insn(struct elf *elf, struct section *sec, 518ef47cc01SPeter Zijlstra unsigned long offset, unsigned int type, 519ef47cc01SPeter Zijlstra struct section *insn_sec, unsigned long insn_off) 520ef47cc01SPeter Zijlstra { 521ef47cc01SPeter Zijlstra struct symbol *sym; 522ef47cc01SPeter Zijlstra int addend; 523ef47cc01SPeter Zijlstra 524ef47cc01SPeter Zijlstra if (insn_sec->sym) { 525ef47cc01SPeter Zijlstra sym = insn_sec->sym; 526ef47cc01SPeter Zijlstra addend = insn_off; 527ef47cc01SPeter Zijlstra 528ef47cc01SPeter Zijlstra } else { 529ef47cc01SPeter Zijlstra /* 530ef47cc01SPeter Zijlstra * The Clang assembler strips section symbols, so we have to 531ef47cc01SPeter Zijlstra * reference the function symbol instead: 532ef47cc01SPeter Zijlstra */ 533ef47cc01SPeter Zijlstra sym = find_symbol_containing(insn_sec, insn_off); 534ef47cc01SPeter Zijlstra if (!sym) { 535ef47cc01SPeter Zijlstra /* 536ef47cc01SPeter Zijlstra * Hack alert. This happens when we need to reference 537ef47cc01SPeter Zijlstra * the NOP pad insn immediately after the function. 538ef47cc01SPeter Zijlstra */ 539ef47cc01SPeter Zijlstra sym = find_symbol_containing(insn_sec, insn_off - 1); 540ef47cc01SPeter Zijlstra } 541ef47cc01SPeter Zijlstra 542ef47cc01SPeter Zijlstra if (!sym) { 543ef47cc01SPeter Zijlstra WARN("can't find symbol containing %s+0x%lx", insn_sec->name, insn_off); 544ef47cc01SPeter Zijlstra return -1; 545ef47cc01SPeter Zijlstra } 546ef47cc01SPeter Zijlstra 547ef47cc01SPeter Zijlstra addend = insn_off - sym->offset; 548ef47cc01SPeter Zijlstra } 549ef47cc01SPeter Zijlstra 550ef47cc01SPeter Zijlstra return elf_add_reloc(elf, sec, offset, type, sym, addend); 55134f7c96dSPeter Zijlstra } 55234f7c96dSPeter Zijlstra 553fb414783SMatt Helsley static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) 554fb414783SMatt Helsley { 555fb414783SMatt Helsley if (!gelf_getrel(sec->data, i, &reloc->rel)) { 556fb414783SMatt Helsley WARN_ELF("gelf_getrel"); 557fb414783SMatt Helsley return -1; 558fb414783SMatt Helsley } 559fb414783SMatt Helsley reloc->type = GELF_R_TYPE(reloc->rel.r_info); 560fb414783SMatt Helsley reloc->addend = 0; 561fb414783SMatt Helsley reloc->offset = reloc->rel.r_offset; 562fb414783SMatt Helsley *symndx = GELF_R_SYM(reloc->rel.r_info); 563fb414783SMatt Helsley return 0; 564fb414783SMatt Helsley } 565fb414783SMatt Helsley 566fb414783SMatt Helsley static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) 567fb414783SMatt Helsley { 568fb414783SMatt Helsley if (!gelf_getrela(sec->data, i, &reloc->rela)) { 569fb414783SMatt Helsley WARN_ELF("gelf_getrela"); 570fb414783SMatt Helsley return -1; 571fb414783SMatt Helsley } 572fb414783SMatt Helsley reloc->type = GELF_R_TYPE(reloc->rela.r_info); 573fb414783SMatt Helsley reloc->addend = reloc->rela.r_addend; 574fb414783SMatt Helsley reloc->offset = reloc->rela.r_offset; 575fb414783SMatt Helsley *symndx = GELF_R_SYM(reloc->rela.r_info); 576fb414783SMatt Helsley return 0; 577fb414783SMatt Helsley } 578fb414783SMatt Helsley 579f1974222SMatt Helsley static int read_relocs(struct elf *elf) 580442f04c3SJosh Poimboeuf { 581442f04c3SJosh Poimboeuf struct section *sec; 582f1974222SMatt Helsley struct reloc *reloc; 583442f04c3SJosh Poimboeuf int i; 584442f04c3SJosh Poimboeuf unsigned int symndx; 585f1974222SMatt Helsley unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0; 586442f04c3SJosh Poimboeuf 587*d33b9035SPeter Zijlstra if (!elf_alloc_hash(reloc, elf->text_size / 16)) 58825cf0d8aSPeter Zijlstra return -1; 58925cf0d8aSPeter Zijlstra 590442f04c3SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 591fb414783SMatt Helsley if ((sec->sh.sh_type != SHT_RELA) && 592fb414783SMatt Helsley (sec->sh.sh_type != SHT_REL)) 593442f04c3SJosh Poimboeuf continue; 594442f04c3SJosh Poimboeuf 5951e968bf5SSami Tolvanen sec->base = find_section_by_index(elf, sec->sh.sh_info); 596442f04c3SJosh Poimboeuf if (!sec->base) { 597f1974222SMatt Helsley WARN("can't find base section for reloc section %s", 598442f04c3SJosh Poimboeuf sec->name); 599442f04c3SJosh Poimboeuf return -1; 600442f04c3SJosh Poimboeuf } 601442f04c3SJosh Poimboeuf 602f1974222SMatt Helsley sec->base->reloc = sec; 603442f04c3SJosh Poimboeuf 604f1974222SMatt Helsley nr_reloc = 0; 605442f04c3SJosh Poimboeuf for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { 606f1974222SMatt Helsley reloc = malloc(sizeof(*reloc)); 607f1974222SMatt Helsley if (!reloc) { 608442f04c3SJosh Poimboeuf perror("malloc"); 609442f04c3SJosh Poimboeuf return -1; 610442f04c3SJosh Poimboeuf } 611f1974222SMatt Helsley memset(reloc, 0, sizeof(*reloc)); 612fb414783SMatt Helsley switch (sec->sh.sh_type) { 613fb414783SMatt Helsley case SHT_REL: 614fb414783SMatt Helsley if (read_rel_reloc(sec, i, reloc, &symndx)) 615442f04c3SJosh Poimboeuf return -1; 616fb414783SMatt Helsley break; 617fb414783SMatt Helsley case SHT_RELA: 618fb414783SMatt Helsley if (read_rela_reloc(sec, i, reloc, &symndx)) 619fb414783SMatt Helsley return -1; 620fb414783SMatt Helsley break; 621fb414783SMatt Helsley default: return -1; 622442f04c3SJosh Poimboeuf } 623442f04c3SJosh Poimboeuf 624f1974222SMatt Helsley reloc->sec = sec; 625d832c005SPeter Zijlstra reloc->idx = i; 626d832c005SPeter Zijlstra reloc->sym = find_symbol_by_index(elf, symndx); 627f1974222SMatt Helsley if (!reloc->sym) { 628f1974222SMatt Helsley WARN("can't find reloc entry symbol %d for %s", 629442f04c3SJosh Poimboeuf symndx, sec->name); 630442f04c3SJosh Poimboeuf return -1; 631442f04c3SJosh Poimboeuf } 632042ba73fSJosh Poimboeuf 6333a647607SPeter Zijlstra list_add_tail(&reloc->list, &sec->reloc_list); 63425cf0d8aSPeter Zijlstra elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); 6353a647607SPeter Zijlstra 636f1974222SMatt Helsley nr_reloc++; 637442f04c3SJosh Poimboeuf } 638f1974222SMatt Helsley max_reloc = max(max_reloc, nr_reloc); 639f1974222SMatt Helsley tot_reloc += nr_reloc; 6401e11f3fdSPeter Zijlstra } 6411e11f3fdSPeter Zijlstra 6421e11f3fdSPeter Zijlstra if (stats) { 643f1974222SMatt Helsley printf("max_reloc: %lu\n", max_reloc); 644f1974222SMatt Helsley printf("tot_reloc: %lu\n", tot_reloc); 64525cf0d8aSPeter Zijlstra printf("reloc_bits: %d\n", elf->reloc_bits); 646442f04c3SJosh Poimboeuf } 647442f04c3SJosh Poimboeuf 648442f04c3SJosh Poimboeuf return 0; 649442f04c3SJosh Poimboeuf } 650442f04c3SJosh Poimboeuf 651bc359ff2SIngo Molnar struct elf *elf_open_read(const char *name, int flags) 652442f04c3SJosh Poimboeuf { 653442f04c3SJosh Poimboeuf struct elf *elf; 654627fce14SJosh Poimboeuf Elf_Cmd cmd; 655442f04c3SJosh Poimboeuf 656442f04c3SJosh Poimboeuf elf_version(EV_CURRENT); 657442f04c3SJosh Poimboeuf 658442f04c3SJosh Poimboeuf elf = malloc(sizeof(*elf)); 659442f04c3SJosh Poimboeuf if (!elf) { 660442f04c3SJosh Poimboeuf perror("malloc"); 661442f04c3SJosh Poimboeuf return NULL; 662442f04c3SJosh Poimboeuf } 66334f7c96dSPeter Zijlstra memset(elf, 0, offsetof(struct elf, sections)); 664442f04c3SJosh Poimboeuf 665442f04c3SJosh Poimboeuf INIT_LIST_HEAD(&elf->sections); 666442f04c3SJosh Poimboeuf 667627fce14SJosh Poimboeuf elf->fd = open(name, flags); 668442f04c3SJosh Poimboeuf if (elf->fd == -1) { 669385d11b1SJosh Poimboeuf fprintf(stderr, "objtool: Can't open '%s': %s\n", 670385d11b1SJosh Poimboeuf name, strerror(errno)); 671442f04c3SJosh Poimboeuf goto err; 672442f04c3SJosh Poimboeuf } 673442f04c3SJosh Poimboeuf 674627fce14SJosh Poimboeuf if ((flags & O_ACCMODE) == O_RDONLY) 675627fce14SJosh Poimboeuf cmd = ELF_C_READ_MMAP; 676627fce14SJosh Poimboeuf else if ((flags & O_ACCMODE) == O_RDWR) 677627fce14SJosh Poimboeuf cmd = ELF_C_RDWR; 678627fce14SJosh Poimboeuf else /* O_WRONLY */ 679627fce14SJosh Poimboeuf cmd = ELF_C_WRITE; 680627fce14SJosh Poimboeuf 681627fce14SJosh Poimboeuf elf->elf = elf_begin(elf->fd, cmd, NULL); 682442f04c3SJosh Poimboeuf if (!elf->elf) { 683baa41469SJosh Poimboeuf WARN_ELF("elf_begin"); 684442f04c3SJosh Poimboeuf goto err; 685442f04c3SJosh Poimboeuf } 686442f04c3SJosh Poimboeuf 687442f04c3SJosh Poimboeuf if (!gelf_getehdr(elf->elf, &elf->ehdr)) { 688baa41469SJosh Poimboeuf WARN_ELF("gelf_getehdr"); 689442f04c3SJosh Poimboeuf goto err; 690442f04c3SJosh Poimboeuf } 691442f04c3SJosh Poimboeuf 692442f04c3SJosh Poimboeuf if (read_sections(elf)) 693442f04c3SJosh Poimboeuf goto err; 694442f04c3SJosh Poimboeuf 695442f04c3SJosh Poimboeuf if (read_symbols(elf)) 696442f04c3SJosh Poimboeuf goto err; 697442f04c3SJosh Poimboeuf 698f1974222SMatt Helsley if (read_relocs(elf)) 699442f04c3SJosh Poimboeuf goto err; 700442f04c3SJosh Poimboeuf 701442f04c3SJosh Poimboeuf return elf; 702442f04c3SJosh Poimboeuf 703442f04c3SJosh Poimboeuf err: 704442f04c3SJosh Poimboeuf elf_close(elf); 705442f04c3SJosh Poimboeuf return NULL; 706442f04c3SJosh Poimboeuf } 707442f04c3SJosh Poimboeuf 708417a4dc9SPeter Zijlstra static int elf_add_string(struct elf *elf, struct section *strtab, char *str) 709417a4dc9SPeter Zijlstra { 710417a4dc9SPeter Zijlstra Elf_Data *data; 711417a4dc9SPeter Zijlstra Elf_Scn *s; 712417a4dc9SPeter Zijlstra int len; 713417a4dc9SPeter Zijlstra 714417a4dc9SPeter Zijlstra if (!strtab) 715417a4dc9SPeter Zijlstra strtab = find_section_by_name(elf, ".strtab"); 716417a4dc9SPeter Zijlstra if (!strtab) { 717417a4dc9SPeter Zijlstra WARN("can't find .strtab section"); 718417a4dc9SPeter Zijlstra return -1; 719417a4dc9SPeter Zijlstra } 720417a4dc9SPeter Zijlstra 721417a4dc9SPeter Zijlstra s = elf_getscn(elf->elf, strtab->idx); 722417a4dc9SPeter Zijlstra if (!s) { 723417a4dc9SPeter Zijlstra WARN_ELF("elf_getscn"); 724417a4dc9SPeter Zijlstra return -1; 725417a4dc9SPeter Zijlstra } 726417a4dc9SPeter Zijlstra 727417a4dc9SPeter Zijlstra data = elf_newdata(s); 728417a4dc9SPeter Zijlstra if (!data) { 729417a4dc9SPeter Zijlstra WARN_ELF("elf_newdata"); 730417a4dc9SPeter Zijlstra return -1; 731417a4dc9SPeter Zijlstra } 732417a4dc9SPeter Zijlstra 733417a4dc9SPeter Zijlstra data->d_buf = str; 734417a4dc9SPeter Zijlstra data->d_size = strlen(str) + 1; 735417a4dc9SPeter Zijlstra data->d_align = 1; 736417a4dc9SPeter Zijlstra 737417a4dc9SPeter Zijlstra len = strtab->len; 738417a4dc9SPeter Zijlstra strtab->len += data->d_size; 739417a4dc9SPeter Zijlstra strtab->changed = true; 740417a4dc9SPeter Zijlstra 741417a4dc9SPeter Zijlstra return len; 742417a4dc9SPeter Zijlstra } 743417a4dc9SPeter Zijlstra 7442f2f7e47SPeter Zijlstra struct symbol *elf_create_undef_symbol(struct elf *elf, const char *name) 7452f2f7e47SPeter Zijlstra { 7462f2f7e47SPeter Zijlstra struct section *symtab; 7472f2f7e47SPeter Zijlstra struct symbol *sym; 7482f2f7e47SPeter Zijlstra Elf_Data *data; 7492f2f7e47SPeter Zijlstra Elf_Scn *s; 7502f2f7e47SPeter Zijlstra 7512f2f7e47SPeter Zijlstra sym = malloc(sizeof(*sym)); 7522f2f7e47SPeter Zijlstra if (!sym) { 7532f2f7e47SPeter Zijlstra perror("malloc"); 7542f2f7e47SPeter Zijlstra return NULL; 7552f2f7e47SPeter Zijlstra } 7562f2f7e47SPeter Zijlstra memset(sym, 0, sizeof(*sym)); 7572f2f7e47SPeter Zijlstra 7582f2f7e47SPeter Zijlstra sym->name = strdup(name); 7592f2f7e47SPeter Zijlstra 7602f2f7e47SPeter Zijlstra sym->sym.st_name = elf_add_string(elf, NULL, sym->name); 7612f2f7e47SPeter Zijlstra if (sym->sym.st_name == -1) 7622f2f7e47SPeter Zijlstra return NULL; 7632f2f7e47SPeter Zijlstra 7642f2f7e47SPeter Zijlstra sym->sym.st_info = GELF_ST_INFO(STB_GLOBAL, STT_NOTYPE); 7652f2f7e47SPeter Zijlstra // st_other 0 7662f2f7e47SPeter Zijlstra // st_shndx 0 7672f2f7e47SPeter Zijlstra // st_value 0 7682f2f7e47SPeter Zijlstra // st_size 0 7692f2f7e47SPeter Zijlstra 7702f2f7e47SPeter Zijlstra symtab = find_section_by_name(elf, ".symtab"); 7712f2f7e47SPeter Zijlstra if (!symtab) { 7722f2f7e47SPeter Zijlstra WARN("can't find .symtab"); 7732f2f7e47SPeter Zijlstra return NULL; 7742f2f7e47SPeter Zijlstra } 7752f2f7e47SPeter Zijlstra 7762f2f7e47SPeter Zijlstra s = elf_getscn(elf->elf, symtab->idx); 7772f2f7e47SPeter Zijlstra if (!s) { 7782f2f7e47SPeter Zijlstra WARN_ELF("elf_getscn"); 7792f2f7e47SPeter Zijlstra return NULL; 7802f2f7e47SPeter Zijlstra } 7812f2f7e47SPeter Zijlstra 7822f2f7e47SPeter Zijlstra data = elf_newdata(s); 7832f2f7e47SPeter Zijlstra if (!data) { 7842f2f7e47SPeter Zijlstra WARN_ELF("elf_newdata"); 7852f2f7e47SPeter Zijlstra return NULL; 7862f2f7e47SPeter Zijlstra } 7872f2f7e47SPeter Zijlstra 7882f2f7e47SPeter Zijlstra data->d_buf = &sym->sym; 7892f2f7e47SPeter Zijlstra data->d_size = sizeof(sym->sym); 7902f2f7e47SPeter Zijlstra data->d_align = 1; 7912f2f7e47SPeter Zijlstra 7922f2f7e47SPeter Zijlstra sym->idx = symtab->len / sizeof(sym->sym); 7932f2f7e47SPeter Zijlstra 7942f2f7e47SPeter Zijlstra symtab->len += data->d_size; 7952f2f7e47SPeter Zijlstra symtab->changed = true; 7962f2f7e47SPeter Zijlstra 7972f2f7e47SPeter Zijlstra sym->sec = find_section_by_index(elf, 0); 7982f2f7e47SPeter Zijlstra 7992f2f7e47SPeter Zijlstra elf_add_symbol(elf, sym); 8002f2f7e47SPeter Zijlstra 8012f2f7e47SPeter Zijlstra return sym; 8022f2f7e47SPeter Zijlstra } 8032f2f7e47SPeter Zijlstra 804627fce14SJosh Poimboeuf struct section *elf_create_section(struct elf *elf, const char *name, 8051e7e4788SJosh Poimboeuf unsigned int sh_flags, size_t entsize, int nr) 806627fce14SJosh Poimboeuf { 807627fce14SJosh Poimboeuf struct section *sec, *shstrtab; 808627fce14SJosh Poimboeuf size_t size = entsize * nr; 8093c3ea503SMichael Forney Elf_Scn *s; 810627fce14SJosh Poimboeuf 811627fce14SJosh Poimboeuf sec = malloc(sizeof(*sec)); 812627fce14SJosh Poimboeuf if (!sec) { 813627fce14SJosh Poimboeuf perror("malloc"); 814627fce14SJosh Poimboeuf return NULL; 815627fce14SJosh Poimboeuf } 816627fce14SJosh Poimboeuf memset(sec, 0, sizeof(*sec)); 817627fce14SJosh Poimboeuf 818627fce14SJosh Poimboeuf INIT_LIST_HEAD(&sec->symbol_list); 819f1974222SMatt Helsley INIT_LIST_HEAD(&sec->reloc_list); 820627fce14SJosh Poimboeuf 821627fce14SJosh Poimboeuf s = elf_newscn(elf->elf); 822627fce14SJosh Poimboeuf if (!s) { 823627fce14SJosh Poimboeuf WARN_ELF("elf_newscn"); 824627fce14SJosh Poimboeuf return NULL; 825627fce14SJosh Poimboeuf } 826627fce14SJosh Poimboeuf 827627fce14SJosh Poimboeuf sec->name = strdup(name); 828627fce14SJosh Poimboeuf if (!sec->name) { 829627fce14SJosh Poimboeuf perror("strdup"); 830627fce14SJosh Poimboeuf return NULL; 831627fce14SJosh Poimboeuf } 832627fce14SJosh Poimboeuf 833627fce14SJosh Poimboeuf sec->idx = elf_ndxscn(s); 834627fce14SJosh Poimboeuf sec->len = size; 835627fce14SJosh Poimboeuf sec->changed = true; 836627fce14SJosh Poimboeuf 837627fce14SJosh Poimboeuf sec->data = elf_newdata(s); 838627fce14SJosh Poimboeuf if (!sec->data) { 839627fce14SJosh Poimboeuf WARN_ELF("elf_newdata"); 840627fce14SJosh Poimboeuf return NULL; 841627fce14SJosh Poimboeuf } 842627fce14SJosh Poimboeuf 843627fce14SJosh Poimboeuf sec->data->d_size = size; 844627fce14SJosh Poimboeuf sec->data->d_align = 1; 845627fce14SJosh Poimboeuf 846627fce14SJosh Poimboeuf if (size) { 847627fce14SJosh Poimboeuf sec->data->d_buf = malloc(size); 848627fce14SJosh Poimboeuf if (!sec->data->d_buf) { 849627fce14SJosh Poimboeuf perror("malloc"); 850627fce14SJosh Poimboeuf return NULL; 851627fce14SJosh Poimboeuf } 852627fce14SJosh Poimboeuf memset(sec->data->d_buf, 0, size); 853627fce14SJosh Poimboeuf } 854627fce14SJosh Poimboeuf 855627fce14SJosh Poimboeuf if (!gelf_getshdr(s, &sec->sh)) { 856627fce14SJosh Poimboeuf WARN_ELF("gelf_getshdr"); 857627fce14SJosh Poimboeuf return NULL; 858627fce14SJosh Poimboeuf } 859627fce14SJosh Poimboeuf 860627fce14SJosh Poimboeuf sec->sh.sh_size = size; 861627fce14SJosh Poimboeuf sec->sh.sh_entsize = entsize; 862627fce14SJosh Poimboeuf sec->sh.sh_type = SHT_PROGBITS; 863627fce14SJosh Poimboeuf sec->sh.sh_addralign = 1; 8641e7e4788SJosh Poimboeuf sec->sh.sh_flags = SHF_ALLOC | sh_flags; 865627fce14SJosh Poimboeuf 8666d77d3b4SSimon Ser /* Add section name to .shstrtab (or .strtab for Clang) */ 867627fce14SJosh Poimboeuf shstrtab = find_section_by_name(elf, ".shstrtab"); 8686d77d3b4SSimon Ser if (!shstrtab) 8696d77d3b4SSimon Ser shstrtab = find_section_by_name(elf, ".strtab"); 870627fce14SJosh Poimboeuf if (!shstrtab) { 8716d77d3b4SSimon Ser WARN("can't find .shstrtab or .strtab section"); 872627fce14SJosh Poimboeuf return NULL; 873627fce14SJosh Poimboeuf } 874417a4dc9SPeter Zijlstra sec->sh.sh_name = elf_add_string(elf, shstrtab, sec->name); 875417a4dc9SPeter Zijlstra if (sec->sh.sh_name == -1) 876627fce14SJosh Poimboeuf return NULL; 877627fce14SJosh Poimboeuf 87853038996SPeter Zijlstra list_add_tail(&sec->list, &elf->sections); 87925cf0d8aSPeter Zijlstra elf_hash_add(section, &sec->hash, sec->idx); 88025cf0d8aSPeter Zijlstra elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); 88153038996SPeter Zijlstra 8822b10be23SPeter Zijlstra elf->changed = true; 8832b10be23SPeter Zijlstra 884627fce14SJosh Poimboeuf return sec; 885627fce14SJosh Poimboeuf } 886627fce14SJosh Poimboeuf 887fb414783SMatt Helsley static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base) 888fb414783SMatt Helsley { 889fb414783SMatt Helsley char *relocname; 890fb414783SMatt Helsley struct section *sec; 891fb414783SMatt Helsley 892fb414783SMatt Helsley relocname = malloc(strlen(base->name) + strlen(".rel") + 1); 893fb414783SMatt Helsley if (!relocname) { 894fb414783SMatt Helsley perror("malloc"); 895fb414783SMatt Helsley return NULL; 896fb414783SMatt Helsley } 897fb414783SMatt Helsley strcpy(relocname, ".rel"); 898fb414783SMatt Helsley strcat(relocname, base->name); 899fb414783SMatt Helsley 9001e7e4788SJosh Poimboeuf sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rel), 0); 901fb414783SMatt Helsley free(relocname); 902fb414783SMatt Helsley if (!sec) 903fb414783SMatt Helsley return NULL; 904fb414783SMatt Helsley 905fb414783SMatt Helsley base->reloc = sec; 906fb414783SMatt Helsley sec->base = base; 907fb414783SMatt Helsley 908fb414783SMatt Helsley sec->sh.sh_type = SHT_REL; 909fb414783SMatt Helsley sec->sh.sh_addralign = 8; 910fb414783SMatt Helsley sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; 911fb414783SMatt Helsley sec->sh.sh_info = base->idx; 912fb414783SMatt Helsley sec->sh.sh_flags = SHF_INFO_LINK; 913fb414783SMatt Helsley 914fb414783SMatt Helsley return sec; 915fb414783SMatt Helsley } 916fb414783SMatt Helsley 917fb414783SMatt Helsley static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base) 918627fce14SJosh Poimboeuf { 919f1974222SMatt Helsley char *relocname; 920627fce14SJosh Poimboeuf struct section *sec; 921627fce14SJosh Poimboeuf 922f1974222SMatt Helsley relocname = malloc(strlen(base->name) + strlen(".rela") + 1); 923f1974222SMatt Helsley if (!relocname) { 924627fce14SJosh Poimboeuf perror("malloc"); 925627fce14SJosh Poimboeuf return NULL; 926627fce14SJosh Poimboeuf } 927f1974222SMatt Helsley strcpy(relocname, ".rela"); 928f1974222SMatt Helsley strcat(relocname, base->name); 929627fce14SJosh Poimboeuf 9301e7e4788SJosh Poimboeuf sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rela), 0); 931f1974222SMatt Helsley free(relocname); 932627fce14SJosh Poimboeuf if (!sec) 933627fce14SJosh Poimboeuf return NULL; 934627fce14SJosh Poimboeuf 935f1974222SMatt Helsley base->reloc = sec; 936627fce14SJosh Poimboeuf sec->base = base; 937627fce14SJosh Poimboeuf 938627fce14SJosh Poimboeuf sec->sh.sh_type = SHT_RELA; 939627fce14SJosh Poimboeuf sec->sh.sh_addralign = 8; 940627fce14SJosh Poimboeuf sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; 941627fce14SJosh Poimboeuf sec->sh.sh_info = base->idx; 942627fce14SJosh Poimboeuf sec->sh.sh_flags = SHF_INFO_LINK; 943627fce14SJosh Poimboeuf 944627fce14SJosh Poimboeuf return sec; 945627fce14SJosh Poimboeuf } 946627fce14SJosh Poimboeuf 947d0c5c4ccSPeter Zijlstra static struct section *elf_create_reloc_section(struct elf *elf, 948fb414783SMatt Helsley struct section *base, 949fb414783SMatt Helsley int reltype) 950fb414783SMatt Helsley { 951fb414783SMatt Helsley switch (reltype) { 952fb414783SMatt Helsley case SHT_REL: return elf_create_rel_reloc_section(elf, base); 953fb414783SMatt Helsley case SHT_RELA: return elf_create_rela_reloc_section(elf, base); 954fb414783SMatt Helsley default: return NULL; 955fb414783SMatt Helsley } 956fb414783SMatt Helsley } 957fb414783SMatt Helsley 958fb414783SMatt Helsley static int elf_rebuild_rel_reloc_section(struct section *sec, int nr) 959627fce14SJosh Poimboeuf { 960f1974222SMatt Helsley struct reloc *reloc; 961fb414783SMatt Helsley int idx = 0, size; 962a1a664ecSMartin Schwidefsky void *buf; 963fb414783SMatt Helsley 964fb414783SMatt Helsley /* Allocate a buffer for relocations */ 965a1a664ecSMartin Schwidefsky size = nr * sizeof(GElf_Rel); 966a1a664ecSMartin Schwidefsky buf = malloc(size); 967a1a664ecSMartin Schwidefsky if (!buf) { 968fb414783SMatt Helsley perror("malloc"); 969fb414783SMatt Helsley return -1; 970fb414783SMatt Helsley } 971fb414783SMatt Helsley 972a1a664ecSMartin Schwidefsky sec->data->d_buf = buf; 973fb414783SMatt Helsley sec->data->d_size = size; 974a1a664ecSMartin Schwidefsky sec->data->d_type = ELF_T_REL; 975fb414783SMatt Helsley 976fb414783SMatt Helsley sec->sh.sh_size = size; 977fb414783SMatt Helsley 978fb414783SMatt Helsley idx = 0; 979fb414783SMatt Helsley list_for_each_entry(reloc, &sec->reloc_list, list) { 980a1a664ecSMartin Schwidefsky reloc->rel.r_offset = reloc->offset; 981a1a664ecSMartin Schwidefsky reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 982a1a664ecSMartin Schwidefsky gelf_update_rel(sec->data, idx, &reloc->rel); 983fb414783SMatt Helsley idx++; 984fb414783SMatt Helsley } 985fb414783SMatt Helsley 986fb414783SMatt Helsley return 0; 987fb414783SMatt Helsley } 988fb414783SMatt Helsley 989fb414783SMatt Helsley static int elf_rebuild_rela_reloc_section(struct section *sec, int nr) 990fb414783SMatt Helsley { 991fb414783SMatt Helsley struct reloc *reloc; 992fb414783SMatt Helsley int idx = 0, size; 993a1a664ecSMartin Schwidefsky void *buf; 994627fce14SJosh Poimboeuf 995fb414783SMatt Helsley /* Allocate a buffer for relocations with addends */ 996a1a664ecSMartin Schwidefsky size = nr * sizeof(GElf_Rela); 997a1a664ecSMartin Schwidefsky buf = malloc(size); 998a1a664ecSMartin Schwidefsky if (!buf) { 999627fce14SJosh Poimboeuf perror("malloc"); 1000627fce14SJosh Poimboeuf return -1; 1001627fce14SJosh Poimboeuf } 1002627fce14SJosh Poimboeuf 1003a1a664ecSMartin Schwidefsky sec->data->d_buf = buf; 1004627fce14SJosh Poimboeuf sec->data->d_size = size; 1005a1a664ecSMartin Schwidefsky sec->data->d_type = ELF_T_RELA; 1006627fce14SJosh Poimboeuf 1007627fce14SJosh Poimboeuf sec->sh.sh_size = size; 1008627fce14SJosh Poimboeuf 1009627fce14SJosh Poimboeuf idx = 0; 1010f1974222SMatt Helsley list_for_each_entry(reloc, &sec->reloc_list, list) { 1011a1a664ecSMartin Schwidefsky reloc->rela.r_offset = reloc->offset; 1012a1a664ecSMartin Schwidefsky reloc->rela.r_addend = reloc->addend; 1013a1a664ecSMartin Schwidefsky reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 1014a1a664ecSMartin Schwidefsky gelf_update_rela(sec->data, idx, &reloc->rela); 1015627fce14SJosh Poimboeuf idx++; 1016627fce14SJosh Poimboeuf } 1017627fce14SJosh Poimboeuf 1018627fce14SJosh Poimboeuf return 0; 1019627fce14SJosh Poimboeuf } 1020627fce14SJosh Poimboeuf 10213a647607SPeter Zijlstra static int elf_rebuild_reloc_section(struct elf *elf, struct section *sec) 1022fb414783SMatt Helsley { 1023fb414783SMatt Helsley struct reloc *reloc; 1024fb414783SMatt Helsley int nr; 1025fb414783SMatt Helsley 1026fb414783SMatt Helsley nr = 0; 1027fb414783SMatt Helsley list_for_each_entry(reloc, &sec->reloc_list, list) 1028fb414783SMatt Helsley nr++; 1029fb414783SMatt Helsley 1030fb414783SMatt Helsley switch (sec->sh.sh_type) { 1031fb414783SMatt Helsley case SHT_REL: return elf_rebuild_rel_reloc_section(sec, nr); 1032fb414783SMatt Helsley case SHT_RELA: return elf_rebuild_rela_reloc_section(sec, nr); 1033fb414783SMatt Helsley default: return -1; 1034fb414783SMatt Helsley } 1035fb414783SMatt Helsley } 1036fb414783SMatt Helsley 1037fdabdd0bSPeter Zijlstra int elf_write_insn(struct elf *elf, struct section *sec, 1038fdabdd0bSPeter Zijlstra unsigned long offset, unsigned int len, 1039fdabdd0bSPeter Zijlstra const char *insn) 1040fdabdd0bSPeter Zijlstra { 1041fdabdd0bSPeter Zijlstra Elf_Data *data = sec->data; 1042fdabdd0bSPeter Zijlstra 1043fdabdd0bSPeter Zijlstra if (data->d_type != ELF_T_BYTE || data->d_off) { 1044fdabdd0bSPeter Zijlstra WARN("write to unexpected data for section: %s", sec->name); 1045fdabdd0bSPeter Zijlstra return -1; 1046fdabdd0bSPeter Zijlstra } 1047fdabdd0bSPeter Zijlstra 1048fdabdd0bSPeter Zijlstra memcpy(data->d_buf + offset, insn, len); 1049fdabdd0bSPeter Zijlstra elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY); 1050fdabdd0bSPeter Zijlstra 1051fdabdd0bSPeter Zijlstra elf->changed = true; 1052fdabdd0bSPeter Zijlstra 1053fdabdd0bSPeter Zijlstra return 0; 1054fdabdd0bSPeter Zijlstra } 1055fdabdd0bSPeter Zijlstra 1056d832c005SPeter Zijlstra int elf_write_reloc(struct elf *elf, struct reloc *reloc) 1057fdabdd0bSPeter Zijlstra { 1058d832c005SPeter Zijlstra struct section *sec = reloc->sec; 1059fdabdd0bSPeter Zijlstra 1060d832c005SPeter Zijlstra if (sec->sh.sh_type == SHT_REL) { 1061d832c005SPeter Zijlstra reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 1062d832c005SPeter Zijlstra reloc->rel.r_offset = reloc->offset; 1063fdabdd0bSPeter Zijlstra 1064d832c005SPeter Zijlstra if (!gelf_update_rel(sec->data, reloc->idx, &reloc->rel)) { 1065d832c005SPeter Zijlstra WARN_ELF("gelf_update_rel"); 1066d832c005SPeter Zijlstra return -1; 1067d832c005SPeter Zijlstra } 1068d832c005SPeter Zijlstra } else { 1069d832c005SPeter Zijlstra reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 1070d832c005SPeter Zijlstra reloc->rela.r_addend = reloc->addend; 1071d832c005SPeter Zijlstra reloc->rela.r_offset = reloc->offset; 1072d832c005SPeter Zijlstra 1073d832c005SPeter Zijlstra if (!gelf_update_rela(sec->data, reloc->idx, &reloc->rela)) { 1074fdabdd0bSPeter Zijlstra WARN_ELF("gelf_update_rela"); 1075fdabdd0bSPeter Zijlstra return -1; 1076fdabdd0bSPeter Zijlstra } 1077d832c005SPeter Zijlstra } 1078fdabdd0bSPeter Zijlstra 1079fdabdd0bSPeter Zijlstra elf->changed = true; 1080fdabdd0bSPeter Zijlstra 1081fdabdd0bSPeter Zijlstra return 0; 1082fdabdd0bSPeter Zijlstra } 1083fdabdd0bSPeter Zijlstra 10842b10be23SPeter Zijlstra int elf_write(struct elf *elf) 1085627fce14SJosh Poimboeuf { 1086627fce14SJosh Poimboeuf struct section *sec; 1087627fce14SJosh Poimboeuf Elf_Scn *s; 1088627fce14SJosh Poimboeuf 10893a647607SPeter Zijlstra /* Update changed relocation sections and section headers: */ 1090627fce14SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 1091627fce14SJosh Poimboeuf if (sec->changed) { 10923a647607SPeter Zijlstra if (sec->base && 10933a647607SPeter Zijlstra elf_rebuild_reloc_section(elf, sec)) { 10943a647607SPeter Zijlstra WARN("elf_rebuild_reloc_section"); 10953a647607SPeter Zijlstra return -1; 10963a647607SPeter Zijlstra } 10973a647607SPeter Zijlstra 1098627fce14SJosh Poimboeuf s = elf_getscn(elf->elf, sec->idx); 1099627fce14SJosh Poimboeuf if (!s) { 1100627fce14SJosh Poimboeuf WARN_ELF("elf_getscn"); 1101627fce14SJosh Poimboeuf return -1; 1102627fce14SJosh Poimboeuf } 1103627fce14SJosh Poimboeuf if (!gelf_update_shdr(s, &sec->sh)) { 1104627fce14SJosh Poimboeuf WARN_ELF("gelf_update_shdr"); 1105627fce14SJosh Poimboeuf return -1; 1106627fce14SJosh Poimboeuf } 11072b10be23SPeter Zijlstra 11082b10be23SPeter Zijlstra sec->changed = false; 11093a647607SPeter Zijlstra elf->changed = true; 1110627fce14SJosh Poimboeuf } 1111627fce14SJosh Poimboeuf } 1112627fce14SJosh Poimboeuf 111397dab2aeSJosh Poimboeuf /* Make sure the new section header entries get updated properly. */ 111497dab2aeSJosh Poimboeuf elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY); 111597dab2aeSJosh Poimboeuf 111697dab2aeSJosh Poimboeuf /* Write all changes to the file. */ 1117627fce14SJosh Poimboeuf if (elf_update(elf->elf, ELF_C_WRITE) < 0) { 1118627fce14SJosh Poimboeuf WARN_ELF("elf_update"); 1119627fce14SJosh Poimboeuf return -1; 1120627fce14SJosh Poimboeuf } 1121627fce14SJosh Poimboeuf 11222b10be23SPeter Zijlstra elf->changed = false; 11232b10be23SPeter Zijlstra 1124627fce14SJosh Poimboeuf return 0; 1125627fce14SJosh Poimboeuf } 1126627fce14SJosh Poimboeuf 1127442f04c3SJosh Poimboeuf void elf_close(struct elf *elf) 1128442f04c3SJosh Poimboeuf { 1129442f04c3SJosh Poimboeuf struct section *sec, *tmpsec; 1130442f04c3SJosh Poimboeuf struct symbol *sym, *tmpsym; 1131f1974222SMatt Helsley struct reloc *reloc, *tmpreloc; 1132442f04c3SJosh Poimboeuf 1133baa41469SJosh Poimboeuf if (elf->elf) 1134baa41469SJosh Poimboeuf elf_end(elf->elf); 1135baa41469SJosh Poimboeuf 1136baa41469SJosh Poimboeuf if (elf->fd > 0) 1137baa41469SJosh Poimboeuf close(elf->fd); 1138baa41469SJosh Poimboeuf 1139442f04c3SJosh Poimboeuf list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { 1140a196e171SJosh Poimboeuf list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { 1141442f04c3SJosh Poimboeuf list_del(&sym->list); 1142042ba73fSJosh Poimboeuf hash_del(&sym->hash); 1143442f04c3SJosh Poimboeuf free(sym); 1144442f04c3SJosh Poimboeuf } 1145f1974222SMatt Helsley list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) { 1146f1974222SMatt Helsley list_del(&reloc->list); 1147f1974222SMatt Helsley hash_del(&reloc->hash); 1148f1974222SMatt Helsley free(reloc); 1149442f04c3SJosh Poimboeuf } 1150442f04c3SJosh Poimboeuf list_del(&sec->list); 1151442f04c3SJosh Poimboeuf free(sec); 1152442f04c3SJosh Poimboeuf } 1153baa41469SJosh Poimboeuf 1154442f04c3SJosh Poimboeuf free(elf); 1155442f04c3SJosh Poimboeuf } 1156