mirror of
https://github.com/dynup/kpatch
synced 2025-03-30 23:26:23 +00:00
dynrelas support, obsoleting link-vmlinux-syms
This adds dynamic linking support for the patch modules. It is the first step toward supporting patching module code and relocatable kernels. Rela entries that reference non-included local and non-exported global symbols are converted to "dynrelas". These dynrelas are relocations that are done by the core module, not the kernel module linker. This allows the core module to apply offsets to the base addresses found in the base vmlinux or module. Signed-off-by: Seth Jennings <sjenning@redhat.com> Conflicts: kpatch-build/kpatch-build
This commit is contained in:
parent
fd8176faf8
commit
21fc274448
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,7 +10,6 @@ Module.symvers
|
||||
kpatch-build/lookup.o
|
||||
kpatch-build/lookup
|
||||
kpatch-build/create-diff-object
|
||||
kpatch-build/link-vmlinux-syms
|
||||
man/kpatch.1.gz
|
||||
man/kpatch-build.1.gz
|
||||
test/integration/test.log
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/stacktrace.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include "kpatch.h"
|
||||
@ -424,6 +425,10 @@ 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;
|
||||
@ -448,6 +453,34 @@ 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_up;
|
||||
}
|
||||
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_up;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_funcs; i++) {
|
||||
func = &funcs[i];
|
||||
|
||||
|
@ -30,6 +30,12 @@ struct kpatch_patch {
|
||||
unsigned long old_size;
|
||||
};
|
||||
|
||||
struct kpatch_dynrela {
|
||||
unsigned long dest;
|
||||
unsigned long src;
|
||||
unsigned long type;
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/types.h>
|
||||
@ -40,7 +46,9 @@ 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 */
|
||||
};
|
||||
|
@ -29,6 +29,7 @@ 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;
|
||||
@ -77,6 +78,9 @@ static int __init patch_init(void)
|
||||
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);
|
||||
|
||||
patch_kobj = kobject_create_and_add(THIS_MODULE->name,
|
||||
kpatch_patches_kobj);
|
||||
|
@ -1,2 +1,4 @@
|
||||
__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);
|
||||
|
@ -3,7 +3,7 @@ include ../Makefile.inc
|
||||
CFLAGS += -I../kmod/patch -Wall -g
|
||||
LDFLAGS = -lelf
|
||||
|
||||
TARGETS = create-diff-object link-vmlinux-syms
|
||||
TARGETS = create-diff-object
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
|
@ -908,23 +908,6 @@ void kpatch_generate_output(struct kpatch_elf *kelf, struct kpatch_elf **kelfout
|
||||
}
|
||||
out->sections_nr = 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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy functions to the output kelf and reindex. Once the symbol is
|
||||
* copied, its include field is set to zero so it isn't copied again
|
||||
@ -1157,6 +1140,7 @@ void kpatch_regenerate_parainstructions_sections(struct kpatch_elf *kelf)
|
||||
/* mark sections for inclusion */
|
||||
sec->include = 1;
|
||||
sec->base->include = 1;
|
||||
sec->base->secsym->include = 1;
|
||||
|
||||
/* update rela section data size */
|
||||
sec->data->d_size = sec->sh.sh_entsize * nr;
|
||||
@ -1473,6 +1457,160 @@ void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
||||
|
||||
}
|
||||
|
||||
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->sec)
|
||||
nr++;
|
||||
}
|
||||
}
|
||||
|
||||
/* create .kpatch.dynrelas*/
|
||||
|
||||
/* allocate section resources */
|
||||
ALLOC_LINK(sec, &kelf->sections);
|
||||
size = nr * sizeof(*dynrelas);
|
||||
dynrelas = malloc(nr * sizeof(*dynrelas));
|
||||
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 text section (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->sec) {
|
||||
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);
|
||||
if (!sec->base->sym)
|
||||
ERROR("expected bundled symbol");
|
||||
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");
|
||||
}
|
||||
|
||||
void kpatch_strip_non_included_syms(struct lookup_table *table,
|
||||
struct kpatch_elf *kelf)
|
||||
{
|
||||
struct symbol *sym, *safe;
|
||||
int index, first;
|
||||
|
||||
first = 1;
|
||||
list_for_each_entry_safe(sym, safe, &kelf->symbols, list) {
|
||||
if (first) {
|
||||
first = 0;
|
||||
continue; /* skip NULL symbol */
|
||||
}
|
||||
if (sym->type == STT_FILE)
|
||||
continue;
|
||||
if (lookup_is_exported_symbol(table, sym->name))
|
||||
continue;
|
||||
if (sym->sec)
|
||||
continue;
|
||||
|
||||
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;
|
||||
@ -1700,6 +1838,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
kpatch_create_patches_sections(kelf_out, vmlinux, hint);
|
||||
kpatch_create_dynamic_rela_sections(kelf_out, vmlinux, hint);
|
||||
kpatch_strip_non_included_syms(vmlinux, kelf_out);
|
||||
|
||||
kpatch_create_shstrtab(kelf_out);
|
||||
kpatch_create_strtab(kelf_out);
|
||||
|
@ -297,7 +297,6 @@ ld -r -o ../patch/output.o $FILES >> "$LOGFILE" 2>&1 || die
|
||||
cd "$TEMPDIR/patch"
|
||||
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
|
||||
|
||||
|
@ -1,291 +0,0 @@
|
||||
/*
|
||||
* link-vmlinux-syms.c
|
||||
*
|
||||
* Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
|
||||
*
|
||||
* 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 <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <error.h>
|
||||
#include <gelf.h>
|
||||
#include <unistd.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;
|
||||
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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user