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 "kpatch-patch.h"
#include "kpatch-elf.h"
#include "kpatch-intermediate.h"
#define DIFF_FATAL(format, ...) \
({ \
@ -1887,17 +1888,20 @@ static int kpatch_is_core_module_symbol(char *name)
!strcmp(name, "kpatch_shadow_get"));
}
static void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
struct lookup_table *table, char *hint,
char *objname)
static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
struct lookup_table *table,
char *hint, char *objname,
char *pmod_name)
{
int nr, index, objname_offset;
struct section *sec, *dynsec, *relasec;
struct rela *rela, *dynrela, *safe;
struct symbol *strsym;
int nr, index;
struct section *sec, *ksym_sec, *krela_sec;
struct rela *rela, *rela2, *safe;
struct symbol *strsym, *ksym_sec_sym;
struct kpatch_symbol *ksyms;
struct kpatch_relocation *krelas;
struct lookup_result result;
struct kpatch_patch_dynrela *dynrelas;
int vmlinux, external, ret;
char *sym_objname;
int ret, vmlinux, external;
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"))
continue;
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 */
dynsec = create_section_pair(kelf, ".kpatch.dynrelas", sizeof(*dynrelas), nr);
relasec = dynsec->rela;
dynrelas = dynsec->data->d_buf;
/* create .kpatch.relocations text/rela section pair */
krela_sec = create_section_pair(kelf, ".kpatch.relocations", sizeof(*krelas), nr);
krelas = krela_sec->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 */
strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings");
if (!strsym)
ERROR("can't find .kpatch.strings symbol");
/* add objname to strings */
objname_offset = offset_of_string(&kelf->strings, objname);
/* populate sections */
index = 0;
list_for_each_entry(sec, &kelf->sections, list) {
@ -1946,6 +1958,17 @@ static void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
continue;
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) {
/* An unchanged local symbol */
@ -1997,56 +2020,89 @@ static void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
* patched.
*/
if (lookup_global_symbol(table, rela->sym->name,
&result))
&result)) {
/*
* Not there, assume it's either an
* exported symbol or provided by
* another .o in the patch module.
* Not there, see if the symbol is
* exported, and set sym_objname to the
* 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;
}
}
log_debug("lookup for %s @ 0x%016lx len %lu\n",
rela->sym->name, result.value, result.size);
/* dest filed in by rela entry below */
/* Fill in ksyms[index] */
if (vmlinux)
dynrelas[index].src = result.value;
ksyms[index].src = result.value;
else
/* for modules, src is discovered at runtime */
dynrelas[index].src = 0;
dynrelas[index].addend = rela->addend;
dynrelas[index].type = rela->type;
dynrelas[index].external = external;
dynrelas[index].sympos = result.pos;
ksyms[index].src = 0;
ksyms[index].pos = result.pos;
ksyms[index].type = rela->sym->type;
ksyms[index].bind = rela->sym->bind;
/* add rela to fill in dest field */
ALLOC_LINK(dynrela, &relasec->relas);
/* add rela to fill in ksyms[index].name field */
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)
dynrela->sym = sec->base->sym;
rela2->sym = sec->base->sym;
else if (sec->base->secsym)
dynrela->sym = sec->base->secsym;
rela2->sym = sec->base->secsym;
else
ERROR("can't create dynrela for section %s (symbol %s): no bundled section or section symbol",
sec->name, rela->sym->name);
dynrela->type = R_X86_64_64;
dynrela->addend = rela->offset;
dynrela->offset = index * sizeof(*dynrelas);
rela2->type = R_X86_64_64;
rela2->addend = rela->offset;
rela2->offset = index * sizeof(*krelas) + \
offsetof(struct kpatch_relocation, dest);
/* add rela to fill in name field */
ALLOC_LINK(dynrela, &relasec->relas);
dynrela->sym = strsym;
dynrela->type = R_X86_64_64;
dynrela->addend = offset_of_string(&kelf->strings, rela->sym->name);
dynrela->offset = index * sizeof(*dynrelas) + offsetof(struct kpatch_patch_dynrela, name);
/* add rela to fill in krelas[index].objname field */
ALLOC_LINK(rela2, &krela_sec->rela->relas);
rela2->sym = strsym;
rela2->type = R_X86_64_64;
rela2->addend = offset_of_string(&kelf->strings, objname);
rela2->offset = index * sizeof(*krelas) + \
offsetof(struct kpatch_relocation, objname);
/* add rela to fill in objname field */
ALLOC_LINK(dynrela, &relasec->relas);
dynrela->sym = strsym;
dynrela->type = R_X86_64_64;
dynrela->addend = objname_offset;
dynrela->offset = index * sizeof(*dynrelas) +
offsetof(struct kpatch_patch_dynrela, objname);
/* add rela to fill in krelas[index].ksym field */
ALLOC_LINK(rela2, &krela_sec->rela->relas);
rela2->sym = ksym_sec_sym;
rela2->type = R_X86_64_64;
rela2->addend = index * sizeof(*ksyms);
rela2->offset = index * sizeof(*krelas) + \
offsetof(struct kpatch_relocation, ksym);
rela->sym->strip = 1;
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 */
dynsec->data->d_size = index * sizeof(struct kpatch_patch_dynrela);
dynsec->sh.sh_size = dynsec->data->d_size;
/* set size to actual number of ksyms/krelas */
ksym_sec->data->d_size = index * sizeof(struct kpatch_symbol);
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)
@ -2246,11 +2305,11 @@ static void kpatch_build_strings_section_data(struct kpatch_elf *kelf)
}
struct arguments {
char *args[5];
char *args[6];
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[] = {
{"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;
break;
case ARGP_KEY_ARG:
if (state->arg_num >= 5)
if (state->arg_num >= 6)
/* Too many arguments. */
argp_usage (state);
arguments->args[state->arg_num] = arg;
break;
case ARGP_KEY_END:
if (state->arg_num < 5)
if (state->arg_num < 6)
/* Not enough arguments. */
argp_usage (state);
break;
@ -2296,7 +2355,7 @@ int main(int argc, char *argv[])
struct section *sec, *symtab;
struct symbol *sym;
char *hint = NULL, *objname, *pos;
char *mod_symvers_path;
char *mod_symvers_path, *pmod_name;
arguments.debug = 0;
argp_parse (&argp, argc, argv, 0, NULL, &arguments);
@ -2308,6 +2367,7 @@ int main(int argc, char *argv[])
childobj = basename(arguments.args[0]);
mod_symvers_path = arguments.args[4];
pmod_name = arguments.args[5];
kelf_base = kpatch_elf_open(arguments.args[0]);
kelf_patched = kpatch_elf_open(arguments.args[1]);
@ -2401,7 +2461,7 @@ int main(int argc, char *argv[])
/* create strings, patches, and dynrelas sections */
kpatch_create_strings_elements(kelf_out);
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_build_strings_section_data(kelf_out);

View File

@ -557,7 +557,9 @@ for i in $FILES; do
fi
cd $TEMPDIR
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]}"
if [[ $rc = 139 ]]; then
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_ */