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_ */