11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2442f04c3SJosh Poimboeuf /* 3442f04c3SJosh Poimboeuf * elf.c - ELF access library 4442f04c3SJosh Poimboeuf * 5442f04c3SJosh Poimboeuf * Adapted from kpatch (https://github.com/dynup/kpatch): 6442f04c3SJosh Poimboeuf * Copyright (C) 2013-2015 Josh Poimboeuf <jpoimboe@redhat.com> 7442f04c3SJosh Poimboeuf * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com> 8442f04c3SJosh Poimboeuf */ 9442f04c3SJosh Poimboeuf 10442f04c3SJosh Poimboeuf #include <sys/types.h> 11442f04c3SJosh Poimboeuf #include <sys/stat.h> 12442f04c3SJosh Poimboeuf #include <fcntl.h> 13442f04c3SJosh Poimboeuf #include <stdio.h> 14442f04c3SJosh Poimboeuf #include <stdlib.h> 15442f04c3SJosh Poimboeuf #include <string.h> 16442f04c3SJosh Poimboeuf #include <unistd.h> 17385d11b1SJosh Poimboeuf #include <errno.h> 187786032eSVasily Gorbik #include <objtool/builtin.h> 19442f04c3SJosh Poimboeuf 207786032eSVasily Gorbik #include <objtool/elf.h> 217786032eSVasily Gorbik #include <objtool/warn.h> 22442f04c3SJosh Poimboeuf 2322566c16SArtem Savkov #define MAX_NAME_LEN 128 2422566c16SArtem Savkov 25ae358196SPeter Zijlstra static inline u32 str_hash(const char *str) 26ae358196SPeter Zijlstra { 27ae358196SPeter Zijlstra return jhash(str, strlen(str), 0); 28ae358196SPeter Zijlstra } 29ae358196SPeter Zijlstra 3034f7c96dSPeter Zijlstra static inline int elf_hash_bits(void) 3134f7c96dSPeter Zijlstra { 3234f7c96dSPeter Zijlstra return vmlinux ? ELF_HASH_BITS : 16; 3334f7c96dSPeter Zijlstra } 3434f7c96dSPeter Zijlstra 3534f7c96dSPeter Zijlstra #define elf_hash_add(hashtable, node, key) \ 3634f7c96dSPeter Zijlstra hlist_add_head(node, &hashtable[hash_min(key, elf_hash_bits())]) 3734f7c96dSPeter Zijlstra 3834f7c96dSPeter Zijlstra static void elf_hash_init(struct hlist_head *table) 3934f7c96dSPeter Zijlstra { 4034f7c96dSPeter Zijlstra __hash_init(table, 1U << elf_hash_bits()); 4134f7c96dSPeter Zijlstra } 4234f7c96dSPeter Zijlstra 4334f7c96dSPeter Zijlstra #define elf_hash_for_each_possible(name, obj, member, key) \ 4434f7c96dSPeter Zijlstra hlist_for_each_entry(obj, &name[hash_min(key, elf_hash_bits())], member) 4534f7c96dSPeter Zijlstra 462d24dd57SPeter Zijlstra static bool symbol_to_offset(struct rb_node *a, const struct rb_node *b) 472a362eccSPeter Zijlstra { 482a362eccSPeter Zijlstra struct symbol *sa = rb_entry(a, struct symbol, node); 492a362eccSPeter Zijlstra struct symbol *sb = rb_entry(b, struct symbol, node); 502a362eccSPeter Zijlstra 512a362eccSPeter Zijlstra if (sa->offset < sb->offset) 522d24dd57SPeter Zijlstra return true; 532a362eccSPeter Zijlstra if (sa->offset > sb->offset) 542d24dd57SPeter Zijlstra return false; 552a362eccSPeter Zijlstra 562a362eccSPeter Zijlstra if (sa->len < sb->len) 572d24dd57SPeter Zijlstra return true; 582a362eccSPeter Zijlstra if (sa->len > sb->len) 592d24dd57SPeter Zijlstra return false; 602a362eccSPeter Zijlstra 612a362eccSPeter Zijlstra sa->alias = sb; 622a362eccSPeter Zijlstra 632d24dd57SPeter Zijlstra return false; 642a362eccSPeter Zijlstra } 652a362eccSPeter Zijlstra 662a362eccSPeter Zijlstra static int symbol_by_offset(const void *key, const struct rb_node *node) 672a362eccSPeter Zijlstra { 682a362eccSPeter Zijlstra const struct symbol *s = rb_entry(node, struct symbol, node); 692a362eccSPeter Zijlstra const unsigned long *o = key; 702a362eccSPeter Zijlstra 712a362eccSPeter Zijlstra if (*o < s->offset) 722a362eccSPeter Zijlstra return -1; 735377cae9SJulien Thierry if (*o >= s->offset + s->len) 742a362eccSPeter Zijlstra return 1; 752a362eccSPeter Zijlstra 762a362eccSPeter Zijlstra return 0; 772a362eccSPeter Zijlstra } 782a362eccSPeter Zijlstra 79894e48caSIngo Molnar struct section *find_section_by_name(const struct elf *elf, const char *name) 80442f04c3SJosh Poimboeuf { 81442f04c3SJosh Poimboeuf struct section *sec; 82442f04c3SJosh Poimboeuf 8334f7c96dSPeter Zijlstra elf_hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name)) 84442f04c3SJosh Poimboeuf if (!strcmp(sec->name, name)) 85442f04c3SJosh Poimboeuf return sec; 86442f04c3SJosh Poimboeuf 87442f04c3SJosh Poimboeuf return NULL; 88442f04c3SJosh Poimboeuf } 89442f04c3SJosh Poimboeuf 90442f04c3SJosh Poimboeuf static struct section *find_section_by_index(struct elf *elf, 91442f04c3SJosh Poimboeuf unsigned int idx) 92442f04c3SJosh Poimboeuf { 93442f04c3SJosh Poimboeuf struct section *sec; 94442f04c3SJosh Poimboeuf 9534f7c96dSPeter Zijlstra elf_hash_for_each_possible(elf->section_hash, sec, hash, idx) 96442f04c3SJosh Poimboeuf if (sec->idx == idx) 97442f04c3SJosh Poimboeuf return sec; 98442f04c3SJosh Poimboeuf 99442f04c3SJosh Poimboeuf return NULL; 100442f04c3SJosh Poimboeuf } 101442f04c3SJosh Poimboeuf 102442f04c3SJosh Poimboeuf static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) 103442f04c3SJosh Poimboeuf { 104442f04c3SJosh Poimboeuf struct symbol *sym; 105442f04c3SJosh Poimboeuf 10634f7c96dSPeter Zijlstra elf_hash_for_each_possible(elf->symbol_hash, sym, hash, idx) 107442f04c3SJosh Poimboeuf if (sym->idx == idx) 108442f04c3SJosh Poimboeuf return sym; 109442f04c3SJosh Poimboeuf 110442f04c3SJosh Poimboeuf return NULL; 111442f04c3SJosh Poimboeuf } 112442f04c3SJosh Poimboeuf 113442f04c3SJosh Poimboeuf struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) 114442f04c3SJosh Poimboeuf { 1152a362eccSPeter Zijlstra struct rb_node *node; 116442f04c3SJosh Poimboeuf 1172d24dd57SPeter Zijlstra rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { 1182a362eccSPeter Zijlstra struct symbol *s = rb_entry(node, struct symbol, node); 1192a362eccSPeter Zijlstra 1202a362eccSPeter Zijlstra if (s->offset == offset && s->type != STT_SECTION) 1212a362eccSPeter Zijlstra return s; 1222a362eccSPeter Zijlstra } 1237acfe531SJosh Poimboeuf 1247acfe531SJosh Poimboeuf return NULL; 1257acfe531SJosh Poimboeuf } 1267acfe531SJosh Poimboeuf 1277acfe531SJosh Poimboeuf struct symbol *find_func_by_offset(struct section *sec, unsigned long offset) 1287acfe531SJosh Poimboeuf { 1292a362eccSPeter Zijlstra struct rb_node *node; 1307acfe531SJosh Poimboeuf 1312d24dd57SPeter Zijlstra rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { 1322a362eccSPeter Zijlstra struct symbol *s = rb_entry(node, struct symbol, node); 1332a362eccSPeter Zijlstra 1342a362eccSPeter Zijlstra if (s->offset == offset && s->type == STT_FUNC) 1352a362eccSPeter Zijlstra return s; 1362a362eccSPeter Zijlstra } 1372a362eccSPeter Zijlstra 1382a362eccSPeter Zijlstra return NULL; 1392a362eccSPeter Zijlstra } 1402a362eccSPeter Zijlstra 141b490f453SMiroslav Benes struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset) 1422a362eccSPeter Zijlstra { 1432a362eccSPeter Zijlstra struct rb_node *node; 1442a362eccSPeter Zijlstra 1452d24dd57SPeter Zijlstra rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { 1462a362eccSPeter Zijlstra struct symbol *s = rb_entry(node, struct symbol, node); 1472a362eccSPeter Zijlstra 1482a362eccSPeter Zijlstra if (s->type != STT_SECTION) 1492a362eccSPeter Zijlstra return s; 1502a362eccSPeter Zijlstra } 1512a362eccSPeter Zijlstra 1522a362eccSPeter Zijlstra return NULL; 1532a362eccSPeter Zijlstra } 1542a362eccSPeter Zijlstra 15553d20720SPeter Zijlstra struct symbol *find_func_containing(struct section *sec, unsigned long offset) 1562a362eccSPeter Zijlstra { 1572a362eccSPeter Zijlstra struct rb_node *node; 1582a362eccSPeter Zijlstra 1592d24dd57SPeter Zijlstra rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { 1602a362eccSPeter Zijlstra struct symbol *s = rb_entry(node, struct symbol, node); 1612a362eccSPeter Zijlstra 1622a362eccSPeter Zijlstra if (s->type == STT_FUNC) 1632a362eccSPeter Zijlstra return s; 1642a362eccSPeter Zijlstra } 165442f04c3SJosh Poimboeuf 166442f04c3SJosh Poimboeuf return NULL; 167442f04c3SJosh Poimboeuf } 168442f04c3SJosh Poimboeuf 169894e48caSIngo Molnar struct symbol *find_symbol_by_name(const struct elf *elf, const char *name) 17013810435SJosh Poimboeuf { 17113810435SJosh Poimboeuf struct symbol *sym; 17213810435SJosh Poimboeuf 17334f7c96dSPeter Zijlstra elf_hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name)) 17413810435SJosh Poimboeuf if (!strcmp(sym->name, name)) 17513810435SJosh Poimboeuf return sym; 17613810435SJosh Poimboeuf 17713810435SJosh Poimboeuf return NULL; 17813810435SJosh Poimboeuf } 17913810435SJosh Poimboeuf 180f1974222SMatt Helsley struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, 1818b5fa6bcSPeter Zijlstra unsigned long offset, unsigned int len) 182442f04c3SJosh Poimboeuf { 183f1974222SMatt Helsley struct reloc *reloc, *r = NULL; 184042ba73fSJosh Poimboeuf unsigned long o; 185442f04c3SJosh Poimboeuf 186f1974222SMatt Helsley if (!sec->reloc) 187442f04c3SJosh Poimboeuf return NULL; 188442f04c3SJosh Poimboeuf 189f1974222SMatt Helsley sec = sec->reloc; 1908b5fa6bcSPeter Zijlstra 19174b873e4SPeter Zijlstra for_offset_range(o, offset, offset + len) { 192f1974222SMatt Helsley elf_hash_for_each_possible(elf->reloc_hash, reloc, hash, 1938b5fa6bcSPeter Zijlstra sec_offset_hash(sec, o)) { 194f1974222SMatt Helsley if (reloc->sec != sec) 19574b873e4SPeter Zijlstra continue; 19674b873e4SPeter Zijlstra 197f1974222SMatt Helsley if (reloc->offset >= offset && reloc->offset < offset + len) { 198f1974222SMatt Helsley if (!r || reloc->offset < r->offset) 199f1974222SMatt Helsley r = reloc; 2008b5fa6bcSPeter Zijlstra } 2018b5fa6bcSPeter Zijlstra } 20274b873e4SPeter Zijlstra if (r) 20374b873e4SPeter Zijlstra return r; 20474b873e4SPeter Zijlstra } 205442f04c3SJosh Poimboeuf 206442f04c3SJosh Poimboeuf return NULL; 207442f04c3SJosh Poimboeuf } 208442f04c3SJosh Poimboeuf 209f1974222SMatt Helsley struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset) 210442f04c3SJosh Poimboeuf { 211f1974222SMatt Helsley return find_reloc_by_dest_range(elf, sec, offset, 1); 212442f04c3SJosh Poimboeuf } 213442f04c3SJosh Poimboeuf 214442f04c3SJosh Poimboeuf static int read_sections(struct elf *elf) 215442f04c3SJosh Poimboeuf { 216442f04c3SJosh Poimboeuf Elf_Scn *s = NULL; 217442f04c3SJosh Poimboeuf struct section *sec; 218442f04c3SJosh Poimboeuf size_t shstrndx, sections_nr; 219442f04c3SJosh Poimboeuf int i; 220442f04c3SJosh Poimboeuf 221442f04c3SJosh Poimboeuf if (elf_getshdrnum(elf->elf, §ions_nr)) { 222baa41469SJosh Poimboeuf WARN_ELF("elf_getshdrnum"); 223442f04c3SJosh Poimboeuf return -1; 224442f04c3SJosh Poimboeuf } 225442f04c3SJosh Poimboeuf 226442f04c3SJosh Poimboeuf if (elf_getshdrstrndx(elf->elf, &shstrndx)) { 227baa41469SJosh Poimboeuf WARN_ELF("elf_getshdrstrndx"); 228442f04c3SJosh Poimboeuf return -1; 229442f04c3SJosh Poimboeuf } 230442f04c3SJosh Poimboeuf 231442f04c3SJosh Poimboeuf for (i = 0; i < sections_nr; i++) { 232442f04c3SJosh Poimboeuf sec = malloc(sizeof(*sec)); 233442f04c3SJosh Poimboeuf if (!sec) { 234442f04c3SJosh Poimboeuf perror("malloc"); 235442f04c3SJosh Poimboeuf return -1; 236442f04c3SJosh Poimboeuf } 237442f04c3SJosh Poimboeuf memset(sec, 0, sizeof(*sec)); 238442f04c3SJosh Poimboeuf 239a196e171SJosh Poimboeuf INIT_LIST_HEAD(&sec->symbol_list); 240f1974222SMatt Helsley INIT_LIST_HEAD(&sec->reloc_list); 241442f04c3SJosh Poimboeuf 242442f04c3SJosh Poimboeuf s = elf_getscn(elf->elf, i); 243442f04c3SJosh Poimboeuf if (!s) { 244baa41469SJosh Poimboeuf WARN_ELF("elf_getscn"); 245442f04c3SJosh Poimboeuf return -1; 246442f04c3SJosh Poimboeuf } 247442f04c3SJosh Poimboeuf 248442f04c3SJosh Poimboeuf sec->idx = elf_ndxscn(s); 249442f04c3SJosh Poimboeuf 250442f04c3SJosh Poimboeuf if (!gelf_getshdr(s, &sec->sh)) { 251baa41469SJosh Poimboeuf WARN_ELF("gelf_getshdr"); 252442f04c3SJosh Poimboeuf return -1; 253442f04c3SJosh Poimboeuf } 254442f04c3SJosh Poimboeuf 255442f04c3SJosh Poimboeuf sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name); 256442f04c3SJosh Poimboeuf if (!sec->name) { 257baa41469SJosh Poimboeuf WARN_ELF("elf_strptr"); 258442f04c3SJosh Poimboeuf return -1; 259442f04c3SJosh Poimboeuf } 260442f04c3SJosh Poimboeuf 261df968c93SPetr Vandrovec if (sec->sh.sh_size != 0) { 262baa41469SJosh Poimboeuf sec->data = elf_getdata(s, NULL); 263baa41469SJosh Poimboeuf if (!sec->data) { 264baa41469SJosh Poimboeuf WARN_ELF("elf_getdata"); 265442f04c3SJosh Poimboeuf return -1; 266442f04c3SJosh Poimboeuf } 267baa41469SJosh Poimboeuf if (sec->data->d_off != 0 || 268baa41469SJosh Poimboeuf sec->data->d_size != sec->sh.sh_size) { 269df968c93SPetr Vandrovec WARN("unexpected data attributes for %s", 270df968c93SPetr Vandrovec sec->name); 271442f04c3SJosh Poimboeuf return -1; 272442f04c3SJosh Poimboeuf } 273df968c93SPetr Vandrovec } 274df968c93SPetr Vandrovec sec->len = sec->sh.sh_size; 27553038996SPeter Zijlstra 27653038996SPeter Zijlstra list_add_tail(&sec->list, &elf->sections); 27734f7c96dSPeter Zijlstra elf_hash_add(elf->section_hash, &sec->hash, sec->idx); 27834f7c96dSPeter Zijlstra elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name)); 279442f04c3SJosh Poimboeuf } 280442f04c3SJosh Poimboeuf 2811e11f3fdSPeter Zijlstra if (stats) 2821e11f3fdSPeter Zijlstra printf("nr_sections: %lu\n", (unsigned long)sections_nr); 2831e11f3fdSPeter Zijlstra 284442f04c3SJosh Poimboeuf /* sanity check, one more call to elf_nextscn() should return NULL */ 285442f04c3SJosh Poimboeuf if (elf_nextscn(elf->elf, s)) { 286442f04c3SJosh Poimboeuf WARN("section entry mismatch"); 287442f04c3SJosh Poimboeuf return -1; 288442f04c3SJosh Poimboeuf } 289442f04c3SJosh Poimboeuf 290442f04c3SJosh Poimboeuf return 0; 291442f04c3SJosh Poimboeuf } 292442f04c3SJosh Poimboeuf 293442f04c3SJosh Poimboeuf static int read_symbols(struct elf *elf) 294442f04c3SJosh Poimboeuf { 29528fe1d7bSSami Tolvanen struct section *symtab, *symtab_shndx, *sec; 2962a362eccSPeter Zijlstra struct symbol *sym, *pfunc; 2972a362eccSPeter Zijlstra struct list_head *entry; 2982a362eccSPeter Zijlstra struct rb_node *pnode; 299442f04c3SJosh Poimboeuf int symbols_nr, i; 30013810435SJosh Poimboeuf char *coldstr; 30128fe1d7bSSami Tolvanen Elf_Data *shndx_data = NULL; 30228fe1d7bSSami Tolvanen Elf32_Word shndx; 303442f04c3SJosh Poimboeuf 304442f04c3SJosh Poimboeuf symtab = find_section_by_name(elf, ".symtab"); 305442f04c3SJosh Poimboeuf if (!symtab) { 3061d489151SJosh Poimboeuf /* 3071d489151SJosh Poimboeuf * A missing symbol table is actually possible if it's an empty 3081d489151SJosh Poimboeuf * .o file. This can happen for thunk_64.o. 3091d489151SJosh Poimboeuf */ 3101d489151SJosh Poimboeuf return 0; 311442f04c3SJosh Poimboeuf } 312442f04c3SJosh Poimboeuf 31328fe1d7bSSami Tolvanen symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); 31428fe1d7bSSami Tolvanen if (symtab_shndx) 31528fe1d7bSSami Tolvanen shndx_data = symtab_shndx->data; 31628fe1d7bSSami Tolvanen 317442f04c3SJosh Poimboeuf symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize; 318442f04c3SJosh Poimboeuf 319442f04c3SJosh Poimboeuf for (i = 0; i < symbols_nr; i++) { 320442f04c3SJosh Poimboeuf sym = malloc(sizeof(*sym)); 321442f04c3SJosh Poimboeuf if (!sym) { 322442f04c3SJosh Poimboeuf perror("malloc"); 323442f04c3SJosh Poimboeuf return -1; 324442f04c3SJosh Poimboeuf } 325442f04c3SJosh Poimboeuf memset(sym, 0, sizeof(*sym)); 3262a362eccSPeter Zijlstra sym->alias = sym; 327442f04c3SJosh Poimboeuf 328442f04c3SJosh Poimboeuf sym->idx = i; 329442f04c3SJosh Poimboeuf 33028fe1d7bSSami Tolvanen if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym, 33128fe1d7bSSami Tolvanen &shndx)) { 33228fe1d7bSSami Tolvanen WARN_ELF("gelf_getsymshndx"); 333442f04c3SJosh Poimboeuf goto err; 334442f04c3SJosh Poimboeuf } 335442f04c3SJosh Poimboeuf 336442f04c3SJosh Poimboeuf sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, 337442f04c3SJosh Poimboeuf sym->sym.st_name); 338442f04c3SJosh Poimboeuf if (!sym->name) { 339baa41469SJosh Poimboeuf WARN_ELF("elf_strptr"); 340442f04c3SJosh Poimboeuf goto err; 341442f04c3SJosh Poimboeuf } 342442f04c3SJosh Poimboeuf 343442f04c3SJosh Poimboeuf sym->type = GELF_ST_TYPE(sym->sym.st_info); 344442f04c3SJosh Poimboeuf sym->bind = GELF_ST_BIND(sym->sym.st_info); 345442f04c3SJosh Poimboeuf 34628fe1d7bSSami Tolvanen if ((sym->sym.st_shndx > SHN_UNDEF && 34728fe1d7bSSami Tolvanen sym->sym.st_shndx < SHN_LORESERVE) || 34828fe1d7bSSami Tolvanen (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) { 34928fe1d7bSSami Tolvanen if (sym->sym.st_shndx != SHN_XINDEX) 35028fe1d7bSSami Tolvanen shndx = sym->sym.st_shndx; 35128fe1d7bSSami Tolvanen 35228fe1d7bSSami Tolvanen sym->sec = find_section_by_index(elf, shndx); 353442f04c3SJosh Poimboeuf if (!sym->sec) { 354442f04c3SJosh Poimboeuf WARN("couldn't find section for symbol %s", 355442f04c3SJosh Poimboeuf sym->name); 356442f04c3SJosh Poimboeuf goto err; 357442f04c3SJosh Poimboeuf } 358442f04c3SJosh Poimboeuf if (sym->type == STT_SECTION) { 359442f04c3SJosh Poimboeuf sym->name = sym->sec->name; 360442f04c3SJosh Poimboeuf sym->sec->sym = sym; 361442f04c3SJosh Poimboeuf } 362442f04c3SJosh Poimboeuf } else 363442f04c3SJosh Poimboeuf sym->sec = find_section_by_index(elf, 0); 364442f04c3SJosh Poimboeuf 365442f04c3SJosh Poimboeuf sym->offset = sym->sym.st_value; 366442f04c3SJosh Poimboeuf sym->len = sym->sym.st_size; 367442f04c3SJosh Poimboeuf 3682d24dd57SPeter Zijlstra rb_add(&sym->node, &sym->sec->symbol_tree, symbol_to_offset); 3692a362eccSPeter Zijlstra pnode = rb_prev(&sym->node); 3702a362eccSPeter Zijlstra if (pnode) 3712a362eccSPeter Zijlstra entry = &rb_entry(pnode, struct symbol, node)->list; 3722a362eccSPeter Zijlstra else 373a196e171SJosh Poimboeuf entry = &sym->sec->symbol_list; 374442f04c3SJosh Poimboeuf list_add(&sym->list, entry); 37534f7c96dSPeter Zijlstra elf_hash_add(elf->symbol_hash, &sym->hash, sym->idx); 37634f7c96dSPeter Zijlstra elf_hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name)); 377a2e38dffSJosh Poimboeuf 378a2e38dffSJosh Poimboeuf /* 379a2e38dffSJosh Poimboeuf * Don't store empty STT_NOTYPE symbols in the rbtree. They 380a2e38dffSJosh Poimboeuf * can exist within a function, confusing the sorting. 381a2e38dffSJosh Poimboeuf */ 382a2e38dffSJosh Poimboeuf if (!sym->len) 383a2e38dffSJosh Poimboeuf rb_erase(&sym->node, &sym->sec->symbol_tree); 384442f04c3SJosh Poimboeuf } 385442f04c3SJosh Poimboeuf 3861e11f3fdSPeter Zijlstra if (stats) 3871e11f3fdSPeter Zijlstra printf("nr_symbols: %lu\n", (unsigned long)symbols_nr); 3881e11f3fdSPeter Zijlstra 38913810435SJosh Poimboeuf /* Create parent/child links for any cold subfunctions */ 39013810435SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 39113810435SJosh Poimboeuf list_for_each_entry(sym, &sec->symbol_list, list) { 39222566c16SArtem Savkov char pname[MAX_NAME_LEN + 1]; 39322566c16SArtem Savkov size_t pnamelen; 39413810435SJosh Poimboeuf if (sym->type != STT_FUNC) 39513810435SJosh Poimboeuf continue; 396e000acc1SKristen Carlson Accardi 397e000acc1SKristen Carlson Accardi if (sym->pfunc == NULL) 398e000acc1SKristen Carlson Accardi sym->pfunc = sym; 399e000acc1SKristen Carlson Accardi 400e000acc1SKristen Carlson Accardi if (sym->cfunc == NULL) 401e000acc1SKristen Carlson Accardi sym->cfunc = sym; 402e000acc1SKristen Carlson Accardi 403bcb6fb5dSJosh Poimboeuf coldstr = strstr(sym->name, ".cold"); 40408b393d0SJosh Poimboeuf if (!coldstr) 40508b393d0SJosh Poimboeuf continue; 40608b393d0SJosh Poimboeuf 40722566c16SArtem Savkov pnamelen = coldstr - sym->name; 40822566c16SArtem Savkov if (pnamelen > MAX_NAME_LEN) { 40922566c16SArtem Savkov WARN("%s(): parent function name exceeds maximum length of %d characters", 41022566c16SArtem Savkov sym->name, MAX_NAME_LEN); 41122566c16SArtem Savkov return -1; 41222566c16SArtem Savkov } 41322566c16SArtem Savkov 41422566c16SArtem Savkov strncpy(pname, sym->name, pnamelen); 41522566c16SArtem Savkov pname[pnamelen] = '\0'; 41622566c16SArtem Savkov pfunc = find_symbol_by_name(elf, pname); 41713810435SJosh Poimboeuf 41813810435SJosh Poimboeuf if (!pfunc) { 41913810435SJosh Poimboeuf WARN("%s(): can't find parent function", 42013810435SJosh Poimboeuf sym->name); 4210b9301fbSArtem Savkov return -1; 42213810435SJosh Poimboeuf } 42313810435SJosh Poimboeuf 42413810435SJosh Poimboeuf sym->pfunc = pfunc; 42513810435SJosh Poimboeuf pfunc->cfunc = sym; 42608b393d0SJosh Poimboeuf 42708b393d0SJosh Poimboeuf /* 42808b393d0SJosh Poimboeuf * Unfortunately, -fnoreorder-functions puts the child 42908b393d0SJosh Poimboeuf * inside the parent. Remove the overlap so we can 43008b393d0SJosh Poimboeuf * have sane assumptions. 43108b393d0SJosh Poimboeuf * 43208b393d0SJosh Poimboeuf * Note that pfunc->len now no longer matches 43308b393d0SJosh Poimboeuf * pfunc->sym.st_size. 43408b393d0SJosh Poimboeuf */ 43508b393d0SJosh Poimboeuf if (sym->sec == pfunc->sec && 43608b393d0SJosh Poimboeuf sym->offset >= pfunc->offset && 43708b393d0SJosh Poimboeuf sym->offset + sym->len == pfunc->offset + pfunc->len) { 43808b393d0SJosh Poimboeuf pfunc->len -= sym->len; 43913810435SJosh Poimboeuf } 44013810435SJosh Poimboeuf } 44113810435SJosh Poimboeuf } 44213810435SJosh Poimboeuf 443442f04c3SJosh Poimboeuf return 0; 444442f04c3SJosh Poimboeuf 445442f04c3SJosh Poimboeuf err: 446442f04c3SJosh Poimboeuf free(sym); 447442f04c3SJosh Poimboeuf return -1; 448442f04c3SJosh Poimboeuf } 449442f04c3SJosh Poimboeuf 450*d0c5c4ccSPeter Zijlstra static struct section *elf_create_reloc_section(struct elf *elf, 451*d0c5c4ccSPeter Zijlstra struct section *base, 452*d0c5c4ccSPeter Zijlstra int reltype); 453*d0c5c4ccSPeter Zijlstra 454ef47cc01SPeter Zijlstra int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset, 455ef47cc01SPeter Zijlstra unsigned int type, struct symbol *sym, int addend) 45634f7c96dSPeter Zijlstra { 457ef47cc01SPeter Zijlstra struct reloc *reloc; 45834f7c96dSPeter Zijlstra 459*d0c5c4ccSPeter Zijlstra if (!sec->reloc && !elf_create_reloc_section(elf, sec, SHT_RELA)) 460*d0c5c4ccSPeter Zijlstra return -1; 461*d0c5c4ccSPeter Zijlstra 462ef47cc01SPeter Zijlstra reloc = malloc(sizeof(*reloc)); 463ef47cc01SPeter Zijlstra if (!reloc) { 464ef47cc01SPeter Zijlstra perror("malloc"); 465ef47cc01SPeter Zijlstra return -1; 466ef47cc01SPeter Zijlstra } 467ef47cc01SPeter Zijlstra memset(reloc, 0, sizeof(*reloc)); 468ef47cc01SPeter Zijlstra 469ef47cc01SPeter Zijlstra reloc->sec = sec->reloc; 470ef47cc01SPeter Zijlstra reloc->offset = offset; 471ef47cc01SPeter Zijlstra reloc->type = type; 472ef47cc01SPeter Zijlstra reloc->sym = sym; 473ef47cc01SPeter Zijlstra reloc->addend = addend; 474ef47cc01SPeter Zijlstra 475ef47cc01SPeter Zijlstra list_add_tail(&reloc->list, &sec->reloc->reloc_list); 476f1974222SMatt Helsley elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc)); 4773a647607SPeter Zijlstra 478ef47cc01SPeter Zijlstra sec->reloc->changed = true; 479ef47cc01SPeter Zijlstra 480ef47cc01SPeter Zijlstra return 0; 481ef47cc01SPeter Zijlstra } 482ef47cc01SPeter Zijlstra 483ef47cc01SPeter Zijlstra int elf_add_reloc_to_insn(struct elf *elf, struct section *sec, 484ef47cc01SPeter Zijlstra unsigned long offset, unsigned int type, 485ef47cc01SPeter Zijlstra struct section *insn_sec, unsigned long insn_off) 486ef47cc01SPeter Zijlstra { 487ef47cc01SPeter Zijlstra struct symbol *sym; 488ef47cc01SPeter Zijlstra int addend; 489ef47cc01SPeter Zijlstra 490ef47cc01SPeter Zijlstra if (insn_sec->sym) { 491ef47cc01SPeter Zijlstra sym = insn_sec->sym; 492ef47cc01SPeter Zijlstra addend = insn_off; 493ef47cc01SPeter Zijlstra 494ef47cc01SPeter Zijlstra } else { 495ef47cc01SPeter Zijlstra /* 496ef47cc01SPeter Zijlstra * The Clang assembler strips section symbols, so we have to 497ef47cc01SPeter Zijlstra * reference the function symbol instead: 498ef47cc01SPeter Zijlstra */ 499ef47cc01SPeter Zijlstra sym = find_symbol_containing(insn_sec, insn_off); 500ef47cc01SPeter Zijlstra if (!sym) { 501ef47cc01SPeter Zijlstra /* 502ef47cc01SPeter Zijlstra * Hack alert. This happens when we need to reference 503ef47cc01SPeter Zijlstra * the NOP pad insn immediately after the function. 504ef47cc01SPeter Zijlstra */ 505ef47cc01SPeter Zijlstra sym = find_symbol_containing(insn_sec, insn_off - 1); 506ef47cc01SPeter Zijlstra } 507ef47cc01SPeter Zijlstra 508ef47cc01SPeter Zijlstra if (!sym) { 509ef47cc01SPeter Zijlstra WARN("can't find symbol containing %s+0x%lx", insn_sec->name, insn_off); 510ef47cc01SPeter Zijlstra return -1; 511ef47cc01SPeter Zijlstra } 512ef47cc01SPeter Zijlstra 513ef47cc01SPeter Zijlstra addend = insn_off - sym->offset; 514ef47cc01SPeter Zijlstra } 515ef47cc01SPeter Zijlstra 516ef47cc01SPeter Zijlstra return elf_add_reloc(elf, sec, offset, type, sym, addend); 51734f7c96dSPeter Zijlstra } 51834f7c96dSPeter Zijlstra 519fb414783SMatt Helsley static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) 520fb414783SMatt Helsley { 521fb414783SMatt Helsley if (!gelf_getrel(sec->data, i, &reloc->rel)) { 522fb414783SMatt Helsley WARN_ELF("gelf_getrel"); 523fb414783SMatt Helsley return -1; 524fb414783SMatt Helsley } 525fb414783SMatt Helsley reloc->type = GELF_R_TYPE(reloc->rel.r_info); 526fb414783SMatt Helsley reloc->addend = 0; 527fb414783SMatt Helsley reloc->offset = reloc->rel.r_offset; 528fb414783SMatt Helsley *symndx = GELF_R_SYM(reloc->rel.r_info); 529fb414783SMatt Helsley return 0; 530fb414783SMatt Helsley } 531fb414783SMatt Helsley 532fb414783SMatt Helsley static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) 533fb414783SMatt Helsley { 534fb414783SMatt Helsley if (!gelf_getrela(sec->data, i, &reloc->rela)) { 535fb414783SMatt Helsley WARN_ELF("gelf_getrela"); 536fb414783SMatt Helsley return -1; 537fb414783SMatt Helsley } 538fb414783SMatt Helsley reloc->type = GELF_R_TYPE(reloc->rela.r_info); 539fb414783SMatt Helsley reloc->addend = reloc->rela.r_addend; 540fb414783SMatt Helsley reloc->offset = reloc->rela.r_offset; 541fb414783SMatt Helsley *symndx = GELF_R_SYM(reloc->rela.r_info); 542fb414783SMatt Helsley return 0; 543fb414783SMatt Helsley } 544fb414783SMatt Helsley 545f1974222SMatt Helsley static int read_relocs(struct elf *elf) 546442f04c3SJosh Poimboeuf { 547442f04c3SJosh Poimboeuf struct section *sec; 548f1974222SMatt Helsley struct reloc *reloc; 549442f04c3SJosh Poimboeuf int i; 550442f04c3SJosh Poimboeuf unsigned int symndx; 551f1974222SMatt Helsley unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0; 552442f04c3SJosh Poimboeuf 553442f04c3SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 554fb414783SMatt Helsley if ((sec->sh.sh_type != SHT_RELA) && 555fb414783SMatt Helsley (sec->sh.sh_type != SHT_REL)) 556442f04c3SJosh Poimboeuf continue; 557442f04c3SJosh Poimboeuf 5581e968bf5SSami Tolvanen sec->base = find_section_by_index(elf, sec->sh.sh_info); 559442f04c3SJosh Poimboeuf if (!sec->base) { 560f1974222SMatt Helsley WARN("can't find base section for reloc section %s", 561442f04c3SJosh Poimboeuf sec->name); 562442f04c3SJosh Poimboeuf return -1; 563442f04c3SJosh Poimboeuf } 564442f04c3SJosh Poimboeuf 565f1974222SMatt Helsley sec->base->reloc = sec; 566442f04c3SJosh Poimboeuf 567f1974222SMatt Helsley nr_reloc = 0; 568442f04c3SJosh Poimboeuf for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { 569f1974222SMatt Helsley reloc = malloc(sizeof(*reloc)); 570f1974222SMatt Helsley if (!reloc) { 571442f04c3SJosh Poimboeuf perror("malloc"); 572442f04c3SJosh Poimboeuf return -1; 573442f04c3SJosh Poimboeuf } 574f1974222SMatt Helsley memset(reloc, 0, sizeof(*reloc)); 575fb414783SMatt Helsley switch (sec->sh.sh_type) { 576fb414783SMatt Helsley case SHT_REL: 577fb414783SMatt Helsley if (read_rel_reloc(sec, i, reloc, &symndx)) 578442f04c3SJosh Poimboeuf return -1; 579fb414783SMatt Helsley break; 580fb414783SMatt Helsley case SHT_RELA: 581fb414783SMatt Helsley if (read_rela_reloc(sec, i, reloc, &symndx)) 582fb414783SMatt Helsley return -1; 583fb414783SMatt Helsley break; 584fb414783SMatt Helsley default: return -1; 585442f04c3SJosh Poimboeuf } 586442f04c3SJosh Poimboeuf 587f1974222SMatt Helsley reloc->sec = sec; 588d832c005SPeter Zijlstra reloc->idx = i; 589d832c005SPeter Zijlstra reloc->sym = find_symbol_by_index(elf, symndx); 590f1974222SMatt Helsley if (!reloc->sym) { 591f1974222SMatt Helsley WARN("can't find reloc entry symbol %d for %s", 592442f04c3SJosh Poimboeuf symndx, sec->name); 593442f04c3SJosh Poimboeuf return -1; 594442f04c3SJosh Poimboeuf } 595042ba73fSJosh Poimboeuf 5963a647607SPeter Zijlstra list_add_tail(&reloc->list, &sec->reloc_list); 5973a647607SPeter Zijlstra elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc)); 5983a647607SPeter Zijlstra 599f1974222SMatt Helsley nr_reloc++; 600442f04c3SJosh Poimboeuf } 601f1974222SMatt Helsley max_reloc = max(max_reloc, nr_reloc); 602f1974222SMatt Helsley tot_reloc += nr_reloc; 6031e11f3fdSPeter Zijlstra } 6041e11f3fdSPeter Zijlstra 6051e11f3fdSPeter Zijlstra if (stats) { 606f1974222SMatt Helsley printf("max_reloc: %lu\n", max_reloc); 607f1974222SMatt Helsley printf("tot_reloc: %lu\n", tot_reloc); 608442f04c3SJosh Poimboeuf } 609442f04c3SJosh Poimboeuf 610442f04c3SJosh Poimboeuf return 0; 611442f04c3SJosh Poimboeuf } 612442f04c3SJosh Poimboeuf 613bc359ff2SIngo Molnar struct elf *elf_open_read(const char *name, int flags) 614442f04c3SJosh Poimboeuf { 615442f04c3SJosh Poimboeuf struct elf *elf; 616627fce14SJosh Poimboeuf Elf_Cmd cmd; 617442f04c3SJosh Poimboeuf 618442f04c3SJosh Poimboeuf elf_version(EV_CURRENT); 619442f04c3SJosh Poimboeuf 620442f04c3SJosh Poimboeuf elf = malloc(sizeof(*elf)); 621442f04c3SJosh Poimboeuf if (!elf) { 622442f04c3SJosh Poimboeuf perror("malloc"); 623442f04c3SJosh Poimboeuf return NULL; 624442f04c3SJosh Poimboeuf } 62534f7c96dSPeter Zijlstra memset(elf, 0, offsetof(struct elf, sections)); 626442f04c3SJosh Poimboeuf 627442f04c3SJosh Poimboeuf INIT_LIST_HEAD(&elf->sections); 628442f04c3SJosh Poimboeuf 62934f7c96dSPeter Zijlstra elf_hash_init(elf->symbol_hash); 63034f7c96dSPeter Zijlstra elf_hash_init(elf->symbol_name_hash); 63134f7c96dSPeter Zijlstra elf_hash_init(elf->section_hash); 63234f7c96dSPeter Zijlstra elf_hash_init(elf->section_name_hash); 633f1974222SMatt Helsley elf_hash_init(elf->reloc_hash); 63434f7c96dSPeter Zijlstra 635627fce14SJosh Poimboeuf elf->fd = open(name, flags); 636442f04c3SJosh Poimboeuf if (elf->fd == -1) { 637385d11b1SJosh Poimboeuf fprintf(stderr, "objtool: Can't open '%s': %s\n", 638385d11b1SJosh Poimboeuf name, strerror(errno)); 639442f04c3SJosh Poimboeuf goto err; 640442f04c3SJosh Poimboeuf } 641442f04c3SJosh Poimboeuf 642627fce14SJosh Poimboeuf if ((flags & O_ACCMODE) == O_RDONLY) 643627fce14SJosh Poimboeuf cmd = ELF_C_READ_MMAP; 644627fce14SJosh Poimboeuf else if ((flags & O_ACCMODE) == O_RDWR) 645627fce14SJosh Poimboeuf cmd = ELF_C_RDWR; 646627fce14SJosh Poimboeuf else /* O_WRONLY */ 647627fce14SJosh Poimboeuf cmd = ELF_C_WRITE; 648627fce14SJosh Poimboeuf 649627fce14SJosh Poimboeuf elf->elf = elf_begin(elf->fd, cmd, NULL); 650442f04c3SJosh Poimboeuf if (!elf->elf) { 651baa41469SJosh Poimboeuf WARN_ELF("elf_begin"); 652442f04c3SJosh Poimboeuf goto err; 653442f04c3SJosh Poimboeuf } 654442f04c3SJosh Poimboeuf 655442f04c3SJosh Poimboeuf if (!gelf_getehdr(elf->elf, &elf->ehdr)) { 656baa41469SJosh Poimboeuf WARN_ELF("gelf_getehdr"); 657442f04c3SJosh Poimboeuf goto err; 658442f04c3SJosh Poimboeuf } 659442f04c3SJosh Poimboeuf 660442f04c3SJosh Poimboeuf if (read_sections(elf)) 661442f04c3SJosh Poimboeuf goto err; 662442f04c3SJosh Poimboeuf 663442f04c3SJosh Poimboeuf if (read_symbols(elf)) 664442f04c3SJosh Poimboeuf goto err; 665442f04c3SJosh Poimboeuf 666f1974222SMatt Helsley if (read_relocs(elf)) 667442f04c3SJosh Poimboeuf goto err; 668442f04c3SJosh Poimboeuf 669442f04c3SJosh Poimboeuf return elf; 670442f04c3SJosh Poimboeuf 671442f04c3SJosh Poimboeuf err: 672442f04c3SJosh Poimboeuf elf_close(elf); 673442f04c3SJosh Poimboeuf return NULL; 674442f04c3SJosh Poimboeuf } 675442f04c3SJosh Poimboeuf 676627fce14SJosh Poimboeuf struct section *elf_create_section(struct elf *elf, const char *name, 6771e7e4788SJosh Poimboeuf unsigned int sh_flags, size_t entsize, int nr) 678627fce14SJosh Poimboeuf { 679627fce14SJosh Poimboeuf struct section *sec, *shstrtab; 680627fce14SJosh Poimboeuf size_t size = entsize * nr; 6813c3ea503SMichael Forney Elf_Scn *s; 682627fce14SJosh Poimboeuf Elf_Data *data; 683627fce14SJosh Poimboeuf 684627fce14SJosh Poimboeuf sec = malloc(sizeof(*sec)); 685627fce14SJosh Poimboeuf if (!sec) { 686627fce14SJosh Poimboeuf perror("malloc"); 687627fce14SJosh Poimboeuf return NULL; 688627fce14SJosh Poimboeuf } 689627fce14SJosh Poimboeuf memset(sec, 0, sizeof(*sec)); 690627fce14SJosh Poimboeuf 691627fce14SJosh Poimboeuf INIT_LIST_HEAD(&sec->symbol_list); 692f1974222SMatt Helsley INIT_LIST_HEAD(&sec->reloc_list); 693627fce14SJosh Poimboeuf 694627fce14SJosh Poimboeuf s = elf_newscn(elf->elf); 695627fce14SJosh Poimboeuf if (!s) { 696627fce14SJosh Poimboeuf WARN_ELF("elf_newscn"); 697627fce14SJosh Poimboeuf return NULL; 698627fce14SJosh Poimboeuf } 699627fce14SJosh Poimboeuf 700627fce14SJosh Poimboeuf sec->name = strdup(name); 701627fce14SJosh Poimboeuf if (!sec->name) { 702627fce14SJosh Poimboeuf perror("strdup"); 703627fce14SJosh Poimboeuf return NULL; 704627fce14SJosh Poimboeuf } 705627fce14SJosh Poimboeuf 706627fce14SJosh Poimboeuf sec->idx = elf_ndxscn(s); 707627fce14SJosh Poimboeuf sec->len = size; 708627fce14SJosh Poimboeuf sec->changed = true; 709627fce14SJosh Poimboeuf 710627fce14SJosh Poimboeuf sec->data = elf_newdata(s); 711627fce14SJosh Poimboeuf if (!sec->data) { 712627fce14SJosh Poimboeuf WARN_ELF("elf_newdata"); 713627fce14SJosh Poimboeuf return NULL; 714627fce14SJosh Poimboeuf } 715627fce14SJosh Poimboeuf 716627fce14SJosh Poimboeuf sec->data->d_size = size; 717627fce14SJosh Poimboeuf sec->data->d_align = 1; 718627fce14SJosh Poimboeuf 719627fce14SJosh Poimboeuf if (size) { 720627fce14SJosh Poimboeuf sec->data->d_buf = malloc(size); 721627fce14SJosh Poimboeuf if (!sec->data->d_buf) { 722627fce14SJosh Poimboeuf perror("malloc"); 723627fce14SJosh Poimboeuf return NULL; 724627fce14SJosh Poimboeuf } 725627fce14SJosh Poimboeuf memset(sec->data->d_buf, 0, size); 726627fce14SJosh Poimboeuf } 727627fce14SJosh Poimboeuf 728627fce14SJosh Poimboeuf if (!gelf_getshdr(s, &sec->sh)) { 729627fce14SJosh Poimboeuf WARN_ELF("gelf_getshdr"); 730627fce14SJosh Poimboeuf return NULL; 731627fce14SJosh Poimboeuf } 732627fce14SJosh Poimboeuf 733627fce14SJosh Poimboeuf sec->sh.sh_size = size; 734627fce14SJosh Poimboeuf sec->sh.sh_entsize = entsize; 735627fce14SJosh Poimboeuf sec->sh.sh_type = SHT_PROGBITS; 736627fce14SJosh Poimboeuf sec->sh.sh_addralign = 1; 7371e7e4788SJosh Poimboeuf sec->sh.sh_flags = SHF_ALLOC | sh_flags; 738627fce14SJosh Poimboeuf 739627fce14SJosh Poimboeuf 7406d77d3b4SSimon Ser /* Add section name to .shstrtab (or .strtab for Clang) */ 741627fce14SJosh Poimboeuf shstrtab = find_section_by_name(elf, ".shstrtab"); 7426d77d3b4SSimon Ser if (!shstrtab) 7436d77d3b4SSimon Ser shstrtab = find_section_by_name(elf, ".strtab"); 744627fce14SJosh Poimboeuf if (!shstrtab) { 7456d77d3b4SSimon Ser WARN("can't find .shstrtab or .strtab section"); 746627fce14SJosh Poimboeuf return NULL; 747627fce14SJosh Poimboeuf } 748627fce14SJosh Poimboeuf 749627fce14SJosh Poimboeuf s = elf_getscn(elf->elf, shstrtab->idx); 750627fce14SJosh Poimboeuf if (!s) { 751627fce14SJosh Poimboeuf WARN_ELF("elf_getscn"); 752627fce14SJosh Poimboeuf return NULL; 753627fce14SJosh Poimboeuf } 754627fce14SJosh Poimboeuf 755627fce14SJosh Poimboeuf data = elf_newdata(s); 756627fce14SJosh Poimboeuf if (!data) { 757627fce14SJosh Poimboeuf WARN_ELF("elf_newdata"); 758627fce14SJosh Poimboeuf return NULL; 759627fce14SJosh Poimboeuf } 760627fce14SJosh Poimboeuf 761627fce14SJosh Poimboeuf data->d_buf = sec->name; 762627fce14SJosh Poimboeuf data->d_size = strlen(name) + 1; 763627fce14SJosh Poimboeuf data->d_align = 1; 764627fce14SJosh Poimboeuf 765627fce14SJosh Poimboeuf sec->sh.sh_name = shstrtab->len; 766627fce14SJosh Poimboeuf 767627fce14SJosh Poimboeuf shstrtab->len += strlen(name) + 1; 768627fce14SJosh Poimboeuf shstrtab->changed = true; 769627fce14SJosh Poimboeuf 77053038996SPeter Zijlstra list_add_tail(&sec->list, &elf->sections); 77134f7c96dSPeter Zijlstra elf_hash_add(elf->section_hash, &sec->hash, sec->idx); 77234f7c96dSPeter Zijlstra elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name)); 77353038996SPeter Zijlstra 7742b10be23SPeter Zijlstra elf->changed = true; 7752b10be23SPeter Zijlstra 776627fce14SJosh Poimboeuf return sec; 777627fce14SJosh Poimboeuf } 778627fce14SJosh Poimboeuf 779fb414783SMatt Helsley static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base) 780fb414783SMatt Helsley { 781fb414783SMatt Helsley char *relocname; 782fb414783SMatt Helsley struct section *sec; 783fb414783SMatt Helsley 784fb414783SMatt Helsley relocname = malloc(strlen(base->name) + strlen(".rel") + 1); 785fb414783SMatt Helsley if (!relocname) { 786fb414783SMatt Helsley perror("malloc"); 787fb414783SMatt Helsley return NULL; 788fb414783SMatt Helsley } 789fb414783SMatt Helsley strcpy(relocname, ".rel"); 790fb414783SMatt Helsley strcat(relocname, base->name); 791fb414783SMatt Helsley 7921e7e4788SJosh Poimboeuf sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rel), 0); 793fb414783SMatt Helsley free(relocname); 794fb414783SMatt Helsley if (!sec) 795fb414783SMatt Helsley return NULL; 796fb414783SMatt Helsley 797fb414783SMatt Helsley base->reloc = sec; 798fb414783SMatt Helsley sec->base = base; 799fb414783SMatt Helsley 800fb414783SMatt Helsley sec->sh.sh_type = SHT_REL; 801fb414783SMatt Helsley sec->sh.sh_addralign = 8; 802fb414783SMatt Helsley sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; 803fb414783SMatt Helsley sec->sh.sh_info = base->idx; 804fb414783SMatt Helsley sec->sh.sh_flags = SHF_INFO_LINK; 805fb414783SMatt Helsley 806fb414783SMatt Helsley return sec; 807fb414783SMatt Helsley } 808fb414783SMatt Helsley 809fb414783SMatt Helsley static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base) 810627fce14SJosh Poimboeuf { 811f1974222SMatt Helsley char *relocname; 812627fce14SJosh Poimboeuf struct section *sec; 813627fce14SJosh Poimboeuf 814f1974222SMatt Helsley relocname = malloc(strlen(base->name) + strlen(".rela") + 1); 815f1974222SMatt Helsley if (!relocname) { 816627fce14SJosh Poimboeuf perror("malloc"); 817627fce14SJosh Poimboeuf return NULL; 818627fce14SJosh Poimboeuf } 819f1974222SMatt Helsley strcpy(relocname, ".rela"); 820f1974222SMatt Helsley strcat(relocname, base->name); 821627fce14SJosh Poimboeuf 8221e7e4788SJosh Poimboeuf sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rela), 0); 823f1974222SMatt Helsley free(relocname); 824627fce14SJosh Poimboeuf if (!sec) 825627fce14SJosh Poimboeuf return NULL; 826627fce14SJosh Poimboeuf 827f1974222SMatt Helsley base->reloc = sec; 828627fce14SJosh Poimboeuf sec->base = base; 829627fce14SJosh Poimboeuf 830627fce14SJosh Poimboeuf sec->sh.sh_type = SHT_RELA; 831627fce14SJosh Poimboeuf sec->sh.sh_addralign = 8; 832627fce14SJosh Poimboeuf sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; 833627fce14SJosh Poimboeuf sec->sh.sh_info = base->idx; 834627fce14SJosh Poimboeuf sec->sh.sh_flags = SHF_INFO_LINK; 835627fce14SJosh Poimboeuf 836627fce14SJosh Poimboeuf return sec; 837627fce14SJosh Poimboeuf } 838627fce14SJosh Poimboeuf 839*d0c5c4ccSPeter Zijlstra static struct section *elf_create_reloc_section(struct elf *elf, 840fb414783SMatt Helsley struct section *base, 841fb414783SMatt Helsley int reltype) 842fb414783SMatt Helsley { 843fb414783SMatt Helsley switch (reltype) { 844fb414783SMatt Helsley case SHT_REL: return elf_create_rel_reloc_section(elf, base); 845fb414783SMatt Helsley case SHT_RELA: return elf_create_rela_reloc_section(elf, base); 846fb414783SMatt Helsley default: return NULL; 847fb414783SMatt Helsley } 848fb414783SMatt Helsley } 849fb414783SMatt Helsley 850fb414783SMatt Helsley static int elf_rebuild_rel_reloc_section(struct section *sec, int nr) 851627fce14SJosh Poimboeuf { 852f1974222SMatt Helsley struct reloc *reloc; 853fb414783SMatt Helsley int idx = 0, size; 854a1a664ecSMartin Schwidefsky void *buf; 855fb414783SMatt Helsley 856fb414783SMatt Helsley /* Allocate a buffer for relocations */ 857a1a664ecSMartin Schwidefsky size = nr * sizeof(GElf_Rel); 858a1a664ecSMartin Schwidefsky buf = malloc(size); 859a1a664ecSMartin Schwidefsky if (!buf) { 860fb414783SMatt Helsley perror("malloc"); 861fb414783SMatt Helsley return -1; 862fb414783SMatt Helsley } 863fb414783SMatt Helsley 864a1a664ecSMartin Schwidefsky sec->data->d_buf = buf; 865fb414783SMatt Helsley sec->data->d_size = size; 866a1a664ecSMartin Schwidefsky sec->data->d_type = ELF_T_REL; 867fb414783SMatt Helsley 868fb414783SMatt Helsley sec->sh.sh_size = size; 869fb414783SMatt Helsley 870fb414783SMatt Helsley idx = 0; 871fb414783SMatt Helsley list_for_each_entry(reloc, &sec->reloc_list, list) { 872a1a664ecSMartin Schwidefsky reloc->rel.r_offset = reloc->offset; 873a1a664ecSMartin Schwidefsky reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 874a1a664ecSMartin Schwidefsky gelf_update_rel(sec->data, idx, &reloc->rel); 875fb414783SMatt Helsley idx++; 876fb414783SMatt Helsley } 877fb414783SMatt Helsley 878fb414783SMatt Helsley return 0; 879fb414783SMatt Helsley } 880fb414783SMatt Helsley 881fb414783SMatt Helsley static int elf_rebuild_rela_reloc_section(struct section *sec, int nr) 882fb414783SMatt Helsley { 883fb414783SMatt Helsley struct reloc *reloc; 884fb414783SMatt Helsley int idx = 0, size; 885a1a664ecSMartin Schwidefsky void *buf; 886627fce14SJosh Poimboeuf 887fb414783SMatt Helsley /* Allocate a buffer for relocations with addends */ 888a1a664ecSMartin Schwidefsky size = nr * sizeof(GElf_Rela); 889a1a664ecSMartin Schwidefsky buf = malloc(size); 890a1a664ecSMartin Schwidefsky if (!buf) { 891627fce14SJosh Poimboeuf perror("malloc"); 892627fce14SJosh Poimboeuf return -1; 893627fce14SJosh Poimboeuf } 894627fce14SJosh Poimboeuf 895a1a664ecSMartin Schwidefsky sec->data->d_buf = buf; 896627fce14SJosh Poimboeuf sec->data->d_size = size; 897a1a664ecSMartin Schwidefsky sec->data->d_type = ELF_T_RELA; 898627fce14SJosh Poimboeuf 899627fce14SJosh Poimboeuf sec->sh.sh_size = size; 900627fce14SJosh Poimboeuf 901627fce14SJosh Poimboeuf idx = 0; 902f1974222SMatt Helsley list_for_each_entry(reloc, &sec->reloc_list, list) { 903a1a664ecSMartin Schwidefsky reloc->rela.r_offset = reloc->offset; 904a1a664ecSMartin Schwidefsky reloc->rela.r_addend = reloc->addend; 905a1a664ecSMartin Schwidefsky reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 906a1a664ecSMartin Schwidefsky gelf_update_rela(sec->data, idx, &reloc->rela); 907627fce14SJosh Poimboeuf idx++; 908627fce14SJosh Poimboeuf } 909627fce14SJosh Poimboeuf 910627fce14SJosh Poimboeuf return 0; 911627fce14SJosh Poimboeuf } 912627fce14SJosh Poimboeuf 9133a647607SPeter Zijlstra static int elf_rebuild_reloc_section(struct elf *elf, struct section *sec) 914fb414783SMatt Helsley { 915fb414783SMatt Helsley struct reloc *reloc; 916fb414783SMatt Helsley int nr; 917fb414783SMatt Helsley 918fb414783SMatt Helsley nr = 0; 919fb414783SMatt Helsley list_for_each_entry(reloc, &sec->reloc_list, list) 920fb414783SMatt Helsley nr++; 921fb414783SMatt Helsley 922fb414783SMatt Helsley switch (sec->sh.sh_type) { 923fb414783SMatt Helsley case SHT_REL: return elf_rebuild_rel_reloc_section(sec, nr); 924fb414783SMatt Helsley case SHT_RELA: return elf_rebuild_rela_reloc_section(sec, nr); 925fb414783SMatt Helsley default: return -1; 926fb414783SMatt Helsley } 927fb414783SMatt Helsley } 928fb414783SMatt Helsley 929fdabdd0bSPeter Zijlstra int elf_write_insn(struct elf *elf, struct section *sec, 930fdabdd0bSPeter Zijlstra unsigned long offset, unsigned int len, 931fdabdd0bSPeter Zijlstra const char *insn) 932fdabdd0bSPeter Zijlstra { 933fdabdd0bSPeter Zijlstra Elf_Data *data = sec->data; 934fdabdd0bSPeter Zijlstra 935fdabdd0bSPeter Zijlstra if (data->d_type != ELF_T_BYTE || data->d_off) { 936fdabdd0bSPeter Zijlstra WARN("write to unexpected data for section: %s", sec->name); 937fdabdd0bSPeter Zijlstra return -1; 938fdabdd0bSPeter Zijlstra } 939fdabdd0bSPeter Zijlstra 940fdabdd0bSPeter Zijlstra memcpy(data->d_buf + offset, insn, len); 941fdabdd0bSPeter Zijlstra elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY); 942fdabdd0bSPeter Zijlstra 943fdabdd0bSPeter Zijlstra elf->changed = true; 944fdabdd0bSPeter Zijlstra 945fdabdd0bSPeter Zijlstra return 0; 946fdabdd0bSPeter Zijlstra } 947fdabdd0bSPeter Zijlstra 948d832c005SPeter Zijlstra int elf_write_reloc(struct elf *elf, struct reloc *reloc) 949fdabdd0bSPeter Zijlstra { 950d832c005SPeter Zijlstra struct section *sec = reloc->sec; 951fdabdd0bSPeter Zijlstra 952d832c005SPeter Zijlstra if (sec->sh.sh_type == SHT_REL) { 953d832c005SPeter Zijlstra reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 954d832c005SPeter Zijlstra reloc->rel.r_offset = reloc->offset; 955fdabdd0bSPeter Zijlstra 956d832c005SPeter Zijlstra if (!gelf_update_rel(sec->data, reloc->idx, &reloc->rel)) { 957d832c005SPeter Zijlstra WARN_ELF("gelf_update_rel"); 958d832c005SPeter Zijlstra return -1; 959d832c005SPeter Zijlstra } 960d832c005SPeter Zijlstra } else { 961d832c005SPeter Zijlstra reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 962d832c005SPeter Zijlstra reloc->rela.r_addend = reloc->addend; 963d832c005SPeter Zijlstra reloc->rela.r_offset = reloc->offset; 964d832c005SPeter Zijlstra 965d832c005SPeter Zijlstra if (!gelf_update_rela(sec->data, reloc->idx, &reloc->rela)) { 966fdabdd0bSPeter Zijlstra WARN_ELF("gelf_update_rela"); 967fdabdd0bSPeter Zijlstra return -1; 968fdabdd0bSPeter Zijlstra } 969d832c005SPeter Zijlstra } 970fdabdd0bSPeter Zijlstra 971fdabdd0bSPeter Zijlstra elf->changed = true; 972fdabdd0bSPeter Zijlstra 973fdabdd0bSPeter Zijlstra return 0; 974fdabdd0bSPeter Zijlstra } 975fdabdd0bSPeter Zijlstra 9762b10be23SPeter Zijlstra int elf_write(struct elf *elf) 977627fce14SJosh Poimboeuf { 978627fce14SJosh Poimboeuf struct section *sec; 979627fce14SJosh Poimboeuf Elf_Scn *s; 980627fce14SJosh Poimboeuf 9813a647607SPeter Zijlstra /* Update changed relocation sections and section headers: */ 982627fce14SJosh Poimboeuf list_for_each_entry(sec, &elf->sections, list) { 983627fce14SJosh Poimboeuf if (sec->changed) { 9843a647607SPeter Zijlstra if (sec->base && 9853a647607SPeter Zijlstra elf_rebuild_reloc_section(elf, sec)) { 9863a647607SPeter Zijlstra WARN("elf_rebuild_reloc_section"); 9873a647607SPeter Zijlstra return -1; 9883a647607SPeter Zijlstra } 9893a647607SPeter Zijlstra 990627fce14SJosh Poimboeuf s = elf_getscn(elf->elf, sec->idx); 991627fce14SJosh Poimboeuf if (!s) { 992627fce14SJosh Poimboeuf WARN_ELF("elf_getscn"); 993627fce14SJosh Poimboeuf return -1; 994627fce14SJosh Poimboeuf } 995627fce14SJosh Poimboeuf if (!gelf_update_shdr(s, &sec->sh)) { 996627fce14SJosh Poimboeuf WARN_ELF("gelf_update_shdr"); 997627fce14SJosh Poimboeuf return -1; 998627fce14SJosh Poimboeuf } 9992b10be23SPeter Zijlstra 10002b10be23SPeter Zijlstra sec->changed = false; 10013a647607SPeter Zijlstra elf->changed = true; 1002627fce14SJosh Poimboeuf } 1003627fce14SJosh Poimboeuf } 1004627fce14SJosh Poimboeuf 100597dab2aeSJosh Poimboeuf /* Make sure the new section header entries get updated properly. */ 100697dab2aeSJosh Poimboeuf elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY); 100797dab2aeSJosh Poimboeuf 100897dab2aeSJosh Poimboeuf /* Write all changes to the file. */ 1009627fce14SJosh Poimboeuf if (elf_update(elf->elf, ELF_C_WRITE) < 0) { 1010627fce14SJosh Poimboeuf WARN_ELF("elf_update"); 1011627fce14SJosh Poimboeuf return -1; 1012627fce14SJosh Poimboeuf } 1013627fce14SJosh Poimboeuf 10142b10be23SPeter Zijlstra elf->changed = false; 10152b10be23SPeter Zijlstra 1016627fce14SJosh Poimboeuf return 0; 1017627fce14SJosh Poimboeuf } 1018627fce14SJosh Poimboeuf 1019442f04c3SJosh Poimboeuf void elf_close(struct elf *elf) 1020442f04c3SJosh Poimboeuf { 1021442f04c3SJosh Poimboeuf struct section *sec, *tmpsec; 1022442f04c3SJosh Poimboeuf struct symbol *sym, *tmpsym; 1023f1974222SMatt Helsley struct reloc *reloc, *tmpreloc; 1024442f04c3SJosh Poimboeuf 1025baa41469SJosh Poimboeuf if (elf->elf) 1026baa41469SJosh Poimboeuf elf_end(elf->elf); 1027baa41469SJosh Poimboeuf 1028baa41469SJosh Poimboeuf if (elf->fd > 0) 1029baa41469SJosh Poimboeuf close(elf->fd); 1030baa41469SJosh Poimboeuf 1031442f04c3SJosh Poimboeuf list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { 1032a196e171SJosh Poimboeuf list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { 1033442f04c3SJosh Poimboeuf list_del(&sym->list); 1034042ba73fSJosh Poimboeuf hash_del(&sym->hash); 1035442f04c3SJosh Poimboeuf free(sym); 1036442f04c3SJosh Poimboeuf } 1037f1974222SMatt Helsley list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) { 1038f1974222SMatt Helsley list_del(&reloc->list); 1039f1974222SMatt Helsley hash_del(&reloc->hash); 1040f1974222SMatt Helsley free(reloc); 1041442f04c3SJosh Poimboeuf } 1042442f04c3SJosh Poimboeuf list_del(&sec->list); 1043442f04c3SJosh Poimboeuf free(sec); 1044442f04c3SJosh Poimboeuf } 1045baa41469SJosh Poimboeuf 1046442f04c3SJosh Poimboeuf free(elf); 1047442f04c3SJosh Poimboeuf } 1048