Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
584fd3b
Documentation
LICENSES
arch
block
certs
crypto
drivers
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
accounting
arch
bootconfig
bpf
build
cgroup
debugging
edid
firewire
firmware
gpio
hv
iio
include
io_uring
kvm
laptop
leds
lib
memory-model
objtool
Documentation
arch
include
.gitignore
Build
Makefile
builtin-check.c
builtin-orc.c
check.c
elf.c
objtool.c
orc_dump.c
orc_gen.c
special.c
sync-check.sh
weak.c
pci
pcmcia
perf
power
scripts
spi
testing
thermal
time
tracing
usb
virtio
vm
wmi
Makefile
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
tools
/
objtool
/
elf.c
Blame
Blame
Latest commit
History
History
1153 lines (928 loc) · 24.4 KB
Breadcrumbs
linux
/
tools
/
objtool
/
elf.c
Top
File metadata and controls
Code
Blame
1153 lines (928 loc) · 24.4 KB
Raw
// SPDX-License-Identifier: GPL-2.0-or-later /* * elf.c - ELF access library * * Adapted from kpatch (https://github.com/dynup/kpatch): * Copyright (C) 2013-2015 Josh Poimboeuf <jpoimboe@redhat.com> * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com> */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <objtool/builtin.h> #include <objtool/elf.h> #include <objtool/warn.h> #define MAX_NAME_LEN 128 static inline u32 str_hash(const char *str) { return jhash(str, strlen(str), 0); } static inline int elf_hash_bits(void) { return vmlinux ? ELF_HASH_BITS : 16; } #define elf_hash_add(hashtable, node, key) \ hlist_add_head(node, &hashtable[hash_min(key, elf_hash_bits())]) static void elf_hash_init(struct hlist_head *table) { __hash_init(table, 1U << elf_hash_bits()); } #define elf_hash_for_each_possible(name, obj, member, key) \ hlist_for_each_entry(obj, &name[hash_min(key, elf_hash_bits())], member) static bool symbol_to_offset(struct rb_node *a, const struct rb_node *b) { struct symbol *sa = rb_entry(a, struct symbol, node); struct symbol *sb = rb_entry(b, struct symbol, node); if (sa->offset < sb->offset) return true; if (sa->offset > sb->offset) return false; if (sa->len < sb->len) return true; if (sa->len > sb->len) return false; sa->alias = sb; return false; } static int symbol_by_offset(const void *key, const struct rb_node *node) { const struct symbol *s = rb_entry(node, struct symbol, node); const unsigned long *o = key; if (*o < s->offset) return -1; if (*o >= s->offset + s->len) return 1; return 0; } struct section *find_section_by_name(const struct elf *elf, const char *name) { struct section *sec; elf_hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name)) if (!strcmp(sec->name, name)) return sec; return NULL; } static struct section *find_section_by_index(struct elf *elf, unsigned int idx) { struct section *sec; elf_hash_for_each_possible(elf->section_hash, sec, hash, idx) if (sec->idx == idx) return sec; return NULL; } static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) { struct symbol *sym; elf_hash_for_each_possible(elf->symbol_hash, sym, hash, idx) if (sym->idx == idx) return sym; return NULL; } struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) { struct rb_node *node; rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { struct symbol *s = rb_entry(node, struct symbol, node); if (s->offset == offset && s->type != STT_SECTION) return s; } return NULL; } struct symbol *find_func_by_offset(struct section *sec, unsigned long offset) { struct rb_node *node; rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { struct symbol *s = rb_entry(node, struct symbol, node); if (s->offset == offset && s->type == STT_FUNC) return s; } return NULL; } struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset) { struct rb_node *node; rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { struct symbol *s = rb_entry(node, struct symbol, node); if (s->type != STT_SECTION) return s; } return NULL; } struct symbol *find_func_containing(struct section *sec, unsigned long offset) { struct rb_node *node; rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) { struct symbol *s = rb_entry(node, struct symbol, node); if (s->type == STT_FUNC) return s; } return NULL; } struct symbol *find_symbol_by_name(const struct elf *elf, const char *name) { struct symbol *sym; elf_hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name)) if (!strcmp(sym->name, name)) return sym; return NULL; } struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, unsigned long offset, unsigned int len) { struct reloc *reloc, *r = NULL; unsigned long o; if (!sec->reloc) return NULL; sec = sec->reloc; for_offset_range(o, offset, offset + len) { elf_hash_for_each_possible(elf->reloc_hash, reloc, hash, sec_offset_hash(sec, o)) { if (reloc->sec != sec) continue; if (reloc->offset >= offset && reloc->offset < offset + len) { if (!r || reloc->offset < r->offset) r = reloc; } } if (r) return r; } return NULL; } struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset) { return find_reloc_by_dest_range(elf, sec, offset, 1); } static int read_sections(struct elf *elf) { Elf_Scn *s = NULL; struct section *sec; size_t shstrndx, sections_nr; int i; if (elf_getshdrnum(elf->elf, §ions_nr)) { WARN_ELF("elf_getshdrnum"); return -1; } if (elf_getshdrstrndx(elf->elf, &shstrndx)) { WARN_ELF("elf_getshdrstrndx"); return -1; } for (i = 0; i < sections_nr; i++) { sec = malloc(sizeof(*sec)); if (!sec) { perror("malloc"); return -1; } memset(sec, 0, sizeof(*sec)); INIT_LIST_HEAD(&sec->symbol_list); INIT_LIST_HEAD(&sec->reloc_list); s = elf_getscn(elf->elf, i); if (!s) { WARN_ELF("elf_getscn"); return -1; } sec->idx = elf_ndxscn(s); if (!gelf_getshdr(s, &sec->sh)) { WARN_ELF("gelf_getshdr"); return -1; } sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name); if (!sec->name) { WARN_ELF("elf_strptr"); return -1; } if (sec->sh.sh_size != 0) { sec->data = elf_getdata(s, NULL); if (!sec->data) { WARN_ELF("elf_getdata"); return -1; } if (sec->data->d_off != 0 || sec->data->d_size != sec->sh.sh_size) { WARN("unexpected data attributes for %s", sec->name); return -1; } } sec->len = sec->sh.sh_size; list_add_tail(&sec->list, &elf->sections); elf_hash_add(elf->section_hash, &sec->hash, sec->idx); elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name)); } if (stats) printf("nr_sections: %lu\n", (unsigned long)sections_nr); /* sanity check, one more call to elf_nextscn() should return NULL */ if (elf_nextscn(elf->elf, s)) { WARN("section entry mismatch"); return -1; } return 0; } static void elf_add_symbol(struct elf *elf, struct symbol *sym) { struct list_head *entry; struct rb_node *pnode; sym->type = GELF_ST_TYPE(sym->sym.st_info); sym->bind = GELF_ST_BIND(sym->sym.st_info); sym->offset = sym->sym.st_value; sym->len = sym->sym.st_size; rb_add(&sym->node, &sym->sec->symbol_tree, symbol_to_offset); pnode = rb_prev(&sym->node); if (pnode) entry = &rb_entry(pnode, struct symbol, node)->list; else entry = &sym->sec->symbol_list; list_add(&sym->list, entry); elf_hash_add(elf->symbol_hash, &sym->hash, sym->idx); elf_hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name)); /* * Don't store empty STT_NOTYPE symbols in the rbtree. They * can exist within a function, confusing the sorting. */ if (!sym->len) rb_erase(&sym->node, &sym->sec->symbol_tree); } static int read_symbols(struct elf *elf) { struct section *symtab, *symtab_shndx, *sec; struct symbol *sym, *pfunc; int symbols_nr, i; char *coldstr; Elf_Data *shndx_data = NULL; Elf32_Word shndx; symtab = find_section_by_name(elf, ".symtab"); if (!symtab) { /* * A missing symbol table is actually possible if it's an empty * .o file. This can happen for thunk_64.o. */ return 0; } symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); if (symtab_shndx) shndx_data = symtab_shndx->data; symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize; for (i = 0; i < symbols_nr; i++) { sym = malloc(sizeof(*sym)); if (!sym) { perror("malloc"); return -1; } memset(sym, 0, sizeof(*sym)); sym->alias = sym; sym->idx = i; if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym, &shndx)) { WARN_ELF("gelf_getsymshndx"); goto err; } sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, sym->sym.st_name); if (!sym->name) { WARN_ELF("elf_strptr"); goto err; } if ((sym->sym.st_shndx > SHN_UNDEF && sym->sym.st_shndx < SHN_LORESERVE) || (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) { if (sym->sym.st_shndx != SHN_XINDEX) shndx = sym->sym.st_shndx; sym->sec = find_section_by_index(elf, shndx); if (!sym->sec) { WARN("couldn't find section for symbol %s", sym->name); goto err; } if (GELF_ST_TYPE(sym->sym.st_info) == STT_SECTION) { sym->name = sym->sec->name; sym->sec->sym = sym; } } else sym->sec = find_section_by_index(elf, 0); elf_add_symbol(elf, sym); } if (stats) printf("nr_symbols: %lu\n", (unsigned long)symbols_nr); /* Create parent/child links for any cold subfunctions */ list_for_each_entry(sec, &elf->sections, list) { list_for_each_entry(sym, &sec->symbol_list, list) { char pname[MAX_NAME_LEN + 1]; size_t pnamelen; if (sym->type != STT_FUNC) continue; if (sym->pfunc == NULL) sym->pfunc = sym; if (sym->cfunc == NULL) sym->cfunc = sym; coldstr = strstr(sym->name, ".cold"); if (!coldstr) continue; pnamelen = coldstr - sym->name; if (pnamelen > MAX_NAME_LEN) { WARN("%s(): parent function name exceeds maximum length of %d characters", sym->name, MAX_NAME_LEN); return -1; } strncpy(pname, sym->name, pnamelen); pname[pnamelen] = '\0'; pfunc = find_symbol_by_name(elf, pname); if (!pfunc) { WARN("%s(): can't find parent function", sym->name); return -1; } sym->pfunc = pfunc; pfunc->cfunc = sym; /* * Unfortunately, -fnoreorder-functions puts the child * inside the parent. Remove the overlap so we can * have sane assumptions. * * Note that pfunc->len now no longer matches * pfunc->sym.st_size. */ if (sym->sec == pfunc->sec && sym->offset >= pfunc->offset && sym->offset + sym->len == pfunc->offset + pfunc->len) { pfunc->len -= sym->len; } } } return 0; err: free(sym); return -1; } static struct section *elf_create_reloc_section(struct elf *elf, struct section *base, int reltype); int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset, unsigned int type, struct symbol *sym, int addend) { struct reloc *reloc; if (!sec->reloc && !elf_create_reloc_section(elf, sec, SHT_RELA)) return -1; reloc = malloc(sizeof(*reloc)); if (!reloc) { perror("malloc"); return -1; } memset(reloc, 0, sizeof(*reloc)); reloc->sec = sec->reloc; reloc->offset = offset; reloc->type = type; reloc->sym = sym; reloc->addend = addend; list_add_tail(&reloc->list, &sec->reloc->reloc_list); elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc)); sec->reloc->changed = true; return 0; } int elf_add_reloc_to_insn(struct elf *elf, struct section *sec, unsigned long offset, unsigned int type, struct section *insn_sec, unsigned long insn_off) { struct symbol *sym; int addend; if (insn_sec->sym) { sym = insn_sec->sym; addend = insn_off; } else { /* * The Clang assembler strips section symbols, so we have to * reference the function symbol instead: */ sym = find_symbol_containing(insn_sec, insn_off); if (!sym) { /* * Hack alert. This happens when we need to reference * the NOP pad insn immediately after the function. */ sym = find_symbol_containing(insn_sec, insn_off - 1); } if (!sym) { WARN("can't find symbol containing %s+0x%lx", insn_sec->name, insn_off); return -1; } addend = insn_off - sym->offset; } return elf_add_reloc(elf, sec, offset, type, sym, addend); } static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) { if (!gelf_getrel(sec->data, i, &reloc->rel)) { WARN_ELF("gelf_getrel"); return -1; } reloc->type = GELF_R_TYPE(reloc->rel.r_info); reloc->addend = 0; reloc->offset = reloc->rel.r_offset; *symndx = GELF_R_SYM(reloc->rel.r_info); return 0; } static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) { if (!gelf_getrela(sec->data, i, &reloc->rela)) { WARN_ELF("gelf_getrela"); return -1; } reloc->type = GELF_R_TYPE(reloc->rela.r_info); reloc->addend = reloc->rela.r_addend; reloc->offset = reloc->rela.r_offset; *symndx = GELF_R_SYM(reloc->rela.r_info); return 0; } static int read_relocs(struct elf *elf) { struct section *sec; struct reloc *reloc; int i; unsigned int symndx; unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0; list_for_each_entry(sec, &elf->sections, list) { if ((sec->sh.sh_type != SHT_RELA) && (sec->sh.sh_type != SHT_REL)) continue; sec->base = find_section_by_index(elf, sec->sh.sh_info); if (!sec->base) { WARN("can't find base section for reloc section %s", sec->name); return -1; } sec->base->reloc = sec; nr_reloc = 0; for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { reloc = malloc(sizeof(*reloc)); if (!reloc) { perror("malloc"); return -1; } memset(reloc, 0, sizeof(*reloc)); switch (sec->sh.sh_type) { case SHT_REL: if (read_rel_reloc(sec, i, reloc, &symndx)) return -1; break; case SHT_RELA: if (read_rela_reloc(sec, i, reloc, &symndx)) return -1; break; default: return -1; } reloc->sec = sec; reloc->idx = i; reloc->sym = find_symbol_by_index(elf, symndx); if (!reloc->sym) { WARN("can't find reloc entry symbol %d for %s", symndx, sec->name); return -1; } list_add_tail(&reloc->list, &sec->reloc_list); elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc)); nr_reloc++; } max_reloc = max(max_reloc, nr_reloc); tot_reloc += nr_reloc; } if (stats) { printf("max_reloc: %lu\n", max_reloc); printf("tot_reloc: %lu\n", tot_reloc); } return 0; } struct elf *elf_open_read(const char *name, int flags) { struct elf *elf; Elf_Cmd cmd; elf_version(EV_CURRENT); elf = malloc(sizeof(*elf)); if (!elf) { perror("malloc"); return NULL; } memset(elf, 0, offsetof(struct elf, sections)); INIT_LIST_HEAD(&elf->sections); elf_hash_init(elf->symbol_hash); elf_hash_init(elf->symbol_name_hash); elf_hash_init(elf->section_hash); elf_hash_init(elf->section_name_hash); elf_hash_init(elf->reloc_hash); elf->fd = open(name, flags); if (elf->fd == -1) { fprintf(stderr, "objtool: Can't open '%s': %s\n", name, strerror(errno)); goto err; } if ((flags & O_ACCMODE) == O_RDONLY) cmd = ELF_C_READ_MMAP; else if ((flags & O_ACCMODE) == O_RDWR) cmd = ELF_C_RDWR; else /* O_WRONLY */ cmd = ELF_C_WRITE; elf->elf = elf_begin(elf->fd, cmd, NULL); if (!elf->elf) { WARN_ELF("elf_begin"); goto err; } if (!gelf_getehdr(elf->elf, &elf->ehdr)) { WARN_ELF("gelf_getehdr"); goto err; } if (read_sections(elf)) goto err; if (read_symbols(elf)) goto err; if (read_relocs(elf)) goto err; return elf; err: elf_close(elf); return NULL; } static int elf_add_string(struct elf *elf, struct section *strtab, char *str) { Elf_Data *data; Elf_Scn *s; int len; if (!strtab) strtab = find_section_by_name(elf, ".strtab"); if (!strtab) { WARN("can't find .strtab section"); return -1; } s = elf_getscn(elf->elf, strtab->idx); if (!s) { WARN_ELF("elf_getscn"); return -1; } data = elf_newdata(s); if (!data) { WARN_ELF("elf_newdata"); return -1; } data->d_buf = str; data->d_size = strlen(str) + 1; data->d_align = 1; len = strtab->len; strtab->len += data->d_size; strtab->changed = true; return len; } struct symbol *elf_create_undef_symbol(struct elf *elf, const char *name) { struct section *symtab, *symtab_shndx; struct symbol *sym; Elf_Data *data; Elf_Scn *s; sym = malloc(sizeof(*sym)); if (!sym) { perror("malloc"); return NULL; } memset(sym, 0, sizeof(*sym)); sym->name = strdup(name); sym->sym.st_name = elf_add_string(elf, NULL, sym->name); if (sym->sym.st_name == -1) return NULL; sym->sym.st_info = GELF_ST_INFO(STB_GLOBAL, STT_NOTYPE); // st_other 0 // st_shndx 0 // st_value 0 // st_size 0 symtab = find_section_by_name(elf, ".symtab"); if (!symtab) { WARN("can't find .symtab"); return NULL; } s = elf_getscn(elf->elf, symtab->idx); if (!s) { WARN_ELF("elf_getscn"); return NULL; } data = elf_newdata(s); if (!data) { WARN_ELF("elf_newdata"); return NULL; } data->d_buf = &sym->sym; data->d_size = sizeof(sym->sym); data->d_align = 1; data->d_type = ELF_T_SYM; sym->idx = symtab->len / sizeof(sym->sym); symtab->len += data->d_size; symtab->changed = true; symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); if (symtab_shndx) { s = elf_getscn(elf->elf, symtab_shndx->idx); if (!s) { WARN_ELF("elf_getscn"); return NULL; } data = elf_newdata(s); if (!data) { WARN_ELF("elf_newdata"); return NULL; } data->d_buf = &sym->sym.st_size; /* conveniently 0 */ data->d_size = sizeof(Elf32_Word); data->d_align = 4; data->d_type = ELF_T_WORD; symtab_shndx->len += 4; symtab_shndx->changed = true; } sym->sec = find_section_by_index(elf, 0); elf_add_symbol(elf, sym); return sym; } struct section *elf_create_section(struct elf *elf, const char *name, unsigned int sh_flags, size_t entsize, int nr) { struct section *sec, *shstrtab; size_t size = entsize * nr; Elf_Scn *s; sec = malloc(sizeof(*sec)); if (!sec) { perror("malloc"); return NULL; } memset(sec, 0, sizeof(*sec)); INIT_LIST_HEAD(&sec->symbol_list); INIT_LIST_HEAD(&sec->reloc_list); s = elf_newscn(elf->elf); if (!s) { WARN_ELF("elf_newscn"); return NULL; } sec->name = strdup(name); if (!sec->name) { perror("strdup"); return NULL; } sec->idx = elf_ndxscn(s); sec->len = size; sec->changed = true; sec->data = elf_newdata(s); if (!sec->data) { WARN_ELF("elf_newdata"); return NULL; } sec->data->d_size = size; sec->data->d_align = 1; if (size) { sec->data->d_buf = malloc(size); if (!sec->data->d_buf) { perror("malloc"); return NULL; } memset(sec->data->d_buf, 0, size); } if (!gelf_getshdr(s, &sec->sh)) { WARN_ELF("gelf_getshdr"); return NULL; } sec->sh.sh_size = size; sec->sh.sh_entsize = entsize; sec->sh.sh_type = SHT_PROGBITS; sec->sh.sh_addralign = 1; sec->sh.sh_flags = SHF_ALLOC | sh_flags; /* Add section name to .shstrtab (or .strtab for Clang) */ shstrtab = find_section_by_name(elf, ".shstrtab"); if (!shstrtab) shstrtab = find_section_by_name(elf, ".strtab"); if (!shstrtab) { WARN("can't find .shstrtab or .strtab section"); return NULL; } sec->sh.sh_name = elf_add_string(elf, shstrtab, sec->name); if (sec->sh.sh_name == -1) return NULL; list_add_tail(&sec->list, &elf->sections); elf_hash_add(elf->section_hash, &sec->hash, sec->idx); elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name)); elf->changed = true; return sec; } static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base) { char *relocname; struct section *sec; relocname = malloc(strlen(base->name) + strlen(".rel") + 1); if (!relocname) { perror("malloc"); return NULL; } strcpy(relocname, ".rel"); strcat(relocname, base->name); sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rel), 0); free(relocname); if (!sec) return NULL; base->reloc = sec; sec->base = base; sec->sh.sh_type = SHT_REL; sec->sh.sh_addralign = 8; sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; sec->sh.sh_info = base->idx; sec->sh.sh_flags = SHF_INFO_LINK; return sec; } static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base) { char *relocname; struct section *sec; relocname = malloc(strlen(base->name) + strlen(".rela") + 1); if (!relocname) { perror("malloc"); return NULL; } strcpy(relocname, ".rela"); strcat(relocname, base->name); sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rela), 0); free(relocname); if (!sec) return NULL; base->reloc = sec; sec->base = base; sec->sh.sh_type = SHT_RELA; sec->sh.sh_addralign = 8; sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; sec->sh.sh_info = base->idx; sec->sh.sh_flags = SHF_INFO_LINK; return sec; } static struct section *elf_create_reloc_section(struct elf *elf, struct section *base, int reltype) { switch (reltype) { case SHT_REL: return elf_create_rel_reloc_section(elf, base); case SHT_RELA: return elf_create_rela_reloc_section(elf, base); default: return NULL; } } static int elf_rebuild_rel_reloc_section(struct section *sec, int nr) { struct reloc *reloc; int idx = 0, size; void *buf; /* Allocate a buffer for relocations */ size = nr * sizeof(GElf_Rel); buf = malloc(size); if (!buf) { perror("malloc"); return -1; } sec->data->d_buf = buf; sec->data->d_size = size; sec->data->d_type = ELF_T_REL; sec->sh.sh_size = size; idx = 0; list_for_each_entry(reloc, &sec->reloc_list, list) { reloc->rel.r_offset = reloc->offset; reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); gelf_update_rel(sec->data, idx, &reloc->rel); idx++; } return 0; } static int elf_rebuild_rela_reloc_section(struct section *sec, int nr) { struct reloc *reloc; int idx = 0, size; void *buf; /* Allocate a buffer for relocations with addends */ size = nr * sizeof(GElf_Rela); buf = malloc(size); if (!buf) { perror("malloc"); return -1; } sec->data->d_buf = buf; sec->data->d_size = size; sec->data->d_type = ELF_T_RELA; sec->sh.sh_size = size; idx = 0; list_for_each_entry(reloc, &sec->reloc_list, list) { reloc->rela.r_offset = reloc->offset; reloc->rela.r_addend = reloc->addend; reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); gelf_update_rela(sec->data, idx, &reloc->rela); idx++; } return 0; } static int elf_rebuild_reloc_section(struct elf *elf, struct section *sec) { struct reloc *reloc; int nr; nr = 0; list_for_each_entry(reloc, &sec->reloc_list, list) nr++; switch (sec->sh.sh_type) { case SHT_REL: return elf_rebuild_rel_reloc_section(sec, nr); case SHT_RELA: return elf_rebuild_rela_reloc_section(sec, nr); default: return -1; } } int elf_write_insn(struct elf *elf, struct section *sec, unsigned long offset, unsigned int len, const char *insn) { Elf_Data *data = sec->data; if (data->d_type != ELF_T_BYTE || data->d_off) { WARN("write to unexpected data for section: %s", sec->name); return -1; } memcpy(data->d_buf + offset, insn, len); elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY); elf->changed = true; return 0; } int elf_write_reloc(struct elf *elf, struct reloc *reloc) { struct section *sec = reloc->sec; if (sec->sh.sh_type == SHT_REL) { reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); reloc->rel.r_offset = reloc->offset; if (!gelf_update_rel(sec->data, reloc->idx, &reloc->rel)) { WARN_ELF("gelf_update_rel"); return -1; } } else { reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); reloc->rela.r_addend = reloc->addend; reloc->rela.r_offset = reloc->offset; if (!gelf_update_rela(sec->data, reloc->idx, &reloc->rela)) { WARN_ELF("gelf_update_rela"); return -1; } } elf->changed = true; return 0; } int elf_write(struct elf *elf) { struct section *sec; Elf_Scn *s; /* Update changed relocation sections and section headers: */ list_for_each_entry(sec, &elf->sections, list) { if (sec->changed) { if (sec->base && elf_rebuild_reloc_section(elf, sec)) { WARN("elf_rebuild_reloc_section"); return -1; } s = elf_getscn(elf->elf, sec->idx); if (!s) { WARN_ELF("elf_getscn"); return -1; } if (!gelf_update_shdr(s, &sec->sh)) { WARN_ELF("gelf_update_shdr"); return -1; } sec->changed = false; elf->changed = true; } } /* Make sure the new section header entries get updated properly. */ elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY); /* Write all changes to the file. */ if (elf_update(elf->elf, ELF_C_WRITE) < 0) { WARN_ELF("elf_update"); return -1; } elf->changed = false; return 0; } void elf_close(struct elf *elf) { struct section *sec, *tmpsec; struct symbol *sym, *tmpsym; struct reloc *reloc, *tmpreloc; if (elf->elf) elf_end(elf->elf); if (elf->fd > 0) close(elf->fd); list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { list_del(&sym->list); hash_del(&sym->hash); free(sym); } list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) { list_del(&reloc->list); hash_del(&reloc->hash); free(reloc); } list_del(&sec->list); free(sec); } free(elf); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
You can’t perform that action at this time.