create-diff-object: create .kpatch.relocations and .kpatch.symbols sections

Instead of creating dynrela sections, have create-diff-object create
intermediate sections .kpatch.relocations and .kpatch.symbols which can
then be used to build (depending on kernel version) either dynrela sections
or klp rela/klp arch sections + klp symbols in a later phase of kpatch-build.
This commit is contained in:
Jessica Yu 2017-01-23 12:43:17 -08:00
parent 58de46cb9e
commit 87643703a7
3 changed files with 163 additions and 58 deletions

View File

@ -51,6 +51,7 @@
#include "asm/insn.h" #include "asm/insn.h"
#include "kpatch-patch.h" #include "kpatch-patch.h"
#include "kpatch-elf.h" #include "kpatch-elf.h"
#include "kpatch-intermediate.h"
#define DIFF_FATAL(format, ...) \ #define DIFF_FATAL(format, ...) \
({ \ ({ \
@ -1887,17 +1888,20 @@ static int kpatch_is_core_module_symbol(char *name)
!strcmp(name, "kpatch_shadow_get")); !strcmp(name, "kpatch_shadow_get"));
} }
static void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf, static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
struct lookup_table *table, char *hint, struct lookup_table *table,
char *objname) char *hint, char *objname,
char *pmod_name)
{ {
int nr, index, objname_offset; int nr, index;
struct section *sec, *dynsec, *relasec; struct section *sec, *ksym_sec, *krela_sec;
struct rela *rela, *dynrela, *safe; struct rela *rela, *rela2, *safe;
struct symbol *strsym; struct symbol *strsym, *ksym_sec_sym;
struct kpatch_symbol *ksyms;
struct kpatch_relocation *krelas;
struct lookup_result result; struct lookup_result result;
struct kpatch_patch_dynrela *dynrelas; char *sym_objname;
int vmlinux, external, ret; int ret, vmlinux, external;
vmlinux = !strcmp(objname, "vmlinux"); vmlinux = !strcmp(objname, "vmlinux");
@ -1909,22 +1913,30 @@ static void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
if (!strcmp(sec->name, ".rela.kpatch.funcs")) if (!strcmp(sec->name, ".rela.kpatch.funcs"))
continue; continue;
list_for_each_entry(rela, &sec->relas, list) list_for_each_entry(rela, &sec->relas, list)
nr++; /* upper bound on number of dynrelas */ nr++; /* upper bound on number of kpatch relas and symbols */
} }
/* create text/rela section pair */ /* create .kpatch.relocations text/rela section pair */
dynsec = create_section_pair(kelf, ".kpatch.dynrelas", sizeof(*dynrelas), nr); krela_sec = create_section_pair(kelf, ".kpatch.relocations", sizeof(*krelas), nr);
relasec = dynsec->rela; krelas = krela_sec->data->d_buf;
dynrelas = dynsec->data->d_buf;
/* create .kpatch.symbols text/rela section pair */
ksym_sec = create_section_pair(kelf, ".kpatch.symbols", sizeof(*ksyms), nr);
ksyms = ksym_sec->data->d_buf;
/* create .kpatch.symbols section symbol (to set rela->sym later) */
ALLOC_LINK(ksym_sec_sym, &kelf->symbols);
ksym_sec_sym->sec = ksym_sec;
ksym_sec_sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION);
ksym_sec_sym->type = STT_SECTION;
ksym_sec_sym->bind = STB_LOCAL;
ksym_sec_sym->name = ".kpatch.symbols";
/* lookup strings symbol */ /* lookup strings symbol */
strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings"); strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings");
if (!strsym) if (!strsym)
ERROR("can't find .kpatch.strings symbol"); ERROR("can't find .kpatch.strings symbol");
/* add objname to strings */
objname_offset = offset_of_string(&kelf->strings, objname);
/* populate sections */ /* populate sections */
index = 0; index = 0;
list_for_each_entry(sec, &kelf->sections, list) { list_for_each_entry(sec, &kelf->sections, list) {
@ -1946,6 +1958,17 @@ static void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
continue; continue;
external = 0; external = 0;
/*
* sym_objname is the name of the object to which
* rela->sym belongs. We'll need this to build
* ".klp.sym." symbol names later on.
*
* By default sym_objname is the name of the
* component being patched (vmlinux or module).
* If it's an external symbol, sym_objname
* will get reassigned appropriately.
*/
sym_objname = objname;
if (rela->sym->bind == STB_LOCAL) { if (rela->sym->bind == STB_LOCAL) {
/* An unchanged local symbol */ /* An unchanged local symbol */
@ -1997,56 +2020,89 @@ static void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
* patched. * patched.
*/ */
if (lookup_global_symbol(table, rela->sym->name, if (lookup_global_symbol(table, rela->sym->name,
&result)) &result)) {
/* /*
* Not there, assume it's either an * Not there, see if the symbol is
* exported symbol or provided by * exported, and set sym_objname to the
* another .o in the patch module. * object the exported symbol belongs
* to. If it's not exported, assume sym
* is provided by another .o in the
* patch module.
*/ */
sym_objname = lookup_exported_symbol_objname(table, rela->sym->name);
/* Not exported, must be in another .o in patch module */
if (!sym_objname)
sym_objname = pmod_name;
external = 1; external = 1;
}
} }
log_debug("lookup for %s @ 0x%016lx len %lu\n", log_debug("lookup for %s @ 0x%016lx len %lu\n",
rela->sym->name, result.value, result.size); rela->sym->name, result.value, result.size);
/* dest filed in by rela entry below */ /* Fill in ksyms[index] */
if (vmlinux) if (vmlinux)
dynrelas[index].src = result.value; ksyms[index].src = result.value;
else else
/* for modules, src is discovered at runtime */ /* for modules, src is discovered at runtime */
dynrelas[index].src = 0; ksyms[index].src = 0;
dynrelas[index].addend = rela->addend; ksyms[index].pos = result.pos;
dynrelas[index].type = rela->type; ksyms[index].type = rela->sym->type;
dynrelas[index].external = external; ksyms[index].bind = rela->sym->bind;
dynrelas[index].sympos = result.pos;
/* add rela to fill in dest field */ /* add rela to fill in ksyms[index].name field */
ALLOC_LINK(dynrela, &relasec->relas); ALLOC_LINK(rela2, &ksym_sec->rela->relas);
rela2->sym = strsym;
rela2->type = R_X86_64_64;
rela2->addend = offset_of_string(&kelf->strings, rela->sym->name);
rela2->offset = index * sizeof(*ksyms) + \
offsetof(struct kpatch_symbol, name);
/* add rela to fill in ksyms[index].objname field */
ALLOC_LINK(rela2, &ksym_sec->rela->relas);
rela2->sym = strsym;
rela2->type = R_X86_64_64;
rela2->addend = offset_of_string(&kelf->strings, sym_objname);
rela2->offset = index * sizeof(*ksyms) + \
offsetof(struct kpatch_symbol, objname);
/* Fill in krelas[index] */
krelas[index].addend = rela->addend;
krelas[index].type = rela->type;
krelas[index].external = external;
krelas[index].offset = rela->offset;
/* add rela to fill in krelas[index].dest field */
ALLOC_LINK(rela2, &krela_sec->rela->relas);
if (sec->base->sym) if (sec->base->sym)
dynrela->sym = sec->base->sym; rela2->sym = sec->base->sym;
else if (sec->base->secsym) else if (sec->base->secsym)
dynrela->sym = sec->base->secsym; rela2->sym = sec->base->secsym;
else else
ERROR("can't create dynrela for section %s (symbol %s): no bundled section or section symbol", ERROR("can't create dynrela for section %s (symbol %s): no bundled section or section symbol",
sec->name, rela->sym->name); sec->name, rela->sym->name);
dynrela->type = R_X86_64_64; rela2->type = R_X86_64_64;
dynrela->addend = rela->offset; rela2->addend = rela->offset;
dynrela->offset = index * sizeof(*dynrelas); rela2->offset = index * sizeof(*krelas) + \
offsetof(struct kpatch_relocation, dest);
/* add rela to fill in name field */ /* add rela to fill in krelas[index].objname field */
ALLOC_LINK(dynrela, &relasec->relas); ALLOC_LINK(rela2, &krela_sec->rela->relas);
dynrela->sym = strsym; rela2->sym = strsym;
dynrela->type = R_X86_64_64; rela2->type = R_X86_64_64;
dynrela->addend = offset_of_string(&kelf->strings, rela->sym->name); rela2->addend = offset_of_string(&kelf->strings, objname);
dynrela->offset = index * sizeof(*dynrelas) + offsetof(struct kpatch_patch_dynrela, name); rela2->offset = index * sizeof(*krelas) + \
offsetof(struct kpatch_relocation, objname);
/* add rela to fill in objname field */ /* add rela to fill in krelas[index].ksym field */
ALLOC_LINK(dynrela, &relasec->relas); ALLOC_LINK(rela2, &krela_sec->rela->relas);
dynrela->sym = strsym; rela2->sym = ksym_sec_sym;
dynrela->type = R_X86_64_64; rela2->type = R_X86_64_64;
dynrela->addend = objname_offset; rela2->addend = index * sizeof(*ksyms);
dynrela->offset = index * sizeof(*dynrelas) + rela2->offset = index * sizeof(*krelas) + \
offsetof(struct kpatch_patch_dynrela, objname); offsetof(struct kpatch_relocation, ksym);
rela->sym->strip = 1; rela->sym->strip = 1;
list_del(&rela->list); list_del(&rela->list);
@ -2056,9 +2112,12 @@ static void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
} }
} }
/* set size to actual number of dynrelas */ /* set size to actual number of ksyms/krelas */
dynsec->data->d_size = index * sizeof(struct kpatch_patch_dynrela); ksym_sec->data->d_size = index * sizeof(struct kpatch_symbol);
dynsec->sh.sh_size = dynsec->data->d_size; ksym_sec->sh.sh_size = ksym_sec->data->d_size;
krela_sec->data->d_size = index * sizeof(struct kpatch_relocation);
krela_sec->sh.sh_size = krela_sec->data->d_size;
} }
static void kpatch_create_hooks_objname_rela(struct kpatch_elf *kelf, char *objname) static void kpatch_create_hooks_objname_rela(struct kpatch_elf *kelf, char *objname)
@ -2246,11 +2305,11 @@ static void kpatch_build_strings_section_data(struct kpatch_elf *kelf)
} }
struct arguments { struct arguments {
char *args[5]; char *args[6];
int debug; int debug;
}; };
static char args_doc[] = "original.o patched.o kernel-object output.o Module.symvers"; static char args_doc[] = "original.o patched.o kernel-object output.o Module.symvers patch-module-name";
static struct argp_option options[] = { static struct argp_option options[] = {
{"debug", 'd', NULL, 0, "Show debug output" }, {"debug", 'd', NULL, 0, "Show debug output" },
@ -2269,13 +2328,13 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
arguments->debug = 1; arguments->debug = 1;
break; break;
case ARGP_KEY_ARG: case ARGP_KEY_ARG:
if (state->arg_num >= 5) if (state->arg_num >= 6)
/* Too many arguments. */ /* Too many arguments. */
argp_usage (state); argp_usage (state);
arguments->args[state->arg_num] = arg; arguments->args[state->arg_num] = arg;
break; break;
case ARGP_KEY_END: case ARGP_KEY_END:
if (state->arg_num < 5) if (state->arg_num < 6)
/* Not enough arguments. */ /* Not enough arguments. */
argp_usage (state); argp_usage (state);
break; break;
@ -2296,7 +2355,7 @@ int main(int argc, char *argv[])
struct section *sec, *symtab; struct section *sec, *symtab;
struct symbol *sym; struct symbol *sym;
char *hint = NULL, *objname, *pos; char *hint = NULL, *objname, *pos;
char *mod_symvers_path; char *mod_symvers_path, *pmod_name;
arguments.debug = 0; arguments.debug = 0;
argp_parse (&argp, argc, argv, 0, NULL, &arguments); argp_parse (&argp, argc, argv, 0, NULL, &arguments);
@ -2308,6 +2367,7 @@ int main(int argc, char *argv[])
childobj = basename(arguments.args[0]); childobj = basename(arguments.args[0]);
mod_symvers_path = arguments.args[4]; mod_symvers_path = arguments.args[4];
pmod_name = arguments.args[5];
kelf_base = kpatch_elf_open(arguments.args[0]); kelf_base = kpatch_elf_open(arguments.args[0]);
kelf_patched = kpatch_elf_open(arguments.args[1]); kelf_patched = kpatch_elf_open(arguments.args[1]);
@ -2401,7 +2461,7 @@ int main(int argc, char *argv[])
/* create strings, patches, and dynrelas sections */ /* create strings, patches, and dynrelas sections */
kpatch_create_strings_elements(kelf_out); kpatch_create_strings_elements(kelf_out);
kpatch_create_patches_sections(kelf_out, lookup, hint, objname); kpatch_create_patches_sections(kelf_out, lookup, hint, objname);
kpatch_create_dynamic_rela_sections(kelf_out, lookup, hint, objname); kpatch_create_intermediate_sections(kelf_out, lookup, hint, objname, pmod_name);
kpatch_create_hooks_objname_rela(kelf_out, objname); kpatch_create_hooks_objname_rela(kelf_out, objname);
kpatch_build_strings_section_data(kelf_out); kpatch_build_strings_section_data(kelf_out);

View File

@ -557,7 +557,9 @@ for i in $FILES; do
fi fi
cd $TEMPDIR cd $TEMPDIR
if [[ -e "orig/$i" ]]; then if [[ -e "orig/$i" ]]; then
"$TOOLSDIR"/create-diff-object "orig/$i" "patched/$i" "$KOBJFILE" "output/$i" "$OBJDIR/Module.symvers" 2>&1 |tee -a "$LOGFILE" # create-diff-object orig.o patched.o kernel-object output.o Module.symvers patch-mod-name
"$TOOLSDIR"/create-diff-object "orig/$i" "patched/$i" "$KOBJFILE" \
"output/$i" "$OBJDIR/Module.symvers" "kpatch_${PATCHNAME//-/_}" 2>&1 |tee -a "$LOGFILE"
rc="${PIPESTATUS[0]}" rc="${PIPESTATUS[0]}"
if [[ $rc = 139 ]]; then if [[ $rc = 139 ]]; then
warn "create-diff-object SIGSEGV" warn "create-diff-object SIGSEGV"

View File

@ -0,0 +1,43 @@
/*
* kpatch-intermediate.h
*
* Structures for intermediate .kpatch.* sections
*
* 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.
*/
#ifndef _KPATCH_INTERMEDIATE_H_
#define _KPATCH_INTERMEDIATE_H_
struct kpatch_symbol {
unsigned long src;
unsigned long pos;
unsigned char bind, type;
char *name;
char *objname; /* object to which this sym belongs */
};
/* For .kpatch.{symbols,relocations,arch} sections */
struct kpatch_relocation {
unsigned long dest;
unsigned int type;
int addend;
int offset;
int external;
char *objname; /* object to which this rela applies to */
struct kpatch_symbol *ksym;
};
#endif /* _KPATCH_ELF_H_ */