162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * elf.c - ELF access library 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Adapted from kpatch (https://github.com/dynup/kpatch): 662306a36Sopenharmony_ci * Copyright (C) 2013-2015 Josh Poimboeuf <jpoimboe@redhat.com> 762306a36Sopenharmony_ci * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <sys/types.h> 1162306a36Sopenharmony_ci#include <sys/stat.h> 1262306a36Sopenharmony_ci#include <sys/mman.h> 1362306a36Sopenharmony_ci#include <fcntl.h> 1462306a36Sopenharmony_ci#include <stdio.h> 1562306a36Sopenharmony_ci#include <stdlib.h> 1662306a36Sopenharmony_ci#include <string.h> 1762306a36Sopenharmony_ci#include <unistd.h> 1862306a36Sopenharmony_ci#include <errno.h> 1962306a36Sopenharmony_ci#include <linux/interval_tree_generic.h> 2062306a36Sopenharmony_ci#include <objtool/builtin.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <objtool/elf.h> 2362306a36Sopenharmony_ci#include <objtool/warn.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define MAX_NAME_LEN 128 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic inline u32 str_hash(const char *str) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci return jhash(str, strlen(str), 0); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define __elf_table(name) (elf->name##_hash) 3362306a36Sopenharmony_ci#define __elf_bits(name) (elf->name##_bits) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define __elf_table_entry(name, key) \ 3662306a36Sopenharmony_ci __elf_table(name)[hash_min(key, __elf_bits(name))] 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define elf_hash_add(name, node, key) \ 3962306a36Sopenharmony_ci({ \ 4062306a36Sopenharmony_ci struct elf_hash_node *__node = node; \ 4162306a36Sopenharmony_ci __node->next = __elf_table_entry(name, key); \ 4262306a36Sopenharmony_ci __elf_table_entry(name, key) = __node; \ 4362306a36Sopenharmony_ci}) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic inline void __elf_hash_del(struct elf_hash_node *node, 4662306a36Sopenharmony_ci struct elf_hash_node **head) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct elf_hash_node *cur, *prev; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (node == *head) { 5162306a36Sopenharmony_ci *head = node->next; 5262306a36Sopenharmony_ci return; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci for (prev = NULL, cur = *head; cur; prev = cur, cur = cur->next) { 5662306a36Sopenharmony_ci if (cur == node) { 5762306a36Sopenharmony_ci prev->next = cur->next; 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define elf_hash_del(name, node, key) \ 6462306a36Sopenharmony_ci __elf_hash_del(node, &__elf_table_entry(name, key)) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define elf_list_entry(ptr, type, member) \ 6762306a36Sopenharmony_ci({ \ 6862306a36Sopenharmony_ci typeof(ptr) __ptr = (ptr); \ 6962306a36Sopenharmony_ci __ptr ? container_of(__ptr, type, member) : NULL; \ 7062306a36Sopenharmony_ci}) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define elf_hash_for_each_possible(name, obj, member, key) \ 7362306a36Sopenharmony_ci for (obj = elf_list_entry(__elf_table_entry(name, key), typeof(*obj), member); \ 7462306a36Sopenharmony_ci obj; \ 7562306a36Sopenharmony_ci obj = elf_list_entry(obj->member.next, typeof(*(obj)), member)) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define elf_alloc_hash(name, size) \ 7862306a36Sopenharmony_ci({ \ 7962306a36Sopenharmony_ci __elf_bits(name) = max(10, ilog2(size)); \ 8062306a36Sopenharmony_ci __elf_table(name) = mmap(NULL, sizeof(struct elf_hash_node *) << __elf_bits(name), \ 8162306a36Sopenharmony_ci PROT_READ|PROT_WRITE, \ 8262306a36Sopenharmony_ci MAP_PRIVATE|MAP_ANON, -1, 0); \ 8362306a36Sopenharmony_ci if (__elf_table(name) == (void *)-1L) { \ 8462306a36Sopenharmony_ci WARN("mmap fail " #name); \ 8562306a36Sopenharmony_ci __elf_table(name) = NULL; \ 8662306a36Sopenharmony_ci } \ 8762306a36Sopenharmony_ci __elf_table(name); \ 8862306a36Sopenharmony_ci}) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic inline unsigned long __sym_start(struct symbol *s) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci return s->offset; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic inline unsigned long __sym_last(struct symbol *s) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci return s->offset + s->len - 1; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ciINTERVAL_TREE_DEFINE(struct symbol, node, unsigned long, __subtree_last, 10162306a36Sopenharmony_ci __sym_start, __sym_last, static, __sym) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define __sym_for_each(_iter, _tree, _start, _end) \ 10462306a36Sopenharmony_ci for (_iter = __sym_iter_first((_tree), (_start), (_end)); \ 10562306a36Sopenharmony_ci _iter; _iter = __sym_iter_next(_iter, (_start), (_end))) 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistruct symbol_hole { 10862306a36Sopenharmony_ci unsigned long key; 10962306a36Sopenharmony_ci const struct symbol *sym; 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci * Find !section symbol where @offset is after it. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_cistatic int symbol_hole_by_offset(const void *key, const struct rb_node *node) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci const struct symbol *s = rb_entry(node, struct symbol, node); 11862306a36Sopenharmony_ci struct symbol_hole *sh = (void *)key; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (sh->key < s->offset) 12162306a36Sopenharmony_ci return -1; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (sh->key >= s->offset + s->len) { 12462306a36Sopenharmony_ci if (s->type != STT_SECTION) 12562306a36Sopenharmony_ci sh->sym = s; 12662306a36Sopenharmony_ci return 1; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistruct section *find_section_by_name(const struct elf *elf, const char *name) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct section *sec; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci elf_hash_for_each_possible(section_name, sec, name_hash, str_hash(name)) { 13762306a36Sopenharmony_ci if (!strcmp(sec->name, name)) 13862306a36Sopenharmony_ci return sec; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return NULL; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic struct section *find_section_by_index(struct elf *elf, 14562306a36Sopenharmony_ci unsigned int idx) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct section *sec; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci elf_hash_for_each_possible(section, sec, hash, idx) { 15062306a36Sopenharmony_ci if (sec->idx == idx) 15162306a36Sopenharmony_ci return sec; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return NULL; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct symbol *sym; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci elf_hash_for_each_possible(symbol, sym, hash, idx) { 16262306a36Sopenharmony_ci if (sym->idx == idx) 16362306a36Sopenharmony_ci return sym; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return NULL; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistruct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; 17262306a36Sopenharmony_ci struct symbol *iter; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci __sym_for_each(iter, tree, offset, offset) { 17562306a36Sopenharmony_ci if (iter->offset == offset && iter->type != STT_SECTION) 17662306a36Sopenharmony_ci return iter; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return NULL; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistruct symbol *find_func_by_offset(struct section *sec, unsigned long offset) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; 18562306a36Sopenharmony_ci struct symbol *iter; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci __sym_for_each(iter, tree, offset, offset) { 18862306a36Sopenharmony_ci if (iter->offset == offset && iter->type == STT_FUNC) 18962306a36Sopenharmony_ci return iter; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return NULL; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistruct symbol *find_symbol_containing(const struct section *sec, unsigned long offset) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; 19862306a36Sopenharmony_ci struct symbol *iter; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci __sym_for_each(iter, tree, offset, offset) { 20162306a36Sopenharmony_ci if (iter->type != STT_SECTION) 20262306a36Sopenharmony_ci return iter; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return NULL; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* 20962306a36Sopenharmony_ci * Returns size of hole starting at @offset. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ciint find_symbol_hole_containing(const struct section *sec, unsigned long offset) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct symbol_hole hole = { 21462306a36Sopenharmony_ci .key = offset, 21562306a36Sopenharmony_ci .sym = NULL, 21662306a36Sopenharmony_ci }; 21762306a36Sopenharmony_ci struct rb_node *n; 21862306a36Sopenharmony_ci struct symbol *s; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * Find the rightmost symbol for which @offset is after it. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci n = rb_find(&hole, &sec->symbol_tree.rb_root, symbol_hole_by_offset); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* found a symbol that contains @offset */ 22662306a36Sopenharmony_ci if (n) 22762306a36Sopenharmony_ci return 0; /* not a hole */ 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* didn't find a symbol for which @offset is after it */ 23062306a36Sopenharmony_ci if (!hole.sym) 23162306a36Sopenharmony_ci return 0; /* not a hole */ 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* @offset >= sym->offset + sym->len, find symbol after it */ 23462306a36Sopenharmony_ci n = rb_next(&hole.sym->node); 23562306a36Sopenharmony_ci if (!n) 23662306a36Sopenharmony_ci return -1; /* until end of address space */ 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* hole until start of next symbol */ 23962306a36Sopenharmony_ci s = rb_entry(n, struct symbol, node); 24062306a36Sopenharmony_ci return s->offset - offset; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistruct symbol *find_func_containing(struct section *sec, unsigned long offset) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; 24662306a36Sopenharmony_ci struct symbol *iter; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci __sym_for_each(iter, tree, offset, offset) { 24962306a36Sopenharmony_ci if (iter->type == STT_FUNC) 25062306a36Sopenharmony_ci return iter; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return NULL; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistruct symbol *find_symbol_by_name(const struct elf *elf, const char *name) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct symbol *sym; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) { 26162306a36Sopenharmony_ci if (!strcmp(sym->name, name)) 26262306a36Sopenharmony_ci return sym; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return NULL; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistruct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, 26962306a36Sopenharmony_ci unsigned long offset, unsigned int len) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct reloc *reloc, *r = NULL; 27262306a36Sopenharmony_ci struct section *rsec; 27362306a36Sopenharmony_ci unsigned long o; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci rsec = sec->rsec; 27662306a36Sopenharmony_ci if (!rsec) 27762306a36Sopenharmony_ci return NULL; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci for_offset_range(o, offset, offset + len) { 28062306a36Sopenharmony_ci elf_hash_for_each_possible(reloc, reloc, hash, 28162306a36Sopenharmony_ci sec_offset_hash(rsec, o)) { 28262306a36Sopenharmony_ci if (reloc->sec != rsec) 28362306a36Sopenharmony_ci continue; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (reloc_offset(reloc) >= offset && 28662306a36Sopenharmony_ci reloc_offset(reloc) < offset + len) { 28762306a36Sopenharmony_ci if (!r || reloc_offset(reloc) < reloc_offset(r)) 28862306a36Sopenharmony_ci r = reloc; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci if (r) 29262306a36Sopenharmony_ci return r; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return NULL; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistruct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci return find_reloc_by_dest_range(elf, sec, offset, 1); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic bool is_dwarf_section(struct section *sec) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci return !strncmp(sec->name, ".debug_", 7); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int read_sections(struct elf *elf) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci Elf_Scn *s = NULL; 31162306a36Sopenharmony_ci struct section *sec; 31262306a36Sopenharmony_ci size_t shstrndx, sections_nr; 31362306a36Sopenharmony_ci int i; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (elf_getshdrnum(elf->elf, §ions_nr)) { 31662306a36Sopenharmony_ci WARN_ELF("elf_getshdrnum"); 31762306a36Sopenharmony_ci return -1; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (elf_getshdrstrndx(elf->elf, &shstrndx)) { 32162306a36Sopenharmony_ci WARN_ELF("elf_getshdrstrndx"); 32262306a36Sopenharmony_ci return -1; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (!elf_alloc_hash(section, sections_nr) || 32662306a36Sopenharmony_ci !elf_alloc_hash(section_name, sections_nr)) 32762306a36Sopenharmony_ci return -1; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci elf->section_data = calloc(sections_nr, sizeof(*sec)); 33062306a36Sopenharmony_ci if (!elf->section_data) { 33162306a36Sopenharmony_ci perror("calloc"); 33262306a36Sopenharmony_ci return -1; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci for (i = 0; i < sections_nr; i++) { 33562306a36Sopenharmony_ci sec = &elf->section_data[i]; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci INIT_LIST_HEAD(&sec->symbol_list); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci s = elf_getscn(elf->elf, i); 34062306a36Sopenharmony_ci if (!s) { 34162306a36Sopenharmony_ci WARN_ELF("elf_getscn"); 34262306a36Sopenharmony_ci return -1; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci sec->idx = elf_ndxscn(s); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (!gelf_getshdr(s, &sec->sh)) { 34862306a36Sopenharmony_ci WARN_ELF("gelf_getshdr"); 34962306a36Sopenharmony_ci return -1; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name); 35362306a36Sopenharmony_ci if (!sec->name) { 35462306a36Sopenharmony_ci WARN_ELF("elf_strptr"); 35562306a36Sopenharmony_ci return -1; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (sec->sh.sh_size != 0 && !is_dwarf_section(sec)) { 35962306a36Sopenharmony_ci sec->data = elf_getdata(s, NULL); 36062306a36Sopenharmony_ci if (!sec->data) { 36162306a36Sopenharmony_ci WARN_ELF("elf_getdata"); 36262306a36Sopenharmony_ci return -1; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci if (sec->data->d_off != 0 || 36562306a36Sopenharmony_ci sec->data->d_size != sec->sh.sh_size) { 36662306a36Sopenharmony_ci WARN("unexpected data attributes for %s", 36762306a36Sopenharmony_ci sec->name); 36862306a36Sopenharmony_ci return -1; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci list_add_tail(&sec->list, &elf->sections); 37362306a36Sopenharmony_ci elf_hash_add(section, &sec->hash, sec->idx); 37462306a36Sopenharmony_ci elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (is_reloc_sec(sec)) 37762306a36Sopenharmony_ci elf->num_relocs += sec_num_entries(sec); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (opts.stats) { 38162306a36Sopenharmony_ci printf("nr_sections: %lu\n", (unsigned long)sections_nr); 38262306a36Sopenharmony_ci printf("section_bits: %d\n", elf->section_bits); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* sanity check, one more call to elf_nextscn() should return NULL */ 38662306a36Sopenharmony_ci if (elf_nextscn(elf->elf, s)) { 38762306a36Sopenharmony_ci WARN("section entry mismatch"); 38862306a36Sopenharmony_ci return -1; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void elf_add_symbol(struct elf *elf, struct symbol *sym) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct list_head *entry; 39762306a36Sopenharmony_ci struct rb_node *pnode; 39862306a36Sopenharmony_ci struct symbol *iter; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci INIT_LIST_HEAD(&sym->pv_target); 40162306a36Sopenharmony_ci sym->alias = sym; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci sym->type = GELF_ST_TYPE(sym->sym.st_info); 40462306a36Sopenharmony_ci sym->bind = GELF_ST_BIND(sym->sym.st_info); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (sym->type == STT_FILE) 40762306a36Sopenharmony_ci elf->num_files++; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci sym->offset = sym->sym.st_value; 41062306a36Sopenharmony_ci sym->len = sym->sym.st_size; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci __sym_for_each(iter, &sym->sec->symbol_tree, sym->offset, sym->offset) { 41362306a36Sopenharmony_ci if (iter->offset == sym->offset && iter->type == sym->type) 41462306a36Sopenharmony_ci iter->alias = sym; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci __sym_insert(sym, &sym->sec->symbol_tree); 41862306a36Sopenharmony_ci pnode = rb_prev(&sym->node); 41962306a36Sopenharmony_ci if (pnode) 42062306a36Sopenharmony_ci entry = &rb_entry(pnode, struct symbol, node)->list; 42162306a36Sopenharmony_ci else 42262306a36Sopenharmony_ci entry = &sym->sec->symbol_list; 42362306a36Sopenharmony_ci list_add(&sym->list, entry); 42462306a36Sopenharmony_ci elf_hash_add(symbol, &sym->hash, sym->idx); 42562306a36Sopenharmony_ci elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name)); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* 42862306a36Sopenharmony_ci * Don't store empty STT_NOTYPE symbols in the rbtree. They 42962306a36Sopenharmony_ci * can exist within a function, confusing the sorting. 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_ci if (!sym->len) 43262306a36Sopenharmony_ci __sym_remove(sym, &sym->sec->symbol_tree); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int read_symbols(struct elf *elf) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct section *symtab, *symtab_shndx, *sec; 43862306a36Sopenharmony_ci struct symbol *sym, *pfunc; 43962306a36Sopenharmony_ci int symbols_nr, i; 44062306a36Sopenharmony_ci char *coldstr; 44162306a36Sopenharmony_ci Elf_Data *shndx_data = NULL; 44262306a36Sopenharmony_ci Elf32_Word shndx; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci symtab = find_section_by_name(elf, ".symtab"); 44562306a36Sopenharmony_ci if (symtab) { 44662306a36Sopenharmony_ci symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); 44762306a36Sopenharmony_ci if (symtab_shndx) 44862306a36Sopenharmony_ci shndx_data = symtab_shndx->data; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci symbols_nr = sec_num_entries(symtab); 45162306a36Sopenharmony_ci } else { 45262306a36Sopenharmony_ci /* 45362306a36Sopenharmony_ci * A missing symbol table is actually possible if it's an empty 45462306a36Sopenharmony_ci * .o file. This can happen for thunk_64.o. Make sure to at 45562306a36Sopenharmony_ci * least allocate the symbol hash tables so we can do symbol 45662306a36Sopenharmony_ci * lookups without crashing. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci symbols_nr = 0; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (!elf_alloc_hash(symbol, symbols_nr) || 46262306a36Sopenharmony_ci !elf_alloc_hash(symbol_name, symbols_nr)) 46362306a36Sopenharmony_ci return -1; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci elf->symbol_data = calloc(symbols_nr, sizeof(*sym)); 46662306a36Sopenharmony_ci if (!elf->symbol_data) { 46762306a36Sopenharmony_ci perror("calloc"); 46862306a36Sopenharmony_ci return -1; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci for (i = 0; i < symbols_nr; i++) { 47162306a36Sopenharmony_ci sym = &elf->symbol_data[i]; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci sym->idx = i; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym, 47662306a36Sopenharmony_ci &shndx)) { 47762306a36Sopenharmony_ci WARN_ELF("gelf_getsymshndx"); 47862306a36Sopenharmony_ci goto err; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, 48262306a36Sopenharmony_ci sym->sym.st_name); 48362306a36Sopenharmony_ci if (!sym->name) { 48462306a36Sopenharmony_ci WARN_ELF("elf_strptr"); 48562306a36Sopenharmony_ci goto err; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if ((sym->sym.st_shndx > SHN_UNDEF && 48962306a36Sopenharmony_ci sym->sym.st_shndx < SHN_LORESERVE) || 49062306a36Sopenharmony_ci (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) { 49162306a36Sopenharmony_ci if (sym->sym.st_shndx != SHN_XINDEX) 49262306a36Sopenharmony_ci shndx = sym->sym.st_shndx; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci sym->sec = find_section_by_index(elf, shndx); 49562306a36Sopenharmony_ci if (!sym->sec) { 49662306a36Sopenharmony_ci WARN("couldn't find section for symbol %s", 49762306a36Sopenharmony_ci sym->name); 49862306a36Sopenharmony_ci goto err; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci if (GELF_ST_TYPE(sym->sym.st_info) == STT_SECTION) { 50162306a36Sopenharmony_ci sym->name = sym->sec->name; 50262306a36Sopenharmony_ci sym->sec->sym = sym; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci } else 50562306a36Sopenharmony_ci sym->sec = find_section_by_index(elf, 0); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci elf_add_symbol(elf, sym); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (opts.stats) { 51162306a36Sopenharmony_ci printf("nr_symbols: %lu\n", (unsigned long)symbols_nr); 51262306a36Sopenharmony_ci printf("symbol_bits: %d\n", elf->symbol_bits); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* Create parent/child links for any cold subfunctions */ 51662306a36Sopenharmony_ci list_for_each_entry(sec, &elf->sections, list) { 51762306a36Sopenharmony_ci sec_for_each_sym(sec, sym) { 51862306a36Sopenharmony_ci char pname[MAX_NAME_LEN + 1]; 51962306a36Sopenharmony_ci size_t pnamelen; 52062306a36Sopenharmony_ci if (sym->type != STT_FUNC) 52162306a36Sopenharmony_ci continue; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (sym->pfunc == NULL) 52462306a36Sopenharmony_ci sym->pfunc = sym; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (sym->cfunc == NULL) 52762306a36Sopenharmony_ci sym->cfunc = sym; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci coldstr = strstr(sym->name, ".cold"); 53062306a36Sopenharmony_ci if (!coldstr) 53162306a36Sopenharmony_ci continue; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci pnamelen = coldstr - sym->name; 53462306a36Sopenharmony_ci if (pnamelen > MAX_NAME_LEN) { 53562306a36Sopenharmony_ci WARN("%s(): parent function name exceeds maximum length of %d characters", 53662306a36Sopenharmony_ci sym->name, MAX_NAME_LEN); 53762306a36Sopenharmony_ci return -1; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci strncpy(pname, sym->name, pnamelen); 54162306a36Sopenharmony_ci pname[pnamelen] = '\0'; 54262306a36Sopenharmony_ci pfunc = find_symbol_by_name(elf, pname); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (!pfunc) { 54562306a36Sopenharmony_ci WARN("%s(): can't find parent function", 54662306a36Sopenharmony_ci sym->name); 54762306a36Sopenharmony_ci return -1; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci sym->pfunc = pfunc; 55162306a36Sopenharmony_ci pfunc->cfunc = sym; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* 55462306a36Sopenharmony_ci * Unfortunately, -fnoreorder-functions puts the child 55562306a36Sopenharmony_ci * inside the parent. Remove the overlap so we can 55662306a36Sopenharmony_ci * have sane assumptions. 55762306a36Sopenharmony_ci * 55862306a36Sopenharmony_ci * Note that pfunc->len now no longer matches 55962306a36Sopenharmony_ci * pfunc->sym.st_size. 56062306a36Sopenharmony_ci */ 56162306a36Sopenharmony_ci if (sym->sec == pfunc->sec && 56262306a36Sopenharmony_ci sym->offset >= pfunc->offset && 56362306a36Sopenharmony_ci sym->offset + sym->len == pfunc->offset + pfunc->len) { 56462306a36Sopenharmony_ci pfunc->len -= sym->len; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return 0; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cierr: 57262306a36Sopenharmony_ci free(sym); 57362306a36Sopenharmony_ci return -1; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci/* 57762306a36Sopenharmony_ci * @sym's idx has changed. Update the relocs which reference it. 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_cistatic int elf_update_sym_relocs(struct elf *elf, struct symbol *sym) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct reloc *reloc; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci for (reloc = sym->relocs; reloc; reloc = reloc->sym_next_reloc) 58462306a36Sopenharmony_ci set_reloc_sym(elf, reloc, reloc->sym->idx); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci/* 59062306a36Sopenharmony_ci * The libelf API is terrible; gelf_update_sym*() takes a data block relative 59162306a36Sopenharmony_ci * index value, *NOT* the symbol index. As such, iterate the data blocks and 59262306a36Sopenharmony_ci * adjust index until it fits. 59362306a36Sopenharmony_ci * 59462306a36Sopenharmony_ci * If no data block is found, allow adding a new data block provided the index 59562306a36Sopenharmony_ci * is only one past the end. 59662306a36Sopenharmony_ci */ 59762306a36Sopenharmony_cistatic int elf_update_symbol(struct elf *elf, struct section *symtab, 59862306a36Sopenharmony_ci struct section *symtab_shndx, struct symbol *sym) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci Elf32_Word shndx = sym->sec ? sym->sec->idx : SHN_UNDEF; 60162306a36Sopenharmony_ci Elf_Data *symtab_data = NULL, *shndx_data = NULL; 60262306a36Sopenharmony_ci Elf64_Xword entsize = symtab->sh.sh_entsize; 60362306a36Sopenharmony_ci int max_idx, idx = sym->idx; 60462306a36Sopenharmony_ci Elf_Scn *s, *t = NULL; 60562306a36Sopenharmony_ci bool is_special_shndx = sym->sym.st_shndx >= SHN_LORESERVE && 60662306a36Sopenharmony_ci sym->sym.st_shndx != SHN_XINDEX; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (is_special_shndx) 60962306a36Sopenharmony_ci shndx = sym->sym.st_shndx; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci s = elf_getscn(elf->elf, symtab->idx); 61262306a36Sopenharmony_ci if (!s) { 61362306a36Sopenharmony_ci WARN_ELF("elf_getscn"); 61462306a36Sopenharmony_ci return -1; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (symtab_shndx) { 61862306a36Sopenharmony_ci t = elf_getscn(elf->elf, symtab_shndx->idx); 61962306a36Sopenharmony_ci if (!t) { 62062306a36Sopenharmony_ci WARN_ELF("elf_getscn"); 62162306a36Sopenharmony_ci return -1; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci for (;;) { 62662306a36Sopenharmony_ci /* get next data descriptor for the relevant sections */ 62762306a36Sopenharmony_ci symtab_data = elf_getdata(s, symtab_data); 62862306a36Sopenharmony_ci if (t) 62962306a36Sopenharmony_ci shndx_data = elf_getdata(t, shndx_data); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* end-of-list */ 63262306a36Sopenharmony_ci if (!symtab_data) { 63362306a36Sopenharmony_ci /* 63462306a36Sopenharmony_ci * Over-allocate to avoid O(n^2) symbol creation 63562306a36Sopenharmony_ci * behaviour. The down side is that libelf doesn't 63662306a36Sopenharmony_ci * like this; see elf_truncate_section() for the fixup. 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_ci int num = max(1U, sym->idx/3); 63962306a36Sopenharmony_ci void *buf; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (idx) { 64262306a36Sopenharmony_ci /* we don't do holes in symbol tables */ 64362306a36Sopenharmony_ci WARN("index out of range"); 64462306a36Sopenharmony_ci return -1; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* if @idx == 0, it's the next contiguous entry, create it */ 64862306a36Sopenharmony_ci symtab_data = elf_newdata(s); 64962306a36Sopenharmony_ci if (t) 65062306a36Sopenharmony_ci shndx_data = elf_newdata(t); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci buf = calloc(num, entsize); 65362306a36Sopenharmony_ci if (!buf) { 65462306a36Sopenharmony_ci WARN("malloc"); 65562306a36Sopenharmony_ci return -1; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci symtab_data->d_buf = buf; 65962306a36Sopenharmony_ci symtab_data->d_size = num * entsize; 66062306a36Sopenharmony_ci symtab_data->d_align = 1; 66162306a36Sopenharmony_ci symtab_data->d_type = ELF_T_SYM; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci mark_sec_changed(elf, symtab, true); 66462306a36Sopenharmony_ci symtab->truncate = true; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (t) { 66762306a36Sopenharmony_ci buf = calloc(num, sizeof(Elf32_Word)); 66862306a36Sopenharmony_ci if (!buf) { 66962306a36Sopenharmony_ci WARN("malloc"); 67062306a36Sopenharmony_ci return -1; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci shndx_data->d_buf = buf; 67462306a36Sopenharmony_ci shndx_data->d_size = num * sizeof(Elf32_Word); 67562306a36Sopenharmony_ci shndx_data->d_align = sizeof(Elf32_Word); 67662306a36Sopenharmony_ci shndx_data->d_type = ELF_T_WORD; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci mark_sec_changed(elf, symtab_shndx, true); 67962306a36Sopenharmony_ci symtab_shndx->truncate = true; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* empty blocks should not happen */ 68662306a36Sopenharmony_ci if (!symtab_data->d_size) { 68762306a36Sopenharmony_ci WARN("zero size data"); 68862306a36Sopenharmony_ci return -1; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* is this the right block? */ 69262306a36Sopenharmony_ci max_idx = symtab_data->d_size / entsize; 69362306a36Sopenharmony_ci if (idx < max_idx) 69462306a36Sopenharmony_ci break; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* adjust index and try again */ 69762306a36Sopenharmony_ci idx -= max_idx; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci /* something went side-ways */ 70162306a36Sopenharmony_ci if (idx < 0) { 70262306a36Sopenharmony_ci WARN("negative index"); 70362306a36Sopenharmony_ci return -1; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* setup extended section index magic and write the symbol */ 70762306a36Sopenharmony_ci if ((shndx >= SHN_UNDEF && shndx < SHN_LORESERVE) || is_special_shndx) { 70862306a36Sopenharmony_ci sym->sym.st_shndx = shndx; 70962306a36Sopenharmony_ci if (!shndx_data) 71062306a36Sopenharmony_ci shndx = 0; 71162306a36Sopenharmony_ci } else { 71262306a36Sopenharmony_ci sym->sym.st_shndx = SHN_XINDEX; 71362306a36Sopenharmony_ci if (!shndx_data) { 71462306a36Sopenharmony_ci WARN("no .symtab_shndx"); 71562306a36Sopenharmony_ci return -1; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (!gelf_update_symshndx(symtab_data, shndx_data, idx, &sym->sym, shndx)) { 72062306a36Sopenharmony_ci WARN_ELF("gelf_update_symshndx"); 72162306a36Sopenharmony_ci return -1; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci return 0; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic struct symbol * 72862306a36Sopenharmony_ci__elf_create_symbol(struct elf *elf, struct symbol *sym) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct section *symtab, *symtab_shndx; 73162306a36Sopenharmony_ci Elf32_Word first_non_local, new_idx; 73262306a36Sopenharmony_ci struct symbol *old; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci symtab = find_section_by_name(elf, ".symtab"); 73562306a36Sopenharmony_ci if (symtab) { 73662306a36Sopenharmony_ci symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); 73762306a36Sopenharmony_ci } else { 73862306a36Sopenharmony_ci WARN("no .symtab"); 73962306a36Sopenharmony_ci return NULL; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci new_idx = sec_num_entries(symtab); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (GELF_ST_BIND(sym->sym.st_info) != STB_LOCAL) 74562306a36Sopenharmony_ci goto non_local; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* 74862306a36Sopenharmony_ci * Move the first global symbol, as per sh_info, into a new, higher 74962306a36Sopenharmony_ci * symbol index. This fees up a spot for a new local symbol. 75062306a36Sopenharmony_ci */ 75162306a36Sopenharmony_ci first_non_local = symtab->sh.sh_info; 75262306a36Sopenharmony_ci old = find_symbol_by_index(elf, first_non_local); 75362306a36Sopenharmony_ci if (old) { 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci elf_hash_del(symbol, &old->hash, old->idx); 75662306a36Sopenharmony_ci elf_hash_add(symbol, &old->hash, new_idx); 75762306a36Sopenharmony_ci old->idx = new_idx; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (elf_update_symbol(elf, symtab, symtab_shndx, old)) { 76062306a36Sopenharmony_ci WARN("elf_update_symbol move"); 76162306a36Sopenharmony_ci return NULL; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (elf_update_sym_relocs(elf, old)) 76562306a36Sopenharmony_ci return NULL; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci new_idx = first_non_local; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* 77162306a36Sopenharmony_ci * Either way, we will add a LOCAL symbol. 77262306a36Sopenharmony_ci */ 77362306a36Sopenharmony_ci symtab->sh.sh_info += 1; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cinon_local: 77662306a36Sopenharmony_ci sym->idx = new_idx; 77762306a36Sopenharmony_ci if (elf_update_symbol(elf, symtab, symtab_shndx, sym)) { 77862306a36Sopenharmony_ci WARN("elf_update_symbol"); 77962306a36Sopenharmony_ci return NULL; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci symtab->sh.sh_size += symtab->sh.sh_entsize; 78362306a36Sopenharmony_ci mark_sec_changed(elf, symtab, true); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (symtab_shndx) { 78662306a36Sopenharmony_ci symtab_shndx->sh.sh_size += sizeof(Elf32_Word); 78762306a36Sopenharmony_ci mark_sec_changed(elf, symtab_shndx, true); 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci return sym; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic struct symbol * 79462306a36Sopenharmony_cielf_create_section_symbol(struct elf *elf, struct section *sec) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci struct symbol *sym = calloc(1, sizeof(*sym)); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (!sym) { 79962306a36Sopenharmony_ci perror("malloc"); 80062306a36Sopenharmony_ci return NULL; 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci sym->name = sec->name; 80462306a36Sopenharmony_ci sym->sec = sec; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci // st_name 0 80762306a36Sopenharmony_ci sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION); 80862306a36Sopenharmony_ci // st_other 0 80962306a36Sopenharmony_ci // st_value 0 81062306a36Sopenharmony_ci // st_size 0 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci sym = __elf_create_symbol(elf, sym); 81362306a36Sopenharmony_ci if (sym) 81462306a36Sopenharmony_ci elf_add_symbol(elf, sym); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci return sym; 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic int elf_add_string(struct elf *elf, struct section *strtab, char *str); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_cistruct symbol * 82262306a36Sopenharmony_cielf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci struct symbol *sym = calloc(1, sizeof(*sym)); 82562306a36Sopenharmony_ci size_t namelen = strlen(orig->name) + sizeof("__pfx_"); 82662306a36Sopenharmony_ci char *name = malloc(namelen); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (!sym || !name) { 82962306a36Sopenharmony_ci perror("malloc"); 83062306a36Sopenharmony_ci return NULL; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci snprintf(name, namelen, "__pfx_%s", orig->name); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci sym->name = name; 83662306a36Sopenharmony_ci sym->sec = orig->sec; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci sym->sym.st_name = elf_add_string(elf, NULL, name); 83962306a36Sopenharmony_ci sym->sym.st_info = orig->sym.st_info; 84062306a36Sopenharmony_ci sym->sym.st_value = orig->sym.st_value - size; 84162306a36Sopenharmony_ci sym->sym.st_size = size; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci sym = __elf_create_symbol(elf, sym); 84462306a36Sopenharmony_ci if (sym) 84562306a36Sopenharmony_ci elf_add_symbol(elf, sym); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return sym; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec, 85162306a36Sopenharmony_ci unsigned int reloc_idx, 85262306a36Sopenharmony_ci unsigned long offset, struct symbol *sym, 85362306a36Sopenharmony_ci s64 addend, unsigned int type) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct reloc *reloc, empty = { 0 }; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (reloc_idx >= sec_num_entries(rsec)) { 85862306a36Sopenharmony_ci WARN("%s: bad reloc_idx %u for %s with %d relocs", 85962306a36Sopenharmony_ci __func__, reloc_idx, rsec->name, sec_num_entries(rsec)); 86062306a36Sopenharmony_ci return NULL; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci reloc = &rsec->relocs[reloc_idx]; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (memcmp(reloc, &empty, sizeof(empty))) { 86662306a36Sopenharmony_ci WARN("%s: %s: reloc %d already initialized!", 86762306a36Sopenharmony_ci __func__, rsec->name, reloc_idx); 86862306a36Sopenharmony_ci return NULL; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci reloc->sec = rsec; 87262306a36Sopenharmony_ci reloc->sym = sym; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci set_reloc_offset(elf, reloc, offset); 87562306a36Sopenharmony_ci set_reloc_sym(elf, reloc, sym->idx); 87662306a36Sopenharmony_ci set_reloc_type(elf, reloc, type); 87762306a36Sopenharmony_ci set_reloc_addend(elf, reloc, addend); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); 88062306a36Sopenharmony_ci reloc->sym_next_reloc = sym->relocs; 88162306a36Sopenharmony_ci sym->relocs = reloc; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci return reloc; 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistruct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec, 88762306a36Sopenharmony_ci unsigned long offset, 88862306a36Sopenharmony_ci unsigned int reloc_idx, 88962306a36Sopenharmony_ci struct section *insn_sec, 89062306a36Sopenharmony_ci unsigned long insn_off) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci struct symbol *sym = insn_sec->sym; 89362306a36Sopenharmony_ci int addend = insn_off; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (!(insn_sec->sh.sh_flags & SHF_EXECINSTR)) { 89662306a36Sopenharmony_ci WARN("bad call to %s() for data symbol %s", 89762306a36Sopenharmony_ci __func__, sym->name); 89862306a36Sopenharmony_ci return NULL; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (!sym) { 90262306a36Sopenharmony_ci /* 90362306a36Sopenharmony_ci * Due to how weak functions work, we must use section based 90462306a36Sopenharmony_ci * relocations. Symbol based relocations would result in the 90562306a36Sopenharmony_ci * weak and non-weak function annotations being overlaid on the 90662306a36Sopenharmony_ci * non-weak function after linking. 90762306a36Sopenharmony_ci */ 90862306a36Sopenharmony_ci sym = elf_create_section_symbol(elf, insn_sec); 90962306a36Sopenharmony_ci if (!sym) 91062306a36Sopenharmony_ci return NULL; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci insn_sec->sym = sym; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci return elf_init_reloc(elf, sec->rsec, reloc_idx, offset, sym, addend, 91662306a36Sopenharmony_ci elf_text_rela_type(elf)); 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cistruct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec, 92062306a36Sopenharmony_ci unsigned long offset, 92162306a36Sopenharmony_ci unsigned int reloc_idx, 92262306a36Sopenharmony_ci struct symbol *sym, 92362306a36Sopenharmony_ci s64 addend) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci if (sym->sec && (sec->sh.sh_flags & SHF_EXECINSTR)) { 92662306a36Sopenharmony_ci WARN("bad call to %s() for text symbol %s", 92762306a36Sopenharmony_ci __func__, sym->name); 92862306a36Sopenharmony_ci return NULL; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return elf_init_reloc(elf, sec->rsec, reloc_idx, offset, sym, addend, 93262306a36Sopenharmony_ci elf_data_rela_type(elf)); 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic int read_relocs(struct elf *elf) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci unsigned long nr_reloc, max_reloc = 0; 93862306a36Sopenharmony_ci struct section *rsec; 93962306a36Sopenharmony_ci struct reloc *reloc; 94062306a36Sopenharmony_ci unsigned int symndx; 94162306a36Sopenharmony_ci struct symbol *sym; 94262306a36Sopenharmony_ci int i; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci if (!elf_alloc_hash(reloc, elf->num_relocs)) 94562306a36Sopenharmony_ci return -1; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci list_for_each_entry(rsec, &elf->sections, list) { 94862306a36Sopenharmony_ci if (!is_reloc_sec(rsec)) 94962306a36Sopenharmony_ci continue; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci rsec->base = find_section_by_index(elf, rsec->sh.sh_info); 95262306a36Sopenharmony_ci if (!rsec->base) { 95362306a36Sopenharmony_ci WARN("can't find base section for reloc section %s", 95462306a36Sopenharmony_ci rsec->name); 95562306a36Sopenharmony_ci return -1; 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci rsec->base->rsec = rsec; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci nr_reloc = 0; 96162306a36Sopenharmony_ci rsec->relocs = calloc(sec_num_entries(rsec), sizeof(*reloc)); 96262306a36Sopenharmony_ci if (!rsec->relocs) { 96362306a36Sopenharmony_ci perror("calloc"); 96462306a36Sopenharmony_ci return -1; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci for (i = 0; i < sec_num_entries(rsec); i++) { 96762306a36Sopenharmony_ci reloc = &rsec->relocs[i]; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci reloc->sec = rsec; 97062306a36Sopenharmony_ci symndx = reloc_sym(reloc); 97162306a36Sopenharmony_ci reloc->sym = sym = find_symbol_by_index(elf, symndx); 97262306a36Sopenharmony_ci if (!reloc->sym) { 97362306a36Sopenharmony_ci WARN("can't find reloc entry symbol %d for %s", 97462306a36Sopenharmony_ci symndx, rsec->name); 97562306a36Sopenharmony_ci return -1; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); 97962306a36Sopenharmony_ci reloc->sym_next_reloc = sym->relocs; 98062306a36Sopenharmony_ci sym->relocs = reloc; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci nr_reloc++; 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci max_reloc = max(max_reloc, nr_reloc); 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (opts.stats) { 98862306a36Sopenharmony_ci printf("max_reloc: %lu\n", max_reloc); 98962306a36Sopenharmony_ci printf("num_relocs: %lu\n", elf->num_relocs); 99062306a36Sopenharmony_ci printf("reloc_bits: %d\n", elf->reloc_bits); 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci return 0; 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_cistruct elf *elf_open_read(const char *name, int flags) 99762306a36Sopenharmony_ci{ 99862306a36Sopenharmony_ci struct elf *elf; 99962306a36Sopenharmony_ci Elf_Cmd cmd; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci elf_version(EV_CURRENT); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci elf = malloc(sizeof(*elf)); 100462306a36Sopenharmony_ci if (!elf) { 100562306a36Sopenharmony_ci perror("malloc"); 100662306a36Sopenharmony_ci return NULL; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci memset(elf, 0, sizeof(*elf)); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci INIT_LIST_HEAD(&elf->sections); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci elf->fd = open(name, flags); 101362306a36Sopenharmony_ci if (elf->fd == -1) { 101462306a36Sopenharmony_ci fprintf(stderr, "objtool: Can't open '%s': %s\n", 101562306a36Sopenharmony_ci name, strerror(errno)); 101662306a36Sopenharmony_ci goto err; 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if ((flags & O_ACCMODE) == O_RDONLY) 102062306a36Sopenharmony_ci cmd = ELF_C_READ_MMAP; 102162306a36Sopenharmony_ci else if ((flags & O_ACCMODE) == O_RDWR) 102262306a36Sopenharmony_ci cmd = ELF_C_RDWR; 102362306a36Sopenharmony_ci else /* O_WRONLY */ 102462306a36Sopenharmony_ci cmd = ELF_C_WRITE; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci elf->elf = elf_begin(elf->fd, cmd, NULL); 102762306a36Sopenharmony_ci if (!elf->elf) { 102862306a36Sopenharmony_ci WARN_ELF("elf_begin"); 102962306a36Sopenharmony_ci goto err; 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci if (!gelf_getehdr(elf->elf, &elf->ehdr)) { 103362306a36Sopenharmony_ci WARN_ELF("gelf_getehdr"); 103462306a36Sopenharmony_ci goto err; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (read_sections(elf)) 103862306a36Sopenharmony_ci goto err; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (read_symbols(elf)) 104162306a36Sopenharmony_ci goto err; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci if (read_relocs(elf)) 104462306a36Sopenharmony_ci goto err; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci return elf; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_cierr: 104962306a36Sopenharmony_ci elf_close(elf); 105062306a36Sopenharmony_ci return NULL; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic int elf_add_string(struct elf *elf, struct section *strtab, char *str) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci Elf_Data *data; 105662306a36Sopenharmony_ci Elf_Scn *s; 105762306a36Sopenharmony_ci int len; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (!strtab) 106062306a36Sopenharmony_ci strtab = find_section_by_name(elf, ".strtab"); 106162306a36Sopenharmony_ci if (!strtab) { 106262306a36Sopenharmony_ci WARN("can't find .strtab section"); 106362306a36Sopenharmony_ci return -1; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci s = elf_getscn(elf->elf, strtab->idx); 106762306a36Sopenharmony_ci if (!s) { 106862306a36Sopenharmony_ci WARN_ELF("elf_getscn"); 106962306a36Sopenharmony_ci return -1; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci data = elf_newdata(s); 107362306a36Sopenharmony_ci if (!data) { 107462306a36Sopenharmony_ci WARN_ELF("elf_newdata"); 107562306a36Sopenharmony_ci return -1; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci data->d_buf = str; 107962306a36Sopenharmony_ci data->d_size = strlen(str) + 1; 108062306a36Sopenharmony_ci data->d_align = 1; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci len = strtab->sh.sh_size; 108362306a36Sopenharmony_ci strtab->sh.sh_size += data->d_size; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci mark_sec_changed(elf, strtab, true); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci return len; 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cistruct section *elf_create_section(struct elf *elf, const char *name, 109162306a36Sopenharmony_ci size_t entsize, unsigned int nr) 109262306a36Sopenharmony_ci{ 109362306a36Sopenharmony_ci struct section *sec, *shstrtab; 109462306a36Sopenharmony_ci size_t size = entsize * nr; 109562306a36Sopenharmony_ci Elf_Scn *s; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci sec = malloc(sizeof(*sec)); 109862306a36Sopenharmony_ci if (!sec) { 109962306a36Sopenharmony_ci perror("malloc"); 110062306a36Sopenharmony_ci return NULL; 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci memset(sec, 0, sizeof(*sec)); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci INIT_LIST_HEAD(&sec->symbol_list); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci s = elf_newscn(elf->elf); 110762306a36Sopenharmony_ci if (!s) { 110862306a36Sopenharmony_ci WARN_ELF("elf_newscn"); 110962306a36Sopenharmony_ci return NULL; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci sec->name = strdup(name); 111362306a36Sopenharmony_ci if (!sec->name) { 111462306a36Sopenharmony_ci perror("strdup"); 111562306a36Sopenharmony_ci return NULL; 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci sec->idx = elf_ndxscn(s); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci sec->data = elf_newdata(s); 112162306a36Sopenharmony_ci if (!sec->data) { 112262306a36Sopenharmony_ci WARN_ELF("elf_newdata"); 112362306a36Sopenharmony_ci return NULL; 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci sec->data->d_size = size; 112762306a36Sopenharmony_ci sec->data->d_align = 1; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci if (size) { 113062306a36Sopenharmony_ci sec->data->d_buf = malloc(size); 113162306a36Sopenharmony_ci if (!sec->data->d_buf) { 113262306a36Sopenharmony_ci perror("malloc"); 113362306a36Sopenharmony_ci return NULL; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci memset(sec->data->d_buf, 0, size); 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci if (!gelf_getshdr(s, &sec->sh)) { 113962306a36Sopenharmony_ci WARN_ELF("gelf_getshdr"); 114062306a36Sopenharmony_ci return NULL; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci sec->sh.sh_size = size; 114462306a36Sopenharmony_ci sec->sh.sh_entsize = entsize; 114562306a36Sopenharmony_ci sec->sh.sh_type = SHT_PROGBITS; 114662306a36Sopenharmony_ci sec->sh.sh_addralign = 1; 114762306a36Sopenharmony_ci sec->sh.sh_flags = SHF_ALLOC; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* Add section name to .shstrtab (or .strtab for Clang) */ 115062306a36Sopenharmony_ci shstrtab = find_section_by_name(elf, ".shstrtab"); 115162306a36Sopenharmony_ci if (!shstrtab) 115262306a36Sopenharmony_ci shstrtab = find_section_by_name(elf, ".strtab"); 115362306a36Sopenharmony_ci if (!shstrtab) { 115462306a36Sopenharmony_ci WARN("can't find .shstrtab or .strtab section"); 115562306a36Sopenharmony_ci return NULL; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci sec->sh.sh_name = elf_add_string(elf, shstrtab, sec->name); 115862306a36Sopenharmony_ci if (sec->sh.sh_name == -1) 115962306a36Sopenharmony_ci return NULL; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci list_add_tail(&sec->list, &elf->sections); 116262306a36Sopenharmony_ci elf_hash_add(section, &sec->hash, sec->idx); 116362306a36Sopenharmony_ci elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci mark_sec_changed(elf, sec, true); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci return sec; 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cistatic struct section *elf_create_rela_section(struct elf *elf, 117162306a36Sopenharmony_ci struct section *sec, 117262306a36Sopenharmony_ci unsigned int reloc_nr) 117362306a36Sopenharmony_ci{ 117462306a36Sopenharmony_ci struct section *rsec; 117562306a36Sopenharmony_ci char *rsec_name; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci rsec_name = malloc(strlen(sec->name) + strlen(".rela") + 1); 117862306a36Sopenharmony_ci if (!rsec_name) { 117962306a36Sopenharmony_ci perror("malloc"); 118062306a36Sopenharmony_ci return NULL; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci strcpy(rsec_name, ".rela"); 118362306a36Sopenharmony_ci strcat(rsec_name, sec->name); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci rsec = elf_create_section(elf, rsec_name, elf_rela_size(elf), reloc_nr); 118662306a36Sopenharmony_ci free(rsec_name); 118762306a36Sopenharmony_ci if (!rsec) 118862306a36Sopenharmony_ci return NULL; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci rsec->data->d_type = ELF_T_RELA; 119162306a36Sopenharmony_ci rsec->sh.sh_type = SHT_RELA; 119262306a36Sopenharmony_ci rsec->sh.sh_addralign = elf_addr_size(elf); 119362306a36Sopenharmony_ci rsec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; 119462306a36Sopenharmony_ci rsec->sh.sh_info = sec->idx; 119562306a36Sopenharmony_ci rsec->sh.sh_flags = SHF_INFO_LINK; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci rsec->relocs = calloc(sec_num_entries(rsec), sizeof(struct reloc)); 119862306a36Sopenharmony_ci if (!rsec->relocs) { 119962306a36Sopenharmony_ci perror("calloc"); 120062306a36Sopenharmony_ci return NULL; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci sec->rsec = rsec; 120462306a36Sopenharmony_ci rsec->base = sec; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci return rsec; 120762306a36Sopenharmony_ci} 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_cistruct section *elf_create_section_pair(struct elf *elf, const char *name, 121062306a36Sopenharmony_ci size_t entsize, unsigned int nr, 121162306a36Sopenharmony_ci unsigned int reloc_nr) 121262306a36Sopenharmony_ci{ 121362306a36Sopenharmony_ci struct section *sec; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci sec = elf_create_section(elf, name, entsize, nr); 121662306a36Sopenharmony_ci if (!sec) 121762306a36Sopenharmony_ci return NULL; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci if (!elf_create_rela_section(elf, sec, reloc_nr)) 122062306a36Sopenharmony_ci return NULL; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci return sec; 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ciint elf_write_insn(struct elf *elf, struct section *sec, 122662306a36Sopenharmony_ci unsigned long offset, unsigned int len, 122762306a36Sopenharmony_ci const char *insn) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci Elf_Data *data = sec->data; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci if (data->d_type != ELF_T_BYTE || data->d_off) { 123262306a36Sopenharmony_ci WARN("write to unexpected data for section: %s", sec->name); 123362306a36Sopenharmony_ci return -1; 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci memcpy(data->d_buf + offset, insn, len); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci mark_sec_changed(elf, sec, true); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci return 0; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci/* 124462306a36Sopenharmony_ci * When Elf_Scn::sh_size is smaller than the combined Elf_Data::d_size 124562306a36Sopenharmony_ci * do you: 124662306a36Sopenharmony_ci * 124762306a36Sopenharmony_ci * A) adhere to the section header and truncate the data, or 124862306a36Sopenharmony_ci * B) ignore the section header and write out all the data you've got? 124962306a36Sopenharmony_ci * 125062306a36Sopenharmony_ci * Yes, libelf sucks and we need to manually truncate if we over-allocate data. 125162306a36Sopenharmony_ci */ 125262306a36Sopenharmony_cistatic int elf_truncate_section(struct elf *elf, struct section *sec) 125362306a36Sopenharmony_ci{ 125462306a36Sopenharmony_ci u64 size = sec->sh.sh_size; 125562306a36Sopenharmony_ci bool truncated = false; 125662306a36Sopenharmony_ci Elf_Data *data = NULL; 125762306a36Sopenharmony_ci Elf_Scn *s; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci s = elf_getscn(elf->elf, sec->idx); 126062306a36Sopenharmony_ci if (!s) { 126162306a36Sopenharmony_ci WARN_ELF("elf_getscn"); 126262306a36Sopenharmony_ci return -1; 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci for (;;) { 126662306a36Sopenharmony_ci /* get next data descriptor for the relevant section */ 126762306a36Sopenharmony_ci data = elf_getdata(s, data); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci if (!data) { 127062306a36Sopenharmony_ci if (size) { 127162306a36Sopenharmony_ci WARN("end of section data but non-zero size left\n"); 127262306a36Sopenharmony_ci return -1; 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci return 0; 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci if (truncated) { 127862306a36Sopenharmony_ci /* when we remove symbols */ 127962306a36Sopenharmony_ci WARN("truncated; but more data\n"); 128062306a36Sopenharmony_ci return -1; 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci if (!data->d_size) { 128462306a36Sopenharmony_ci WARN("zero size data"); 128562306a36Sopenharmony_ci return -1; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci if (data->d_size > size) { 128962306a36Sopenharmony_ci truncated = true; 129062306a36Sopenharmony_ci data->d_size = size; 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci size -= data->d_size; 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ciint elf_write(struct elf *elf) 129862306a36Sopenharmony_ci{ 129962306a36Sopenharmony_ci struct section *sec; 130062306a36Sopenharmony_ci Elf_Scn *s; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci if (opts.dryrun) 130362306a36Sopenharmony_ci return 0; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* Update changed relocation sections and section headers: */ 130662306a36Sopenharmony_ci list_for_each_entry(sec, &elf->sections, list) { 130762306a36Sopenharmony_ci if (sec->truncate) 130862306a36Sopenharmony_ci elf_truncate_section(elf, sec); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci if (sec_changed(sec)) { 131162306a36Sopenharmony_ci s = elf_getscn(elf->elf, sec->idx); 131262306a36Sopenharmony_ci if (!s) { 131362306a36Sopenharmony_ci WARN_ELF("elf_getscn"); 131462306a36Sopenharmony_ci return -1; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci /* Note this also flags the section dirty */ 131862306a36Sopenharmony_ci if (!gelf_update_shdr(s, &sec->sh)) { 131962306a36Sopenharmony_ci WARN_ELF("gelf_update_shdr"); 132062306a36Sopenharmony_ci return -1; 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci mark_sec_changed(elf, sec, false); 132462306a36Sopenharmony_ci } 132562306a36Sopenharmony_ci } 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci /* Make sure the new section header entries get updated properly. */ 132862306a36Sopenharmony_ci elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* Write all changes to the file. */ 133162306a36Sopenharmony_ci if (elf_update(elf->elf, ELF_C_WRITE) < 0) { 133262306a36Sopenharmony_ci WARN_ELF("elf_update"); 133362306a36Sopenharmony_ci return -1; 133462306a36Sopenharmony_ci } 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci elf->changed = false; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci return 0; 133962306a36Sopenharmony_ci} 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_civoid elf_close(struct elf *elf) 134262306a36Sopenharmony_ci{ 134362306a36Sopenharmony_ci if (elf->elf) 134462306a36Sopenharmony_ci elf_end(elf->elf); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci if (elf->fd > 0) 134762306a36Sopenharmony_ci close(elf->fd); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci /* 135062306a36Sopenharmony_ci * NOTE: All remaining allocations are leaked on purpose. Objtool is 135162306a36Sopenharmony_ci * about to exit anyway. 135262306a36Sopenharmony_ci */ 135362306a36Sopenharmony_ci} 1354