From 5e25365244b3c2247d8f3d77a5c2ec8e29823a03 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 15 May 2014 17:17:29 -0500 Subject: [PATCH] Revert #186 (add dynamic symbol linking support) We merged PR #186 a little too hastily. It seg faults with the new parainstructions-section.patch in the integration test suite. Reverting it for now until we get it figured out. This reverts commit e1177e3a03d28858181d577745b159cd7ba694b0. This reverts commit 880e271841177551ccf20e7c758a666ad813c196. This reverts commit 2de5f6cbfb43ca4db2ecdb62770647f9d7c8ee6b. This reverts commit 38b7ac74ad9d1eafac17721af5841b184dfda00d. This reverts commit 108cd9f95ebfb69d81ef0a6ea41de23212d9cfc1. --- .gitignore | 3 +- kmod/core/core.c | 34 --- kmod/core/kpatch.h | 8 - kmod/patch/kpatch-patch-hook.c | 7 +- kmod/patch/kpatch.lds | 6 +- kpatch-build/Makefile | 10 +- kpatch-build/add-patches-section.c | 464 +++++++++++++++++++++++++++++ kpatch-build/create-diff-object.c | 361 ++-------------------- kpatch-build/kpatch-build | 4 +- kpatch-build/link-vmlinux-syms.c | 291 ++++++++++++++++++ kpatch-build/lookup.c | 245 --------------- kpatch-build/lookup.h | 19 -- 12 files changed, 799 insertions(+), 653 deletions(-) create mode 100644 kpatch-build/add-patches-section.c create mode 100644 kpatch-build/link-vmlinux-syms.c delete mode 100644 kpatch-build/lookup.c delete mode 100644 kpatch-build/lookup.h diff --git a/.gitignore b/.gitignore index 70706bfa1..94752691a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,8 +7,9 @@ *.swp .tmp_versions Module.symvers -kpatch-build/lookup +kpatch-build/add-patches-section kpatch-build/create-diff-object +kpatch-build/link-vmlinux-syms man/kpatch.1.gz man/kpatch-build.1.gz test/integration/test.log diff --git a/kmod/core/core.c b/kmod/core/core.c index 80c0372ad..679bd13b0 100644 --- a/kmod/core/core.c +++ b/kmod/core/core.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include "kpatch.h" @@ -425,10 +424,6 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace) int ret, i; struct kpatch_func *funcs, *func; int num_funcs = kpmod->patches_nr; - struct kpatch_dynrela *dynrela; - void *loc; - u64 val; - int size; if (!kpmod->mod || !kpmod->patches || !num_funcs) return -EINVAL; @@ -453,34 +448,6 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace) goto err_up; } - for (i = 0; i < kpmod->dynrelas_nr; i++) { - dynrela = &kpmod->dynrelas[i]; - switch (dynrela->type) { - case R_X86_64_PC32: - loc = (void *)dynrela->dest; - val = (u32)(dynrela->src - dynrela->dest); - size = 4; - break; - case R_X86_64_32S: - loc = (void *)dynrela->dest; - val = (s32)dynrela->src; - size = 4; - break; - default: - printk("unsupported rela type %ld for " - "0x%lx <- 0x%lx at index %d\n", - dynrela->type, dynrela->dest, - dynrela->src, i); - ret = -EINVAL; - goto err_put; - } - set_memory_rw((unsigned long)loc & PAGE_MASK, 1); - ret = probe_kernel_write(loc, &val, size); - set_memory_ro((unsigned long)loc & PAGE_MASK, 1); - if (ret) - goto err_put; - } - for (i = 0; i < num_funcs; i++) { func = &funcs[i]; @@ -597,7 +564,6 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace) kpatch_num_registered--; err_rollback: kpatch_remove_funcs_from_filter(funcs, num_funcs); -err_put: module_put(kpmod->mod); err_up: up(&kpatch_mutex); diff --git a/kmod/core/kpatch.h b/kmod/core/kpatch.h index b380bae05..ecb148568 100644 --- a/kmod/core/kpatch.h +++ b/kmod/core/kpatch.h @@ -30,12 +30,6 @@ struct kpatch_patch { unsigned long old_size; }; -struct kpatch_dynrela { - unsigned long dest; - unsigned long src; - unsigned long type; -}; - #ifdef __KERNEL__ #include @@ -46,9 +40,7 @@ struct kpatch_internal; struct kpatch_module { struct module *mod; struct kpatch_patch *patches; - struct kpatch_dynrela *dynrelas; int patches_nr; - int dynrelas_nr; bool enabled; struct kpatch_internal *internal; /* used internally by core module */ }; diff --git a/kmod/patch/kpatch-patch-hook.c b/kmod/patch/kpatch-patch-hook.c index ca8d5d61a..92f27a1d1 100644 --- a/kmod/patch/kpatch-patch-hook.c +++ b/kmod/patch/kpatch-patch-hook.c @@ -29,7 +29,6 @@ module_param(replace, bool, S_IRUGO); MODULE_PARM_DESC(replace, "replace all previously loaded patch modules"); extern char __kpatch_patches, __kpatch_patches_end; -extern char __kpatch_dynrelas, __kpatch_dynrelas_end; static struct kpatch_module kpmod; static struct kobject *patch_kobj; @@ -72,15 +71,13 @@ static struct kobj_attribute patch_enabled_attr = static int __init patch_init(void) { + struct kpatch_patch *patches; int ret; kpmod.mod = THIS_MODULE; kpmod.patches = (struct kpatch_patch *)&__kpatch_patches; kpmod.patches_nr = (&__kpatch_patches_end - &__kpatch_patches) / - sizeof(*kpmod.patches); - kpmod.dynrelas = (struct kpatch_dynrela *)&__kpatch_dynrelas; - kpmod.dynrelas_nr = (&__kpatch_dynrelas_end - &__kpatch_dynrelas) / - sizeof(*kpmod.dynrelas); + sizeof(*patches); patch_kobj = kobject_create_and_add(THIS_MODULE->name, kpatch_patches_kobj); diff --git a/kmod/patch/kpatch.lds b/kmod/patch/kpatch.lds index 7faef5da5..80bee8328 100644 --- a/kmod/patch/kpatch.lds +++ b/kmod/patch/kpatch.lds @@ -1,4 +1,2 @@ -__kpatch_patches = ADDR(.kpatch.patches); -__kpatch_patches_end = ADDR(.kpatch.patches) + SIZEOF(.kpatch.patches); -__kpatch_dynrelas = ADDR(.kpatch.dynrelas); -__kpatch_dynrelas_end = ADDR(.kpatch.dynrelas) + SIZEOF(.kpatch.dynrelas); +__kpatch_patches = ADDR(.patches); +__kpatch_patches_end = ADDR(.patches) + SIZEOF(.patches); diff --git a/kpatch-build/Makefile b/kpatch-build/Makefile index c7835090f..2eba2d6d5 100644 --- a/kpatch-build/Makefile +++ b/kpatch-build/Makefile @@ -3,12 +3,16 @@ include ../Makefile.inc CFLAGS += -I../kmod/patch -Wall -g LDFLAGS = -lelf -TARGETS = create-diff-object +TARGETS = create-diff-object add-patches-section link-vmlinux-syms + all: $(TARGETS) -create-diff-object: create-diff-object.c list.h lookup.c lookup.h - $(CC) $(CFLAGS) create-diff-object.c lookup.c -o $@ $(LDFLAGS) +%: %.c + $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) + +create-diff-object: create-diff-object.c list.h + $(CC) $(CFLAGS) create-diff-object.c -o $@ $(LDFLAGS) install: all $(INSTALL) -d $(LIBEXECDIR) diff --git a/kpatch-build/add-patches-section.c b/kpatch-build/add-patches-section.c new file mode 100644 index 000000000..b10249417 --- /dev/null +++ b/kpatch-build/add-patches-section.c @@ -0,0 +1,464 @@ +/* + * add-patches-section.c + * + * Copyright (C) 2014 Seth Jennings + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, + * 02110-1301, USA. + */ + +/* + * This tool takes an elf object, the output of create-diff-object + * and the base vmlinux as arguments and adds two new sections + * to the elf object; .patches and .rela.patches. + * + * These two sections allow the kpatch core modules to know which + * functions are overridden by the patch module. + * + * For each struct kpatch_patch entry in the .patches section, the core + * module will register as an ftrace handler for the old function. The new + * function will return to the caller of the old function, not the old function + * itself, bypassing the old function. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kpatch.h" + +#define ERROR(format, ...) \ + error(1, 0, "%s: %d: " format, __FUNCTION__, __LINE__, ##__VA_ARGS__) + +struct section { + Elf_Scn *scn; + GElf_Shdr sh; +}; + +enum symaction { + NOOP, /* do nothing, default */ + PATCH, /* sym is a patched function */ + LINK, /* sym is a non-exported global sym */ +}; + +struct sym { + struct sym *next; + GElf_Sym sym; + char *name; + int index; + enum symaction action; + unsigned long vm_addr; + size_t vm_len; +}; + +struct symlist { + struct sym *head, *tail; + size_t len; +}; + +struct elf { + Elf *elf; + int fd; + size_t shstrndx; + struct section symtab, shstrtab; +}; + +#define for_each_sym(list, iter) \ + for((iter) = (list)->head; (iter); (iter) = (iter)->next) + +enum elfmode { + RDONLY, + RDWR +}; + +static void open_elf(char *path, enum elfmode elfmode, struct elf *elf) +{ + mode_t mode; + Elf_Cmd cmd; + + switch(elfmode) { + case RDONLY: + mode = O_RDONLY; + cmd = ELF_C_READ_MMAP; + break; + case RDWR: + mode = O_RDWR; + cmd = ELF_C_RDWR; + break; + } + + if ((elf->fd = open(path, mode, 0)) < 0) + ERROR("open"); + + elf->elf = elf_begin(elf->fd, cmd, NULL); + if (!elf->elf) { + printf("%s\n", elf_errmsg(-1)); + ERROR("elf_begin"); + } + + if (elf_getshdrstrndx(elf->elf, &elf->shstrndx)) + ERROR("elf_getshdrstrndx"); +} + +static void insert_sym(struct symlist *list, GElf_Sym *sym, char *name, + int index) +{ + struct sym *newsym; + + newsym = malloc(sizeof(*newsym)); + if (!newsym) + ERROR("malloc"); + memset(newsym, 0, sizeof(*newsym)); + newsym->sym = *sym; + newsym->name = name; + newsym->index = index; + + if (list->tail) + list->tail->next = newsym; + if (!list->head) + list->head = newsym; + list->tail = newsym; +} + +static void find_section_by_name(struct elf *elf, char *name, struct section *sec) +{ + Elf_Scn *scn = NULL; + GElf_Shdr sh; + char *secname; + + while ((scn = elf_nextscn(elf->elf, scn))) { + if (!gelf_getshdr(scn, &sh)) + ERROR("gelf_getshdr"); + + secname = elf_strptr(elf->elf, elf->shstrndx, sh.sh_name); + if (!secname) + ERROR("elf_strptr scn"); + + if (!strcmp(secname, name)) + break; + } + + if (!scn) + ERROR("no section %s found", name); + + sec->scn = scn; + sec->sh = sh; +} + +static void create_symlist(struct elf *elf, struct symlist *symlist) +{ + Elf_Scn *scn = elf->symtab.scn; + GElf_Shdr *sh = &elf->symtab.sh; + GElf_Sym sym; + Elf_Data *data; + char *name; + int i; + + /* get symtab data buffer */ + data = elf_getdata(scn, NULL); + if (!data) + ERROR("elf_getdata"); + + symlist->len = sh->sh_size / sh->sh_entsize; + for (i = 0; i < symlist->len; i++) { + if (!gelf_getsym(data, i, &sym)) + ERROR("gelf_getsym"); + + name = elf_strptr(elf->elf, sh->sh_link, sym.st_name); + if(!name) + ERROR("elf_strptr sym"); + + insert_sym(symlist, &sym, name, i); + } +} + +static struct sym *find_symbol_by_name(struct symlist *list, struct sym *sym, + char *hint) +{ + struct sym *cur, *ret = NULL; + char *name = sym->name, *curfile = NULL; + + /* try to find a local symbol in the hint file first */ + if (hint && GELF_ST_BIND(sym->sym.st_info) == STB_LOCAL) { + for_each_sym(list, cur) { + if (GELF_ST_TYPE(cur->sym.st_info) == STT_FILE) + curfile = cur->name; + if (!curfile || strcmp(curfile, hint)) + continue; + if (!strcmp(cur->name, name)) { + if (ret) + ERROR("unresolvable symbol ambiguity for symbol '%s' in file '%s'", name, hint); + ret = cur; + } + } + } + + if (ret) + return ret; + + /* search globally for the symbol */ + for_each_sym(list, cur) + if (GELF_ST_BIND(sym->sym.st_info) == STB_GLOBAL && + !strcmp(cur->name, name)) + return cur; + + return NULL; +} + +/* + * TODO: de-dup common code above these point with code + * in link-vmlinux-syms.c + */ + +int main(int argc, char **argv) +{ + struct symlist symlist, symlistv; + struct sym *cur, *vsym; + struct elf elf, elfv; + void *buf; + struct kpatch_patch *patches_data; + GElf_Rela *relas_data; + int patches_nr = 0, i, patches_size, relas_size, len; + int patches_offset, relas_offset, patches_index, relas_index; + struct section symtab; + Elf_Scn *scn; + Elf_Data *data; + GElf_Shdr sh, *shp; + GElf_Ehdr eh; + GElf_Sym sym; + char *hint = NULL; + + /* set elf version (required by libelf) */ + if (elf_version(EV_CURRENT) == EV_NONE) + ERROR("elf_version"); + + memset(&elf, 0, sizeof(elf)); + memset(&elfv, 0, sizeof(elfv)); + open_elf(argv[1], RDWR, &elf); + open_elf(argv[2], RDONLY, &elfv); + + find_section_by_name(&elf, ".symtab", &(elf.symtab)); + find_section_by_name(&elfv, ".symtab", &(elfv.symtab)); + + find_section_by_name(&elf, ".shstrtab", &(elf.shstrtab)); + + memset(&symlist, 0, sizeof(symlist)); + memset(&symlistv, 0, sizeof(symlistv)); + create_symlist(&elf, &symlist); + create_symlist(&elfv, &symlistv); + + /* lookup patched functions in vmlinux */ + for_each_sym(&symlist, cur) { + if (GELF_ST_TYPE(cur->sym.st_info) == STT_FILE) + hint = cur->name; + + if (GELF_ST_TYPE(cur->sym.st_info) != STT_FUNC) + continue; + + printf("found patched function %s\n", cur->name); + + vsym = find_symbol_by_name(&symlistv, cur, hint); + if (!vsym) + ERROR("couldn't find patched function in vmlinux"); + cur->vm_addr = vsym->sym.st_value; + cur->vm_len = vsym->sym.st_size; + cur->action = PATCH; + printf("original function at address %016lx (length %zu)\n", + cur->vm_addr, cur->vm_len); + patches_nr++; + } + + elf_end(elfv.elf); + close(elfv.fd); + + if (!patches_nr) + ERROR("no patched functions"); + + printf("patches_nr = %d\n", patches_nr); + + /* allocate new section data buffers */ + patches_size = sizeof(*patches_data) * patches_nr; + patches_data = malloc(patches_size); + if (!patches_data) + ERROR("malloc"); + memset(patches_data, 0, patches_size); + + relas_size = sizeof(*relas_data) * patches_nr; + relas_data = malloc(relas_size); + if (!relas_data) + ERROR("malloc"); + memset(relas_data, 0, relas_size); + + printf("patches_size = %d\n",patches_size); + printf("relas_size = %d\n",relas_size); + + /* populate new section data buffers */ + i = 0; + for_each_sym(&symlist, cur) { + if (cur->action != PATCH) + continue; + + patches_data[i].old_addr = cur->vm_addr; + patches_data[i].old_size = cur->vm_len; + patches_data[i].new_size = cur->sym.st_size; + + /* + * Use a relocation to set patches_data[i].new_addr at module + * loading time. + */ + relas_data[i].r_offset = i * sizeof(struct kpatch_patch); + relas_data[i].r_info = GELF_R_INFO(cur->index, R_X86_64_64); + + i++; + } + + /* get next section index from elf header */ + if (!gelf_getehdr(elf.elf, &eh)) + ERROR("gelf_getehdr"); + patches_index = eh.e_shnum; + relas_index = patches_index + 1; + + /* add new section names to shstrtab */ + scn = elf.shstrtab.scn; + shp = &elf.shstrtab.sh; + + data = elf_getdata(scn, NULL); + if (!data) + ERROR("elf_getdata"); + + len = strlen(".patches") + strlen(".rela.patches") + 2; + buf = malloc(data->d_size + len); + memcpy(buf, data->d_buf, data->d_size); + + data->d_buf = buf; + buf = data->d_buf + data->d_size; + + len = strlen(".patches") + 1; + memcpy(buf, ".patches", len); + patches_offset = buf - data->d_buf; + printf("patches_offset = %d\n", patches_offset); + buf += len; + len = strlen(".rela.patches") + 1; + memcpy(buf, ".rela.patches", len); + relas_offset = buf - data->d_buf; + printf("relas_offset = %d\n", relas_offset); + buf += len; + data->d_size = buf - data->d_buf; + + if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY)) + ERROR("elf_flagdata"); + + if (!gelf_update_shdr(scn, shp)) + ERROR("gelf_update_shdr"); + + /* get symtab vars */ + find_section_by_name(&elf, ".symtab", &symtab); + scn = symtab.scn; + shp = &symtab.sh; + + data = elf_getdata(scn, NULL); + if (!data) + ERROR("elf_getdata"); + + /* add new section symbols to symtab */ + len = sizeof(GElf_Sym) * 2; + buf = malloc(data->d_size + len); + memcpy(buf, data->d_buf, data->d_size); + + data->d_buf = buf; + buf = data->d_buf + data->d_size; + + memset(&sym, 0, sizeof(GElf_Sym)); + sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION); + + len = sizeof(GElf_Sym); + sym.st_shndx = patches_index; + memcpy(buf, &sym, len); + buf += len; + sym.st_shndx = relas_index; + memcpy(buf, &sym, len); + buf += len; + data->d_size = buf - data->d_buf; + + if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY)) + ERROR("elf_flagdata"); + + if (!gelf_update_shdr(scn, shp)) + ERROR("gelf_update_shdr"); + + /* create .patches section */ + scn = elf_newscn(elf.elf); + if (!scn) + ERROR("elf_newscn"); + + data = elf_newdata(scn); + if (!data) + ERROR("elf_newdata"); + + data->d_size = patches_size; + data->d_buf = patches_data; + data->d_type = ELF_T_BYTE; + + memset(&sh, 0, sizeof(sh)); + sh.sh_type = SHT_PROGBITS; + sh.sh_name = patches_offset; + sh.sh_entsize = sizeof(struct kpatch_patch); + sh.sh_addralign = 8; + sh.sh_flags = SHF_ALLOC; + sh.sh_size = data->d_size; + + if (!gelf_update_shdr(scn, &sh)) + ERROR("gelf_update_shdr"); + + /* create .rela.patches section */ + scn = elf_newscn(elf.elf); + if (!scn) + ERROR("elf_newscn"); + + data = elf_newdata(scn); + if (!data) + ERROR("elf_newdata"); + + data->d_size = relas_size; + data->d_buf = relas_data; + data->d_type = ELF_T_RELA; + + memset(&sh, 0, sizeof(sh)); + sh.sh_type = SHT_RELA; + sh.sh_name = relas_offset; + sh.sh_entsize = sizeof(GElf_Rela); + sh.sh_addralign = 8; + sh.sh_flags = SHF_ALLOC; + sh.sh_link = elf_ndxscn(elf.symtab.scn); + sh.sh_info = patches_index; + sh.sh_size = data->d_size; + + if (!gelf_update_shdr(scn, &sh)) + ERROR("gelf_update_shdr"); + + if (elf_update(elf.elf, ELF_C_WRITE) < 0) + ERROR("elf_update %s", elf_errmsg(-1)); + + elf_end(elf.elf); + close(elf.fd); + + return 0; +} diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 0f47d13bc..cd587309c 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -46,8 +46,6 @@ #include #include "list.h" -#include "lookup.h" -#include "kpatch.h" #define ERROR(format, ...) \ error(1, 0, "%s: %d: " format, __FUNCTION__, __LINE__, ##__VA_ARGS__) @@ -136,7 +134,6 @@ struct kpatch_elf { Elf *elf; struct list_head sections; struct list_head symbols; - int sections_nr; }; /******************* @@ -278,7 +275,6 @@ void kpatch_create_section_list(struct kpatch_elf *kelf) if (elf_getshdrnum(kelf->elf, §ions_nr)) ERROR("elf_getshdrnum"); - kelf->sections_nr = sections_nr; /* * elf_getshdrnum() includes section index 0 but elf_nextscn @@ -891,16 +887,32 @@ void kpatch_generate_output(struct kpatch_elf *kelf, struct kpatch_elf **kelfout INIT_LIST_HEAD(&out->symbols); /* copy to output kelf sections, link to kelf, and reindex */ - index = 1; + index = 0; list_for_each_entry_safe(sec, safe, &kelf->sections, list) { if (!sec->include) continue; list_del(&sec->list); list_add_tail(&sec->list, &out->sections); - sec->index = index++; + sec->index = ++index; + } + + /* + * Search symbol table for local functions and objects whose sections + * are not included, and modify them to be non-local. + */ + list_for_each_entry(sym, &kelf->symbols, list) { + if ((sym->type == STT_OBJECT || + sym->type == STT_FUNC) && + !sym->sec->include) { + sym->type = STT_NOTYPE; + sym->bind = STB_GLOBAL; + sym->sym.st_info = GELF_ST_INFO(STB_GLOBAL, STT_NOTYPE); + sym->sym.st_shndx = SHN_UNDEF; + sym->sec = NULL; + sym->sym.st_size = 0; + } } - out->sections_nr = index; /* * Copy functions to the output kelf and reindex. Once the symbol is @@ -1345,299 +1357,6 @@ void kpatch_create_symtab(struct kpatch_elf *kelf) find_section_by_name(&kelf->sections, ".shstrtab")->index; } -void kpatch_create_patches_sections(struct kpatch_elf *kelf, - struct lookup_table *table, char *hint) -{ - int nr, size, index; - struct section *sec, *relasec; - struct symbol *sym; - struct rela *rela; - struct lookup_result result; - struct kpatch_patch *patches; - - /* count patched functions */ - nr = 0; - list_for_each_entry(sym, &kelf->symbols, list) - if (sym->type == STT_FUNC && sym->sec->include) - nr++; - - /* create .kpatch.patches */ - - /* allocate section resources */ - ALLOC_LINK(sec, &kelf->sections); - size = nr * sizeof(*patches); - patches = malloc(size); - if (!patches) - ERROR("malloc"); - sec->name = ".kpatch.patches"; - sec->index = kelf->sections_nr++; - - /* set data */ - sec->data = malloc(sizeof(*sec->data)); - if (!sec->data) - ERROR("malloc"); - sec->data->d_buf = patches; - sec->data->d_size = size; - sec->data->d_type = ELF_T_BYTE; - - /* set section header */ - sec->sh.sh_type = SHT_PROGBITS; - sec->sh.sh_entsize = sizeof(*patches); - sec->sh.sh_addralign = 8; - sec->sh.sh_flags = SHF_ALLOC; - sec->sh.sh_size = size; - - /* create .rela.patches */ - - /* allocate section resources */ - ALLOC_LINK(relasec, &kelf->sections); - relasec->name = ".rela.kpatch.patches"; - relasec->index = kelf->sections_nr++; - relasec->base = sec; - INIT_LIST_HEAD(&relasec->relas); - - /* set data, buffers generated by kpatch_rebuild_rela_section_data() */ - relasec->data = malloc(sizeof(*relasec->data)); - if (!relasec->data) - ERROR("malloc"); - - /* set section header */ - relasec->sh.sh_type = SHT_RELA; - relasec->sh.sh_entsize = sizeof(GElf_Rela); - relasec->sh.sh_addralign = 8; - - relasec->sh.sh_link = - find_section_by_name(&kelf->sections, ".symtab")->index; - relasec->sh.sh_info = sec->index; - - /* populate sections */ - index = 0; - list_for_each_entry(sym, &kelf->symbols, list) { - if (sym->type == STT_FUNC && sym->sec->include) { - if (sym->bind == STB_LOCAL) { - if (lookup_local_symbol(table, sym->name, - hint, &result)) - ERROR("lookup_local_symbol %s (%s)", - sym->name, hint); - } else { - if(lookup_global_symbol(table, sym->name, - &result)) - ERROR("lookup_global_symbol %s", - sym->name); - } - log_debug("lookup for %s @ 0x%016lx len %lu\n", - sym->name, result.value, result.size); - - /* add entry in text section */ - patches[index].old_addr = result.value; - patches[index].old_size = result.size; - patches[index].new_size = sym->sym.st_size; - - /* - * Add a relocation that will populate - * the patches[index].new_addr field at - * module load time. - */ - ALLOC_LINK(rela, &relasec->relas); - rela->sym = sym; - rela->type = R_X86_64_64; - rela->addend = 0; - rela->offset = index * sizeof(*patches); - - index++; - } - } - - /* sanity check, index should equal nr */ - if (index != nr) - ERROR("size mismatch in patches sections"); - -} - -void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf, - struct lookup_table *table, char *hint) -{ - int nr, size, index; - struct section *sec, *relasec; - struct rela *rela, *dynrela, *safe; - struct lookup_result result; - struct kpatch_dynrela *dynrelas; - - /* count rela entries that need to be dynamic */ - nr = 0; - list_for_each_entry(sec, &kelf->sections, list) { - if (!is_rela_section(sec)) - continue; - if (!strcmp(sec->name, ".rela.kpatch.patches")) - continue; - list_for_each_entry(rela, &sec->relas, list) { - if (lookup_is_exported_symbol(table, rela->sym->name)) - continue; - if (rela->sym->bind != STB_LOCAL || - !rela->sym->sec->include) - nr++; - } - } - - /* create .kpatch.dynrelas*/ - - /* allocate section resources */ - ALLOC_LINK(sec, &kelf->sections); - size = nr * sizeof(*dynrelas); - dynrelas = malloc(size); - if (!dynrelas) - ERROR("malloc"); - sec->name = ".kpatch.dynrelas"; - sec->index = kelf->sections_nr++; - - /* set data */ - sec->data = malloc(sizeof(*sec->data)); - if (!sec->data) - ERROR("malloc"); - sec->data->d_buf = dynrelas; - sec->data->d_size = size; - sec->data->d_type = ELF_T_BYTE; - - /* set section header */ - sec->sh.sh_type = SHT_PROGBITS; - sec->sh.sh_entsize = sizeof(*dynrelas); - sec->sh.sh_addralign = 8; - sec->sh.sh_flags = SHF_ALLOC; - sec->sh.sh_size = size; - - /* create .rela.kpatch.dynrelas*/ - - /* allocate section resources */ - ALLOC_LINK(relasec, &kelf->sections); - relasec->name = ".rela.kpatch.dynrelas"; - relasec->index = kelf->sections_nr++; - relasec->base = sec; - INIT_LIST_HEAD(&relasec->relas); - - /* set data, buffers generated by kpatch_rebuild_rela_section_data() */ - relasec->data = malloc(sizeof(*relasec->data)); - if (!relasec->data) - ERROR("malloc"); - - /* set section header */ - relasec->sh.sh_type = SHT_RELA; - relasec->sh.sh_entsize = sizeof(GElf_Rela); - relasec->sh.sh_addralign = 8; - - relasec->sh.sh_link = - find_section_by_name(&kelf->sections, ".symtab")->index; - relasec->sh.sh_info = sec->index; - - /* populate sections (reuse sec for iterator here) */ - index = 0; - list_for_each_entry(sec, &kelf->sections, list) { - if (!is_rela_section(sec)) - continue; - if (!strcmp(sec->name, ".rela.kpatch.patches") || - !strcmp(sec->name, ".rela.kpatch.dynrelas")) - continue; - list_for_each_entry_safe(rela, safe, &sec->relas, list) { - if (lookup_is_exported_symbol(table, rela->sym->name)) - continue; - if (rela->sym->bind != STB_LOCAL || - !rela->sym->sec->include) { - if (rela->sym->bind == STB_LOCAL) { - if (lookup_local_symbol(table, rela->sym->name, - hint, &result)) - ERROR("lookup_local_symbol %s (%s)", - rela->sym->name, hint); - } else { - if(lookup_global_symbol(table, rela->sym->name, - &result)) - ERROR("lookup_global_symbol %s", - rela->sym->name); - } - log_debug("lookup for %s @ 0x%016lx len %lu\n", - rela->sym->name, result.value, result.size); - - /* dest filed in by rela entry below */ - dynrelas[index].src = result.value + rela->addend; - dynrelas[index].type = rela->type; - - ALLOC_LINK(dynrela, &relasec->relas); - dynrela->sym = sec->base->sym; - dynrela->type = R_X86_64_64; - dynrela->addend = rela->offset; - dynrela->offset = index * sizeof(*dynrelas); - - list_del(&rela->list); - free(rela); - - index++; - } - } - } - - - /* sanity check, index should equal nr */ - if (index != nr) - ERROR("size mismatch in dynrelas sections"); -} - -/* - * This function strips out symbols that were referenced by changed rela - * sections, but the rela entries that referenced them were converted to - * dynrelas and are no longer needed. - */ -void kpatch_strip_unneeded_syms(struct lookup_table *table, - struct kpatch_elf *kelf) -{ - struct symbol *sym, *safe; - int index; - - list_for_each_entry_safe(sym, safe, &kelf->symbols, list) { - if (lookup_is_exported_symbol(table, sym->name)) - continue; - if ((sym->type == STT_NOTYPE && sym->bind == STB_GLOBAL) || - (sym->sec && !sym->sec->include)) { - list_del(&sym->list); - free(sym); - } - } - - /* reindex */ - index = 0; - list_for_each_entry(sym, &kelf->symbols, list) - sym->index = index++; -} - -void kpatch_rebuild_rela_section_data(struct section *sec) -{ - struct rela *rela; - int nr = 0, index = 0, size; - GElf_Rela *relas; - - list_for_each_entry(rela, &sec->relas, list) - nr++; - - size = nr * sizeof(*relas); - relas = malloc(size); - if (!relas) - ERROR("malloc"); - - sec->data->d_buf = relas; - sec->data->d_size = size; - /* d_type remains ELF_T_RELA */ - - sec->sh.sh_size = size; - - list_for_each_entry(rela, &sec->relas, list) { - relas[index].r_offset = rela->offset; - relas[index].r_addend = rela->addend; - relas[index].r_info = GELF_R_INFO(rela->sym->index, rela->type); - index++; - } - - /* sanity check, index should equal nr */ - if (index != nr) - ERROR("size mismatch in rebuilt rela section"); -} - void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile) { int fd; @@ -1683,18 +1402,16 @@ void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile) if (!data) ERROR("elf_newdata"); - if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY)) - ERROR("elf_flagdata"); - - data->d_type = sec->data->d_type; - data->d_buf = sec->data->d_buf; - data->d_size = sec->data->d_size; + *data = *sec->data; if(!gelf_getshdr(scn, &sh)) ERROR("gelf_getshdr"); sh = sec->sh; + if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY)) + ERROR("elf_flagdata"); + if (!gelf_update_shdr(scn, &sh)) ERROR("gelf_update_shdr"); } @@ -1709,12 +1426,12 @@ void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile) } struct arguments { - char *args[4]; + char *args[3]; int debug; int inventory; }; -static char args_doc[] = "original.o patched.o vmlinux output.o"; +static char args_doc[] = "original.o patched.o output.o"; static struct argp_option options[] = { {"debug", 'd', 0, 0, "Show debug output" }, @@ -1737,13 +1454,13 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) arguments->inventory = 1; break; case ARGP_KEY_ARG: - if (state->arg_num >= 4) + if (state->arg_num >= 3) /* Too many arguments. */ argp_usage (state); arguments->args[state->arg_num] = arg; break; case ARGP_KEY_END: - if (state->arg_num < 4) + if (state->arg_num < 3) /* Not enough arguments. */ argp_usage (state); break; @@ -1761,10 +1478,6 @@ int main(int argc, char *argv[]) char *outfile; struct arguments arguments; int num_changed; - struct lookup_table *vmlinux; - struct section *sec; - struct symbol *sym; - char *hint; arguments.debug = 0; arguments.inventory = 0; @@ -1778,8 +1491,7 @@ int main(int argc, char *argv[]) kelf_base = kpatch_elf_open(arguments.args[0]); kelf_patched = kpatch_elf_open(arguments.args[1]); - vmlinux = lookup_open(arguments.args[2]); - outfile = arguments.args[3]; + outfile = arguments.args[2]; kpatch_compare_elf_headers(kelf_base->elf, kelf_patched->elf); kpatch_check_program_headers(kelf_base->elf); @@ -1823,17 +1535,6 @@ int main(int argc, char *argv[]) /* this is destructive to kelf_patched */ kpatch_generate_output(kelf_patched, &kelf_out); kpatch_create_rela_sections(kelf_out); - - list_for_each_entry(sym, &kelf_out->symbols, list) { - if (sym->type == STT_FILE) { - hint = sym->name; - break; - } - } - kpatch_create_patches_sections(kelf_out, vmlinux, hint); - kpatch_create_dynamic_rela_sections(kelf_out, vmlinux, hint); - kpatch_strip_unneeded_syms(vmlinux, kelf_out); - kpatch_create_shstrtab(kelf_out); kpatch_create_strtab(kelf_out); kpatch_create_symtab(kelf_out); @@ -1841,12 +1542,6 @@ int main(int argc, char *argv[]) if (arguments.inventory) kpatch_write_inventory_file(kelf_out, outfile); - - /* rebuild the rela section data buffers from their relas lists */ - list_for_each_entry(sec, &kelf_out->sections, list) - if (is_rela_section(sec)) - kpatch_rebuild_rela_section_data(sec); - kpatch_write_output_elf(kelf_out, kelf_patched->elf, outfile); return 0; diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index 70d38c245..bedd4473f 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -275,7 +275,7 @@ cd "$TEMPDIR" mkdir output for i in $FILES; do mkdir -p "output/$(dirname $i)" - "$TOOLSDIR"/create-diff-object "orig/$i" "patched/$i" "$TEMPDIR/vmlinux" "output/$i" 2>&1 |tee -a "$LOGFILE" + "$TOOLSDIR"/create-diff-object "orig/$i" "patched/$i" "output/$i" 2>&1 |tee -a "$LOGFILE" [[ "${PIPESTATUS[0]}" -eq 0 ]] || die done @@ -286,8 +286,10 @@ make prepare >> "$LOGFILE" 2>&1 || die cd "$TEMPDIR/output" ld -r -o ../patch/output.o $FILES >> "$LOGFILE" 2>&1 || die cd "$TEMPDIR/patch" +"$TOOLSDIR"/add-patches-section output.o ../vmlinux >> "$LOGFILE" 2>&1 || die KPATCH_BUILD="$SRCDIR" KPATCH_NAME="$PATCHNAME" KBUILD_EXTRA_SYMBOLS="$SYMVERSFILE" make "O=$OBJDIR" >> "$LOGFILE" 2>&1 || die $STRIPCMD "kpatch-$PATCHNAME.ko" >> "$LOGFILE" 2>&1 || die +"$TOOLSDIR"/link-vmlinux-syms "kpatch-$PATCHNAME.ko" ../vmlinux >> "$LOGFILE" 2>&1 || die cp -f "$TEMPDIR/patch/kpatch-$PATCHNAME.ko" "$BASE" || die diff --git a/kpatch-build/link-vmlinux-syms.c b/kpatch-build/link-vmlinux-syms.c new file mode 100644 index 000000000..af0855bc2 --- /dev/null +++ b/kpatch-build/link-vmlinux-syms.c @@ -0,0 +1,291 @@ +/* + * link-vmlinux-syms.c + * + * Copyright (C) 2014 Seth Jennings + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, + * 02110-1301, USA. + */ + +/* + * This tool takes the nearly complete hotfix kernel module and + * the base vmlinux. It hardcodes the addresses of any global symbols + * that are referenced by the output object but are not exported by + * vmlinux into the symbol table of the kernel module. + * + * Global symbols that are exported by the base vmlinux can be + * resolved by the kernel module linker at load time and are + * left unmodified. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ERROR(format, ...) \ + error(1, 0, "%s: %d: " format, __FUNCTION__, __LINE__, ##__VA_ARGS__) + +struct section { + Elf_Scn *scn; + GElf_Shdr sh; +}; + +enum symaction { + NOOP, /* do nothing, default */ + PATCH, /* sym is a patched function */ + LINK, /* sym is a non-exported global sym */ +}; + +struct sym { + struct sym *next; + GElf_Sym sym; + char *name; + int index; + enum symaction action; + unsigned long vm_addr; + size_t vm_len; +}; + +struct symlist { + struct sym *head; + size_t len; +}; + +struct elf { + Elf *elf; + int fd; + size_t shstrndx; + struct section symtab, shstrtab; +}; + +#define for_each_sym(list, iter) \ + for((iter) = (list)->head; (iter); (iter) = (iter)->next) + +enum elfmode { + RDONLY, + RDWR +}; + +static void open_elf(char *path, enum elfmode elfmode, struct elf *elf) +{ + mode_t mode; + Elf_Cmd cmd; + + switch(elfmode) { + case RDONLY: + mode = O_RDONLY; + cmd = ELF_C_READ_MMAP; + break; + case RDWR: + mode = O_RDWR; + cmd = ELF_C_RDWR; + break; + } + + if ((elf->fd = open(path, mode, 0)) < 0) + ERROR("open"); + + elf->elf = elf_begin(elf->fd, cmd, NULL); + if (!elf->elf) { + printf("%s\n", elf_errmsg(-1)); + ERROR("elf_begin"); + } + + if (elf_getshdrstrndx(elf->elf, &elf->shstrndx)) + ERROR("elf_getshdrstrndx"); +} + +static void insert_sym(struct symlist *list, GElf_Sym *sym, char *name, + int index) +{ + struct sym *newsym; + + newsym = malloc(sizeof(*newsym)); + if (!newsym) + ERROR("malloc"); + memset(newsym, 0, sizeof(*newsym)); + newsym->sym = *sym; + newsym->name = name; + newsym->index = index; + + newsym->next = list->head; + list->head = newsym; +} + +static void find_section_by_name(struct elf *elf, char *name, struct section *sec) +{ + Elf_Scn *scn = NULL; + GElf_Shdr sh; + char *secname; + + while ((scn = elf_nextscn(elf->elf, scn))) { + if (!gelf_getshdr(scn, &sh)) + ERROR("gelf_getshdr"); + + secname = elf_strptr(elf->elf, elf->shstrndx, sh.sh_name); + if (!secname) + ERROR("elf_strptr scn"); + + if (!strcmp(secname, name)) + break; + } + + if (!scn) + ERROR("no section %s found", name); + + sec->scn = scn; + sec->sh = sh; +} + +static void create_symlist(struct elf *elf, struct symlist *symlist) +{ + Elf_Scn *scn = elf->symtab.scn; + GElf_Shdr *sh = &elf->symtab.sh; + GElf_Sym sym; + Elf_Data *data; + char *name; + int i; + + /* get symtab data buffer */ + data = elf_getdata(scn, NULL); + if (!data) + ERROR("elf_getdata"); + + symlist->len = sh->sh_size / sh->sh_entsize; + for (i = 0; i < symlist->len; i++) { + if (!gelf_getsym(data, i, &sym)) + ERROR("gelf_getsym"); + + name = elf_strptr(elf->elf, sh->sh_link, sym.st_name); + if(!name) + ERROR("elf_strptr sym"); + + insert_sym(symlist, &sym, name, i); + } +} + +static struct sym *find_symbol_by_name(struct symlist *list, char *name) +{ + struct sym *cur, *ret = NULL; + + for_each_sym(list, cur) { + if (!strcmp(cur->name, name)) { + if (ret) + ERROR("unresolvable symbol ambiguity for symbol '%s'", name); + ret = cur; + } + } + + return ret; +} + +/* + * TODO: de-dup common code above these point with code + * in add-patches-section.c + */ + +int main(int argc, char **argv) +{ + struct symlist symlist, symlistv; + struct sym *cur, *vsym; + struct elf elf, elfv; + char name[255]; + struct section symtab; + Elf_Scn *scn; + Elf_Data *data; + + /* set elf version (required by libelf) */ + if (elf_version(EV_CURRENT) == EV_NONE) + ERROR("elf_version"); + + memset(&elf, 0, sizeof(elf)); + memset(&elfv, 0, sizeof(elfv)); + open_elf(argv[1], RDWR, &elf); + open_elf(argv[2], RDONLY, &elfv); + + find_section_by_name(&elf, ".symtab", &(elf.symtab)); + find_section_by_name(&elfv, ".symtab", &(elfv.symtab)); + + find_section_by_name(&elf, ".shstrtab", &(elf.shstrtab)); + + memset(&symlist, 0, sizeof(symlist)); + memset(&symlistv, 0, sizeof(symlistv)); + create_symlist(&elf, &symlist); + create_symlist(&elfv, &symlistv); + + /* lookup non-exported globals and insert vmlinux address */ + for_each_sym(&symlist, cur) { + if (GELF_ST_TYPE(cur->sym.st_info) != STT_NOTYPE || + GELF_ST_BIND(cur->sym.st_info) != STB_GLOBAL || + cur->sym.st_shndx != STN_UNDEF || + !strcmp(cur->name, "kpatch_register") || + !strcmp(cur->name, "kpatch_unregister") || + !strcmp(cur->name, "kpatch_patches_kobj")) + continue; + + printf("found global symbol %s\n", cur->name); + sprintf(name, "__kstrtab_%s", cur->name); + vsym = find_symbol_by_name(&symlistv, name); + if (vsym) { + printf("symbol is exported by the kernel\n"); + continue; + } + + vsym = find_symbol_by_name(&symlistv, cur->name); + if (!vsym) + ERROR("couldn't find global function %s in vmlinux", + cur->name); + + cur->vm_addr = vsym->sym.st_value; + cur->vm_len = vsym->sym.st_size; + cur->action = LINK; + printf("original symbol at address %016lx (length %zu)\n", + cur->vm_addr, cur->vm_len); + } + + elf_end(elfv.elf); + close(elfv.fd); + + find_section_by_name(&elf, ".symtab", &symtab); + scn = symtab.scn; + + data = elf_getdata(scn, NULL); + if (!data) + ERROR("elf_getdata"); + + /* update LINK symbols */ + for_each_sym(&symlist, cur) { + if (cur->action != LINK) + continue; + cur->sym.st_value = cur->vm_addr; + cur->sym.st_info = GELF_ST_INFO(STB_LOCAL,STT_FUNC); + cur->sym.st_shndx = SHN_ABS; + gelf_update_sym(data, cur->index, &cur->sym); + } + + if (elf_update(elf.elf, ELF_C_WRITE) < 0) + ERROR("elf_update %s", elf_errmsg(-1)); + + elf_end(elf.elf); + close(elf.fd); + + return 0; +} diff --git a/kpatch-build/lookup.c b/kpatch-build/lookup.c deleted file mode 100644 index 935933a84..000000000 --- a/kpatch-build/lookup.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * lookup.c - * - * This file contains functions that assist in the reading and searching - * the symbol table of an ELF object. - * - * Copyright (C) 2014 Seth Jennings - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, - * 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lookup.h" - -#define ERROR(format, ...) \ - error(1, 0, "%s: %d: " format, __FUNCTION__, __LINE__, ##__VA_ARGS__) - -struct symbol { - unsigned long value; - unsigned long size; - char *name; - int type, bind; -}; - -struct lookup_table { - int fd, nr; - Elf *elf; - struct symbol *syms; -}; - -#define for_each_symbol(ndx, iter, table) \ - for (ndx = 0, iter = table->syms; ndx < table->nr; ndx++, iter++) - -struct lookup_table *lookup_open(char *path) -{ - Elf *elf; - int fd, i, len; - Elf_Scn *scn; - GElf_Shdr sh; - GElf_Sym sym; - Elf_Data *data; - char *name; - struct lookup_table *table; - struct symbol *mysym; - size_t shstrndx; - - if ((fd = open(path, O_RDONLY, 0)) < 0) - ERROR("open"); - - elf_version(EV_CURRENT); - - elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); - if (!elf) { - printf("%s\n", elf_errmsg(-1)); - ERROR("elf_begin"); - } - - if (elf_getshdrstrndx(elf, &shstrndx)) - ERROR("elf_getshdrstrndx"); - - scn = NULL; - while ((scn = elf_nextscn(elf, scn))) { - if (!gelf_getshdr(scn, &sh)) - ERROR("gelf_getshdr"); - - name = elf_strptr(elf, shstrndx, sh.sh_name); - if (!name) - ERROR("elf_strptr scn"); - - if (!strcmp(name, ".symtab")) - break; - } - - if (!scn) - ERROR(".symtab section not found"); - - data = elf_getdata(scn, NULL); - if (!data) - ERROR("elf_getdata"); - - len = sh.sh_size / sh.sh_entsize; - - table = malloc(sizeof(*table)); - if (!table) - ERROR("malloc table"); - table->syms = malloc(len * sizeof(struct symbol)); - if (!table->syms) - ERROR("malloc table.syms"); - table->nr = len; - table->fd = fd; - table->elf = elf; - - for_each_symbol(i, mysym, table) { - if (!gelf_getsym(data, i, &sym)) - ERROR("gelf_getsym"); - - name = elf_strptr(elf, sh.sh_link, sym.st_name); - if(!name) - ERROR("elf_strptr sym"); - - mysym->value = sym.st_value; - mysym->size = sym.st_size; - mysym->type = GELF_ST_TYPE(sym.st_info); - mysym->bind = GELF_ST_BIND(sym.st_info); - mysym->name = name; - } - - return table; -} - -void lookup_close(struct lookup_table *table) -{ - elf_end(table->elf); - close(table->fd); - free(table); -} - -int lookup_local_symbol(struct lookup_table *table, char *name, char *hint, - struct lookup_result *result) -{ - struct symbol *sym, *match = NULL; - int i; - char *curfile = NULL; - - memset(result, 0, sizeof(*result)); - for_each_symbol(i, sym, table) { - if (sym->type == STT_FILE) { - if (!strcmp(sym->name, hint)) { - curfile = sym->name; - continue; /* begin hint file symbols */ - } else if (curfile) - curfile = NULL; /* end hint file symbols */ - } - if (!curfile) - continue; - if (sym->bind == STB_LOCAL && !strcmp(sym->name, name)) { - if (match) - /* dup file+symbol, unresolvable ambiguity */ - return 1; - match = sym; - } - } - - if (!match) - return 1; - - result->value = match->value; - result->size = match->size; - return 0; -} - -int lookup_global_symbol(struct lookup_table *table, char *name, - struct lookup_result *result) -{ - struct symbol *sym; - int i; - - memset(result, 0, sizeof(*result)); - for_each_symbol(i, sym, table) - if (sym->bind == STB_GLOBAL && - !strcmp(sym->name, name)) { - result->value = sym->value; - result->size = sym->size; - return 0; - } - - return 1; -} - -int lookup_is_exported_symbol(struct lookup_table *table, char *name) -{ - struct symbol *sym; - int i; - char export[255] = "__ksymtab_"; - - strncat(export, name, 254); - - for_each_symbol(i, sym, table) - if (!strcmp(sym->name, export)) - return 1; - - return 0; -} - -#if 0 /* for local testing */ -static void find_this(struct lookup_table *table, char *sym, char *hint) -{ - struct lookup_result result; - - if (hint) - lookup_local_symbol(table, sym, hint, &result); - else - lookup_global_symbol(table, sym, &result); - - printf("%s %s w/ %s hint at 0x%016lx len %lu\n", - hint ? "local" : "global", sym, hint ? hint : "no", - result.value, result.size); -} - -int main(int argc, char **argv) -{ - struct lookup_table *vmlinux; - - if (argc != 2) - return 1; - - vmlinux = lookup_open(argv[1]); - - printf("printk is%s exported\n", - lookup_is_exported_symbol(vmlinux, "__fentry__") ? "" : " not"); - printf("meminfo_proc_show is%s exported\n", - lookup_is_exported_symbol(vmlinux, "meminfo_proc_show") ? "" : " not"); - - find_this(vmlinux, "printk", NULL); - find_this(vmlinux, "pages_to_scan_show", "ksm.c"); - find_this(vmlinux, "pages_to_scan_show", "huge_memory.c"); - find_this(vmlinux, "pages_to_scan_show", NULL); /* should fail */ - - lookup_close(vmlinux); - - return 0; -} -#endif diff --git a/kpatch-build/lookup.h b/kpatch-build/lookup.h deleted file mode 100644 index cbb3daee1..000000000 --- a/kpatch-build/lookup.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _LOOKUP_H_ -#define _LOOKUP_H_ - -struct lookup_table; - -struct lookup_result { - unsigned long value; - unsigned long size; -}; - -struct lookup_table *lookup_open(char *path); -void lookup_close(struct lookup_table *table); -int lookup_local_symbol(struct lookup_table *table, char *name, char *hint, - struct lookup_result *result); -int lookup_global_symbol(struct lookup_table *table, char *name, - struct lookup_result *result); -int lookup_is_exported_symbol(struct lookup_table *table, char *name); - -#endif /* _LOOKUP_H_ */