829 lines
28 KiB
Diff
829 lines
28 KiB
Diff
From 9d590b5b9fb59c1cd52221feeef2794eb3333571 Mon Sep 17 00:00:00 2001
|
||
From: Zhipeng Xie <xiezhipeng1@huawei.com>
|
||
Date: Thu, 8 Nov 2018 15:12:06 +0000
|
||
Subject: [PATCH] support c plus kernel module
|
||
|
||
support GNU_UNIQUE type symbols.
|
||
support .group section corelation.
|
||
support symbol name longger than 128 bytes.
|
||
fix object size changed error for __FUNCTION__.xxx.
|
||
ignore compile warning for third party modules.
|
||
support functions have no fentry call.
|
||
support all function force enable/disable
|
||
fix sym replacing error for aarch64
|
||
|
||
Signed-off-by: Zhipeng Xie <xiezhipeng1@huawei.com>
|
||
---
|
||
kmod/patch/livepatch-patch-hook.c | 8 ++
|
||
kpatch-build/create-diff-object.c | 161 ++++++++++++++++++++++++-----------
|
||
kpatch-build/create-klp-module.c | 27 +++++-
|
||
kpatch-build/create-kpatch-module.c | 15 +--
|
||
kpatch-build/kpatch-build | 7 ++-
|
||
kpatch-build/kpatch-elf.c | 8 ++-
|
||
kpatch-build/kpatch-gcc | 2 +-
|
||
kpatch-build/lookup.c | 125 +++++++++++++++++++++++++--
|
||
kpatch-build/lookup.h | 14 ++-
|
||
9 files changed, 287 insertions(+), 80 deletions(-)
|
||
|
||
diff --git a/kmod/patch/livepatch-patch-hook.c b/kmod/patch/livepatch-patch-hook.c
|
||
index fefc068..ce1c955 100644
|
||
--- a/kmod/patch/livepatch-patch-hook.c
|
||
+++ b/kmod/patch/livepatch-patch-hook.c
|
||
@@ -497,8 +497,12 @@ static int __init patch_init(void)
|
||
lfunc->old_name = func->kfunc->name;
|
||
lfunc->new_func = (void *)func->kfunc->new_addr;
|
||
#if defined(HAVE_IMMEDIATE)
|
||
+#ifdef __ALL_FORCE__
|
||
+ lfunc->immediate = 1;
|
||
+#else
|
||
lfunc->immediate = patch_is_func_forced(lfunc->new_func);
|
||
#endif
|
||
+#endif
|
||
#ifdef HAVE_SYMPOS
|
||
lfunc->old_sympos = func->kfunc->sympos;
|
||
#else
|
||
@@ -507,7 +511,11 @@ static int __init patch_init(void)
|
||
#ifdef __HULK__
|
||
lfunc->old_size = func->kfunc->old_size;
|
||
lfunc->new_size = func->kfunc->new_size;
|
||
+#ifdef __ALL_FORCE__
|
||
+ lfunc->force = 1;
|
||
+#else
|
||
lfunc->force = patch_is_func_forced(func->kfunc->new_addr);
|
||
+#endif
|
||
lfunc->ref_name= func->kfunc->ref_name;
|
||
lfunc->ref_offset = func->kfunc->ref_offset;
|
||
#endif
|
||
diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c
|
||
index 97ae0d4..8bb650d 100644
|
||
--- a/kpatch-build/create-diff-object.c
|
||
+++ b/kpatch-build/create-diff-object.c
|
||
@@ -224,6 +224,7 @@ static int is_special_static(struct symbol *sym)
|
||
"__func__.",
|
||
"_rs.",
|
||
"CSWTCH.",
|
||
+ "__FUNCTION__.",
|
||
NULL,
|
||
};
|
||
char **prefix;
|
||
@@ -386,7 +387,7 @@ static void kpatch_compare_correlated_nonrela_section(struct section *sec)
|
||
{
|
||
struct section *sec1 = sec, *sec2 = sec->twin;
|
||
|
||
- if (sec1->sh.sh_type != SHT_NOBITS &&
|
||
+ if (sec1->sh.sh_type != SHT_NOBITS && sec1->sh.sh_type != SHT_GROUP &&
|
||
memcmp(sec1->data->d_buf, sec2->data->d_buf, sec1->data->d_size))
|
||
sec->status = CHANGED;
|
||
else
|
||
@@ -400,8 +401,9 @@ static void kpatch_compare_correlated_section(struct section *sec)
|
||
/* Compare section headers (must match or fatal) */
|
||
if (sec1->sh.sh_type != sec2->sh.sh_type ||
|
||
sec1->sh.sh_flags != sec2->sh.sh_flags ||
|
||
- sec1->sh.sh_addralign != sec2->sh.sh_addralign ||
|
||
- sec1->sh.sh_entsize != sec2->sh.sh_entsize)
|
||
+ sec1->sh.sh_entsize != sec2->sh.sh_entsize ||
|
||
+ (sec1->sh.sh_addralign != sec2->sh.sh_addralign &&
|
||
+ strcmp(sec1->name, ".rodata")))
|
||
DIFF_FATAL("%s section header details differ", sec1->name);
|
||
|
||
/* Short circuit for mcount sections, we rebuild regardless */
|
||
@@ -763,6 +765,33 @@ static void kpatch_compare_symbols(struct list_head *symlist)
|
||
}
|
||
}
|
||
|
||
+static int kpatch_correlate_group_section(struct list_head *seclist1, struct list_head *seclist2, struct section *sec1, struct section *sec2)
|
||
+{
|
||
+ unsigned int *data1, *end1, *data2;
|
||
+ struct section *isec1, *isec2;
|
||
+
|
||
+ if (sec1->data->d_size != sec2->data->d_size)
|
||
+ return 1;
|
||
+ data1 = sec1->data->d_buf;
|
||
+ data2 = sec2->data->d_buf;
|
||
+ end1 = sec1->data->d_buf + sec1->data->d_size;
|
||
+ data1++;
|
||
+ data2++;
|
||
+ while (data1 < end1) {
|
||
+ isec1 = find_section_by_index(seclist1, *data1);
|
||
+ if (!isec1)
|
||
+ ERROR("group section not found");
|
||
+ isec2 = find_section_by_index(seclist2, *data2);
|
||
+ if (!isec2)
|
||
+ ERROR("group section not found");
|
||
+ if (strcmp(isec1->name, isec2->name))
|
||
+ return 1;
|
||
+ data1++;
|
||
+ data2++;
|
||
+ }
|
||
+ return 0;
|
||
+}
|
||
+
|
||
static void kpatch_correlate_sections(struct list_head *seclist1, struct list_head *seclist2)
|
||
{
|
||
struct section *sec1, *sec2;
|
||
@@ -777,15 +806,18 @@ static void kpatch_correlate_sections(struct list_head *seclist1, struct list_he
|
||
sec1->secsym))
|
||
continue;
|
||
|
||
- /*
|
||
- * Group sections must match exactly to be correlated.
|
||
- * Changed group sections are currently not supported.
|
||
- */
|
||
+ /* Group section的格式为:
|
||
+ * flag
|
||
+ * section index
|
||
+ * section index
|
||
+ * ...
|
||
+ *
|
||
+ * 当C++代码发生修改时,section index可能会发生变化
|
||
+ * 这时候我们就比对一下section index所对应的section的
|
||
+ * name,如果相同,我们就认为这两个group是SAME
|
||
+ * */
|
||
if (sec1->sh.sh_type == SHT_GROUP) {
|
||
- if (sec1->data->d_size != sec2->data->d_size)
|
||
- continue;
|
||
- if (memcmp(sec1->data->d_buf, sec2->data->d_buf,
|
||
- sec1->data->d_size))
|
||
+ if(kpatch_correlate_group_section(seclist1, seclist2, sec1, sec2))
|
||
continue;
|
||
}
|
||
sec1->twin = sec2;
|
||
@@ -1308,17 +1340,21 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
|
||
#ifdef __powerpc64__
|
||
add_off = 0;
|
||
#else
|
||
- if (rela->type == R_X86_64_PC32) {
|
||
- struct insn insn;
|
||
- rela_insn(sec, rela, &insn);
|
||
- add_off = (long)insn.next_byte -
|
||
- (long)sec->base->data->d_buf -
|
||
- rela->offset;
|
||
- } else if (rela->type == R_X86_64_64 ||
|
||
- rela->type == R_X86_64_32S)
|
||
- add_off = 0;
|
||
- else
|
||
- continue;
|
||
+ add_off = 0;
|
||
+ if (arch == EM_X86_64) {
|
||
+ if (rela->type == R_X86_64_PC32) {
|
||
+ struct insn insn;
|
||
+ rela_insn(sec, rela, &insn);
|
||
+ add_off = (long)insn.next_byte -
|
||
+ (long)sec->base->data->d_buf -
|
||
+ rela->offset;
|
||
+ } else if (rela->type == R_X86_64_64 ||
|
||
+ rela->type == R_X86_64_32S)
|
||
+ add_off = 0;
|
||
+ else
|
||
+ continue;
|
||
+ }
|
||
+ /* add_off is always equal to 0 on arm64 */
|
||
#endif
|
||
|
||
/*
|
||
@@ -1421,17 +1457,6 @@ static void kpatch_verify_patchability(struct kpatch_elf *kelf)
|
||
errs++;
|
||
}
|
||
|
||
- if (sec->status != SAME && sec->grouped) {
|
||
- log_normal("changed section %s is part of a section group\n",
|
||
- sec->name);
|
||
- errs++;
|
||
- }
|
||
-
|
||
- if (sec->sh.sh_type == SHT_GROUP && sec->status == NEW) {
|
||
- log_normal("new/changed group sections are not supported\n");
|
||
- errs++;
|
||
- }
|
||
-
|
||
/*
|
||
* ensure we aren't including .data.* or .bss.*
|
||
* (.data.unlikely and .data.once is ok b/c it only has __warned vars)
|
||
@@ -1458,6 +1483,7 @@ static void kpatch_include_section(struct section *sec)
|
||
/* Include the section and its section symbol */
|
||
if (sec->include)
|
||
return;
|
||
+
|
||
sec->include = 1;
|
||
if (sec->secsym)
|
||
sec->secsym->include = 1;
|
||
@@ -1640,7 +1666,7 @@ static void kpatch_include_force_elements(struct kpatch_elf *kelf)
|
||
sym->include = 0;
|
||
}
|
||
|
||
-int kpatch_include_new_static_var(struct kpatch_elf *kelf)
|
||
+static int kpatch_include_new_static_var(struct kpatch_elf *kelf)
|
||
{
|
||
struct symbol *sym;
|
||
|
||
@@ -2253,6 +2279,23 @@ static void kpatch_include_debug_sections(struct kpatch_elf *kelf)
|
||
}
|
||
}
|
||
|
||
+static void kpatch_ignore_debug_sections(struct kpatch_elf *kelf)
|
||
+{
|
||
+ struct section *sec;
|
||
+
|
||
+ /* include all .debug_* sections */
|
||
+ list_for_each_entry(sec, &kelf->sections, list) {
|
||
+ if (is_debug_section(sec)) {
|
||
+ sec->include = 0;
|
||
+ sec->status = SAME;
|
||
+ if (!is_rela_section(sec)) {
|
||
+ sec->secsym->include = 0;
|
||
+ sec->secsym->status = SAME;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
static void kpatch_mark_ignored_sections(struct kpatch_elf *kelf)
|
||
{
|
||
struct section *sec, *strsec, *ignoresec;
|
||
@@ -2589,14 +2632,13 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
||
funcs[index].old_size = result.size;
|
||
funcs[index].new_size = sym->sym.st_size;
|
||
funcs[index].sympos = result.pos;
|
||
- if (lookup_is_duplicate_symbol(table, sym->name)) {
|
||
+ if (lookup_is_duplicate_symbol(table, sym->name, objname, result.pos)) {
|
||
struct lookup_refsym refsym;
|
||
long offset;
|
||
|
||
- if (lookup_ref_symbol(table, sym->name, &refsym))
|
||
+ if (lookup_ref_symbol_offset(table, sym->name, &refsym, objname, &offset))
|
||
ERROR("unresolvable ambiguity on symbol %s\n", sym->name);
|
||
|
||
- offset = (long)result.value - (long)refsym.value;
|
||
funcs[index].ref_offset = offset;
|
||
|
||
/*
|
||
@@ -2631,7 +2673,7 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
||
ALLOC_LINK(rela, &relasec->relas);
|
||
rela->sym = strsym;
|
||
rela->type = absolute_rela_type;
|
||
- rela->addend = offset_of_string(&kelf->strings, sym->name);
|
||
+ rela->addend = offset_of_string(&kelf->strings, strndup(sym->name, KSYM_NAME_LEN-1));
|
||
rela->offset = index * sizeof(*funcs) +
|
||
offsetof(struct kpatch_patch_func, name);
|
||
|
||
@@ -2780,13 +2822,14 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
||
*/
|
||
if (may_need_dynrela(rela))
|
||
toc_rela(rela)->need_dynrela = 1;
|
||
+
|
||
if (rela->sym->sec) {
|
||
if (rela->sym->type == STT_FUNC &&
|
||
- rela->sym->status == CHANGED &&
|
||
- rela->sym->sec != sec->base &&
|
||
- sec->base->sym &&
|
||
- sec->base->sym->type == STT_FUNC)
|
||
- toc_rela(rela)->need_dynrela = 1;
|
||
+ rela->sym->status == CHANGED &&
|
||
+ rela->sym->sec != sec->base &&
|
||
+ sec->base->sym &&
|
||
+ sec->base->sym->type == STT_FUNC)
|
||
+ toc_rela(rela)->need_dynrela = 1;
|
||
}
|
||
}
|
||
}
|
||
@@ -2820,6 +2863,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
||
if (!strcmp(sec->name, ".rela.kpatch.funcs") ||
|
||
!strcmp(sec->name, ".rela.kpatch.dynrelas"))
|
||
continue;
|
||
+
|
||
list_for_each_entry_safe(rela, safe, &sec->relas, list) {
|
||
if (!rela->need_dynrela)
|
||
continue;
|
||
@@ -2963,13 +3007,12 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
||
else {
|
||
/* for modules, src is discovered at runtime */
|
||
ksyms[index].src = 0;
|
||
- if (lookup_is_duplicate_symbol(table, rela->sym->name)) {
|
||
+ if (lookup_is_duplicate_symbol(table, rela->sym->name, objname, result.pos)) {
|
||
struct lookup_refsym refsym;
|
||
|
||
- if (lookup_ref_symbol(table, rela->sym->name, &refsym))
|
||
+ if (lookup_ref_symbol_offset(table, rela->sym->name, &refsym, objname, &ref_offset))
|
||
ERROR("unresolvable ambiguity on symbol %s\n", rela->sym->name);
|
||
|
||
- ref_offset = (long)result.value - (long)refsym.value;
|
||
/* add rela to fill in ref_name field */
|
||
ALLOC_LINK(rela2, &krela_sec->rela->relas);
|
||
rela2->sym = strsym;
|
||
@@ -2987,7 +3030,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
||
ALLOC_LINK(rela2, &ksym_sec->rela->relas);
|
||
rela2->sym = strsym;
|
||
rela2->type = absolute_rela_type;
|
||
- rela2->addend = offset_of_string(&kelf->strings, rela->sym->name);
|
||
+ rela2->addend = offset_of_string(&kelf->strings, strndup(rela->sym->name, KSYM_NAME_LEN-1));
|
||
rela2->offset = index * sizeof(*ksyms) + \
|
||
offsetof(struct kpatch_symbol, name);
|
||
|
||
@@ -3163,6 +3206,9 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf)
|
||
* __fentry__" so that ftrace will be happy.
|
||
*/
|
||
newdata = malloc(sym->sec->data->d_size);
|
||
+ if (!newdata)
|
||
+ ERROR("malloc");
|
||
+
|
||
memcpy(newdata, sym->sec->data->d_buf, sym->sec->data->d_size);
|
||
sym->sec->data->d_buf = newdata;
|
||
insn = newdata;
|
||
@@ -3186,6 +3232,8 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf)
|
||
rela->offset = index * sizeof(*funcs);
|
||
|
||
newdata = malloc(sym->sec->data->d_size);
|
||
+ if (!newdata)
|
||
+ ERROR("malloc");
|
||
memcpy(newdata, sym->sec->data->d_buf, sym->sec->data->d_size);
|
||
sym->sec->data->d_buf = newdata;
|
||
insnp = newdata;
|
||
@@ -3354,6 +3402,8 @@ int main(int argc, char *argv[])
|
||
char *parent_symtab, *mod_symvers, *patch_name, *output_obj;
|
||
struct sym_compare_type *base_locals;
|
||
char *gcc_add_option, *mlongcall;
|
||
+ char *no_profiling_calls;
|
||
+ char *kallsyms;
|
||
|
||
arguments.debug = 0;
|
||
argp_parse (&argp, argc, argv, 0, NULL, &arguments);
|
||
@@ -3400,9 +3450,12 @@ int main(int argc, char *argv[])
|
||
return EXIT_STATUS_NO_CHANGE;
|
||
}
|
||
|
||
+ kallsyms = getenv("KALLSYMS");
|
||
+ if (kallsyms)
|
||
+ log_debug("kallsyms file:%s\n", kallsyms);
|
||
/* create symbol lookup table */
|
||
base_locals = kpatch_elf_locals(kelf_base);
|
||
- lookup = lookup_open(parent_symtab, mod_symvers, hint, base_locals);
|
||
+ lookup = lookup_open(parent_symtab, mod_symvers, hint, base_locals, kallsyms);
|
||
free(base_locals);
|
||
|
||
kpatch_mark_grouped_sections(kelf_patched);
|
||
@@ -3420,7 +3473,12 @@ int main(int argc, char *argv[])
|
||
*/
|
||
kpatch_mark_ignored_sections(kelf_patched);
|
||
kpatch_compare_correlated_elements(kelf_patched);
|
||
- kpatch_check_func_profiling_calls(kelf_patched);
|
||
+
|
||
+ no_profiling_calls = getenv("NO_PROFILING_CALLS");
|
||
+ if (!no_profiling_calls)
|
||
+ kpatch_check_func_profiling_calls(kelf_patched);
|
||
+ else
|
||
+ log_debug("NO_PROFILING_CALLS set\n");
|
||
kpatch_elf_teardown(kelf_base);
|
||
kpatch_elf_free(kelf_base);
|
||
|
||
@@ -3430,6 +3488,7 @@ int main(int argc, char *argv[])
|
||
kpatch_include_standard_elements(kelf_patched);
|
||
num_changed = kpatch_include_changed_functions(kelf_patched);
|
||
kpatch_include_debug_sections(kelf_patched);
|
||
+ kpatch_ignore_debug_sections(kelf_patched);
|
||
callbacks_exist = kpatch_include_callback_elements(kelf_patched);
|
||
kpatch_include_force_elements(kelf_patched);
|
||
new_globals_exist = kpatch_include_new_globals(kelf_patched);
|
||
@@ -3470,10 +3529,10 @@ int main(int argc, char *argv[])
|
||
kpatch_build_strings_section_data(kelf_out);
|
||
|
||
gcc_add_option = getenv("GCC_ADD_OPTION");
|
||
- printf("gcc add option :%s\n", gcc_add_option);
|
||
+ log_debug("gcc add option :%s\n", gcc_add_option);
|
||
mlongcall = strstr(gcc_add_option, "-mlong-calls");
|
||
if (arch == EM_AARCH64 && mlongcall) {
|
||
- printf("-mlong-calls found, no need to create mcount section\n");
|
||
+ log_debug("-mlong-calls found, no need to create mcount section\n");
|
||
} else {
|
||
kpatch_create_mcount_sections(kelf_out);
|
||
}
|
||
diff --git a/kpatch-build/create-klp-module.c b/kpatch-build/create-klp-module.c
|
||
index 253704b..7a72afd 100644
|
||
--- a/kpatch-build/create-klp-module.c
|
||
+++ b/kpatch-build/create-klp-module.c
|
||
@@ -38,7 +38,9 @@ enum loglevel loglevel = NORMAL;
|
||
*/
|
||
static struct symbol *find_or_add_ksym_to_symbols(struct kpatch_elf *kelf,
|
||
struct section *ksymsec,
|
||
- char *strings, int offset)
|
||
+ char *strings, int offset,
|
||
+ char *ref_name,
|
||
+ long ref_offset)
|
||
{
|
||
struct kpatch_symbol *ksyms, *ksym;
|
||
struct symbol *sym;
|
||
@@ -71,9 +73,14 @@ static struct symbol *find_or_add_ksym_to_symbols(struct kpatch_elf *kelf,
|
||
if (!objname)
|
||
ERROR("strdup");
|
||
|
||
- snprintf(pos, 32, "%lu", ksym->pos);
|
||
/* .klp.sym.objname.name,pos */
|
||
- snprintf(buf, 256, KLP_SYM_PREFIX "%s.%s,%s", objname, name, pos);
|
||
+ if (!ref_name) {
|
||
+ snprintf(pos, 32, "%lu", ksym->pos);
|
||
+ snprintf(buf, 256, KLP_SYM_PREFIX "%s.%s,%s", objname, name, pos);
|
||
+ } else {
|
||
+ snprintf(pos, 32, "%ld", ref_offset);
|
||
+ snprintf(buf, 256, KLP_SYM_PREFIX "%s-%s,%s", objname, ref_name, pos);
|
||
+ }
|
||
|
||
/* Look for an already allocated symbol */
|
||
list_for_each_entry(sym, &kelf->symbols, list) {
|
||
@@ -180,6 +187,7 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section
|
||
struct rela *rela;
|
||
char *objname;
|
||
int nr, index, offset, dest_off;
|
||
+ char *ref_name;
|
||
|
||
krelas = krelasec->data->d_buf;
|
||
nr = krelasec->data->d_size / sizeof(*krelas);
|
||
@@ -206,6 +214,17 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section
|
||
if (!objname)
|
||
ERROR("strdup");
|
||
|
||
+ /* Get the unique ref_name */
|
||
+ rela = find_rela_by_offset(krelasec->rela,
|
||
+ offset + offsetof(struct kpatch_relocation, ref_name));
|
||
+ if (!rela)
|
||
+ ref_name = NULL;
|
||
+ else {
|
||
+ ref_name = strdup(strings + rela->addend);
|
||
+ if (!ref_name)
|
||
+ ERROR("strdup");
|
||
+ }
|
||
+
|
||
/* Get the .kpatch.symbol entry for the rela src */
|
||
rela = find_rela_by_offset(krelasec->rela,
|
||
offset + offsetof(struct kpatch_relocation, ksym));
|
||
@@ -213,7 +232,7 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section
|
||
ERROR("find_rela_by_offset");
|
||
|
||
/* Create (or find) a klp symbol from the rela src entry */
|
||
- sym = find_or_add_ksym_to_symbols(kelf, ksymsec, strings, rela->addend);
|
||
+ sym = find_or_add_ksym_to_symbols(kelf, ksymsec, strings, rela->addend, ref_name, krelas[index].ref_offset);
|
||
if (!sym)
|
||
ERROR("error finding or adding ksym to symtab");
|
||
|
||
diff --git a/kpatch-build/create-kpatch-module.c b/kpatch-build/create-kpatch-module.c
|
||
index 8292dc8..3e24b3a 100644
|
||
--- a/kpatch-build/create-kpatch-module.c
|
||
+++ b/kpatch-build/create-kpatch-module.c
|
||
@@ -112,14 +112,10 @@ static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section
|
||
dynrelas[index].ref_name = krelas[index].ref_name;
|
||
dynrelas[index].ref_offset = krelas[index].ref_offset;
|
||
|
||
- if (dynrelas[index].ref_offset)
|
||
- {
|
||
- /* Get objname offset */
|
||
- rela = find_rela_by_offset(krelasec->rela,
|
||
- index * sizeof(*krelas) + offsetof(struct kpatch_relocation, ref_name));
|
||
- if (!rela) {
|
||
- ERROR("find_rela_by_offset");
|
||
- }
|
||
+ /* Get ref_name offset */
|
||
+ rela = find_rela_by_offset(krelasec->rela,
|
||
+ index * sizeof(*krelas) + offsetof(struct kpatch_relocation, ref_name));
|
||
+ if (rela) {
|
||
ref_name_offset = rela->addend;
|
||
/* ref_name */
|
||
ALLOC_LINK(rela, &dynsec->rela->relas);
|
||
@@ -128,9 +124,8 @@ static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section
|
||
rela->addend = ref_name_offset;
|
||
rela->offset = index * sizeof(*dynrelas) + \
|
||
offsetof(struct kpatch_patch_dynrela, ref_name);
|
||
-
|
||
}
|
||
-
|
||
+
|
||
/* dest */
|
||
ALLOC_LINK(rela, &dynsec->rela->relas);
|
||
rela->sym = sym;
|
||
diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build
|
||
index 81a6a44..a860f12 100755
|
||
--- a/kpatch-build/kpatch-build
|
||
+++ b/kpatch-build/kpatch-build
|
||
@@ -953,6 +953,10 @@ if grep "hulk" $SRCDIR/Makefile > /dev/null; then
|
||
export KCPPFLAGS="-D__HULK__ $KCPPFLAGS"
|
||
fi
|
||
|
||
+if [[ -n "$NO_STACK_CHECK" ]];then
|
||
+ export KCPPFLAGS="-D__ALL_FORCE__ $KCPPFLAGS"
|
||
+fi
|
||
+
|
||
echo "Building patch module: $MODNAME.ko"
|
||
|
||
if [[ -z "$USERSRCDIR" ]] && [[ "$DISTRO" = ubuntu ]]; then
|
||
@@ -990,6 +994,7 @@ KBUILD_EXTRA_SYMBOLS="$KBUILD_EXTRA_SYMBOLS" \
|
||
KPATCH_LDFLAGS="$KPATCH_LDFLAGS" \
|
||
CROSS_COMPILE="$ARCH_COMPILE" \
|
||
make 2>&1 | logger || die
|
||
+${ARCH_COMPILE}strip -g "$TEMPDIR/patch/$MODNAME.ko"
|
||
|
||
if ! "$KPATCH_MODULE"; then
|
||
if [[ -z "$KPATCH_LDFLAGS" ]]; then
|
||
@@ -1019,7 +1024,7 @@ fi
|
||
# column containing lines unique to first file.
|
||
UNDEFINED=$(comm -23 <(sort -u "${TEMPDIR}"/undefined_references) \
|
||
<(sort -u "${TEMPDIR}"/new_symbols) | tr '\n' ' ')
|
||
-[[ ! -z "$UNDEFINED" ]] && die "Undefined symbols: $UNDEFINED"
|
||
+[[ -z "$USERMODDIR" ]] && [[ ! -z "$UNDEFINED" ]] && die "Undefined symbols: $UNDEFINED"
|
||
|
||
cp -f "$TEMPDIR/patch/$MODNAME.ko" "$BASE" || die
|
||
|
||
diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c
|
||
index fcb7161..0794031 100644
|
||
--- a/kpatch-build/kpatch-elf.c
|
||
+++ b/kpatch-build/kpatch-elf.c
|
||
@@ -700,8 +700,14 @@ void kpatch_reindex_elements(struct kpatch_elf *kelf)
|
||
unsigned int index;
|
||
|
||
index = 1; /* elf write function handles NULL section 0 */
|
||
- list_for_each_entry(sec, &kelf->sections, list)
|
||
+ list_for_each_entry(sec, &kelf->sections, list) {
|
||
sec->index = index++;
|
||
+ /*
|
||
+ * since we exclude .group section, we clear SHF_GROUP
|
||
+ * for every section in case of link error.
|
||
+ * */
|
||
+ sec->sh.sh_flags &= (~SHF_GROUP);
|
||
+ }
|
||
|
||
index = 0;
|
||
list_for_each_entry(sym, &kelf->symbols, list) {
|
||
diff --git a/kpatch-build/kpatch-gcc b/kpatch-build/kpatch-gcc
|
||
index be18847..d36e67b 100755
|
||
--- a/kpatch-build/kpatch-gcc
|
||
+++ b/kpatch-build/kpatch-gcc
|
||
@@ -13,7 +13,7 @@ fi
|
||
|
||
declare -a args=("$@")
|
||
|
||
-if [[ "$TOOLCHAINCMD" = ${ARCH_COMPILE}gcc ]] ; then
|
||
+if [[ "$TOOLCHAINCMD" = ${ARCH_COMPILE}gcc || "$TOOLCHAINCMD" = ${ARCH_COMPILE}g++ ]] ; then
|
||
while [ "$#" -gt 0 ]; do
|
||
if [ "$1" = "-o" ]; then
|
||
obj="$2"
|
||
diff --git a/kpatch-build/lookup.c b/kpatch-build/lookup.c
|
||
index e4677db..2a728f6 100644
|
||
--- a/kpatch-build/lookup.c
|
||
+++ b/kpatch-build/lookup.c
|
||
@@ -45,6 +45,7 @@ struct object_symbol {
|
||
char *name;
|
||
int type, bind;
|
||
int sec_index;
|
||
+ unsigned long kaddr;
|
||
};
|
||
|
||
struct export_symbol {
|
||
@@ -238,6 +239,8 @@ static void symtab_read(struct lookup_table *table, char *path)
|
||
table->obj_syms[i].bind = STB_GLOBAL;
|
||
} else if (!strcmp(bind, "WEAK")) {
|
||
table->obj_syms[i].bind = STB_WEAK;
|
||
+ } else if (!strcmp(bind, "GNU_UNIQUE")) {
|
||
+ table->obj_syms[i].bind = STB_GNU_UNIQUE;
|
||
} else {
|
||
ERROR("unknown symbol bind %s", bind);
|
||
}
|
||
@@ -263,6 +266,56 @@ static void symtab_read(struct lookup_table *table, char *path)
|
||
fclose(file);
|
||
}
|
||
|
||
+static void ksymtab_read(struct lookup_table *table, char *path)
|
||
+{
|
||
+ FILE *file;
|
||
+ struct object_symbol *sym, *sym1, *sym2;
|
||
+ unsigned long value;
|
||
+ int i, j, idx;
|
||
+ char line[256], name[256], type[256], mod[256];
|
||
+ idx = 0;
|
||
+
|
||
+ if ((file = fopen(path, "r")) == NULL)
|
||
+ ERROR("fopen");
|
||
+
|
||
+ while (fgets(line, 256, file)) {
|
||
+ if (sscanf(line, "%lx %s %s [%s]\n",
|
||
+ &value, type, name, mod) != 4)
|
||
+ continue;
|
||
+
|
||
+ if (name[0] == '$')
|
||
+ continue;
|
||
+
|
||
+ i = idx;
|
||
+ for_each_obj_symbol_continue(i, sym, table) {
|
||
+ if (!strncmp(sym->name, name, KSYM_NAME_LEN-1)) {
|
||
+ sym->kaddr = value;
|
||
+ idx = i + 1;
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ for_each_obj_symbol(i, sym1, table) {
|
||
+ if (sym1->kaddr == 0)
|
||
+ continue;
|
||
+ for_each_obj_symbol(j, sym2, table) {
|
||
+ if (sym2->kaddr == 0)
|
||
+ continue;
|
||
+ if (sym1 == sym2)
|
||
+ continue;
|
||
+ if (sym1->sec_index != sym2->sec_index)
|
||
+ continue;
|
||
+ if ((long)sym1->value - (long)sym2->value ==
|
||
+ (long)sym1->kaddr - (long)sym2->kaddr)
|
||
+ continue;
|
||
+
|
||
+ ERROR("base mismatch(symbol offset)");
|
||
+ }
|
||
+ }
|
||
+ fclose(file);
|
||
+}
|
||
+
|
||
static void symvers_read(struct lookup_table *table, char *path)
|
||
{
|
||
FILE *file;
|
||
@@ -306,7 +359,8 @@ static void symvers_read(struct lookup_table *table, char *path)
|
||
}
|
||
|
||
struct lookup_table *lookup_open(char *symtab_path, char *symvers_path,
|
||
- char *hint, struct sym_compare_type *locals)
|
||
+ char *hint, struct sym_compare_type *locals,
|
||
+ char *kallsyms)
|
||
{
|
||
struct lookup_table *table;
|
||
|
||
@@ -317,6 +371,8 @@ struct lookup_table *lookup_open(char *symtab_path, char *symvers_path,
|
||
|
||
symtab_read(table, symtab_path);
|
||
symvers_read(table, symvers_path);
|
||
+ if (kallsyms)
|
||
+ ksymtab_read(table, kallsyms);
|
||
find_local_syms(table, hint, locals);
|
||
|
||
return table;
|
||
@@ -343,6 +399,15 @@ int lookup_local_symbol(struct lookup_table *table, char *name,
|
||
for_each_obj_symbol(i, sym, table) {
|
||
if (sym->bind == STB_LOCAL && !strcmp(sym->name, name))
|
||
pos++;
|
||
+ else {
|
||
+ /* symbol name longer than KSYM_NAME_LEN will be truncated
|
||
+ * by kernel, so we can not find it using its original
|
||
+ * name. we need to add pos for symbols which have same
|
||
+ * KSYM_NAME_LEN-1 long prefix.*/
|
||
+ if (strlen(name) >= KSYM_NAME_LEN-1 &&
|
||
+ !strncmp(sym->name, name, KSYM_NAME_LEN-1))
|
||
+ pos++;
|
||
+ }
|
||
|
||
if (table->local_syms == sym) {
|
||
in_file = 1;
|
||
@@ -374,15 +439,25 @@ int lookup_global_symbol(struct lookup_table *table, char *name,
|
||
struct lookup_result *result)
|
||
{
|
||
struct object_symbol *sym;
|
||
+ unsigned long pos = 0;
|
||
int i;
|
||
|
||
memset(result, 0, sizeof(*result));
|
||
for_each_obj_symbol(i, sym, table) {
|
||
- if ((sym->bind == STB_GLOBAL || sym->bind == STB_WEAK) &&
|
||
+ /* symbol name longer than KSYM_NAME_LEN will be truncated
|
||
+ * by kernel, so we can not find it using its original
|
||
+ * name. we need to add pos for symbols which have same
|
||
+ * KSYM_NAME_LEN-1 long prefix.*/
|
||
+ if (strlen(name) >= KSYM_NAME_LEN-1 &&
|
||
+ !strncmp(sym->name, name, KSYM_NAME_LEN-1))
|
||
+ pos++;
|
||
+
|
||
+ if ((sym->bind == STB_GLOBAL || sym->bind == STB_WEAK
|
||
+ || sym->bind == STB_GNU_UNIQUE) &&
|
||
!strcmp(sym->name, name)) {
|
||
result->value = sym->value;
|
||
result->size = sym->size;
|
||
- result->pos = 0; /* always 0 for global symbols */
|
||
+ result->pos = pos;
|
||
return 0;
|
||
}
|
||
}
|
||
@@ -429,10 +504,12 @@ char *lookup_exported_symbol_objname(struct lookup_table *table, char *name)
|
||
return NULL;
|
||
}
|
||
|
||
-int lookup_is_duplicate_symbol(struct lookup_table *table, char *name)
|
||
+int lookup_is_duplicate_symbol(struct lookup_table *table, char *name,
|
||
+ char *objname, unsigned long pos)
|
||
{
|
||
struct object_symbol *sym;
|
||
int i, count = 0;
|
||
+ char posstr[32], buf[256];
|
||
|
||
for_each_obj_symbol(i, sym, table)
|
||
if (!strcmp(sym->name, name)) {
|
||
@@ -441,6 +518,17 @@ int lookup_is_duplicate_symbol(struct lookup_table *table, char *name)
|
||
return 1;
|
||
}
|
||
|
||
+ /* symbol name longer than KSYM_NAME_LEN will be truncated
|
||
+ * by kernel, so we can not find it using its original
|
||
+ * name. Here, we consider these long name symbol as duplicated
|
||
+ * symbols. since create_klp_module will create symbol name
|
||
+ * format like .klp.sym.objname.symbol,pos, so we consider name
|
||
+ * length longer than KSYM_NAME_LEN-1 bytes as duplicated symbol*/
|
||
+ snprintf(posstr, 32, "%lu", pos);
|
||
+ snprintf(buf, 256, KLP_SYM_PREFIX "%s.%s,%s", objname, name, posstr);
|
||
+ if (strlen(buf) >= KSYM_NAME_LEN-1)
|
||
+ return 1;
|
||
+
|
||
return 0;
|
||
}
|
||
|
||
@@ -487,24 +575,45 @@ struct object_symbol *lookup_find_symbol_by_name(struct lookup_table *table, cha
|
||
return sym;
|
||
}
|
||
|
||
-int lookup_ref_symbol(struct lookup_table *table, char *name,
|
||
- struct lookup_refsym *refsym)
|
||
+int lookup_ref_symbol_offset(struct lookup_table *table, char *name,
|
||
+ struct lookup_refsym *refsym,
|
||
+ char *objname, long *offset)
|
||
{
|
||
struct object_symbol *orig_sym, *sym;
|
||
int i;
|
||
|
||
orig_sym = lookup_find_symbol_by_name(table, name);
|
||
if (!orig_sym)
|
||
- ERROR("lookup_ref_symbol");
|
||
+ ERROR("lookup_ref_symbol_offset");
|
||
memset(refsym, 0, sizeof(*refsym));
|
||
+
|
||
+ /*find a unique symbol in the same section first*/
|
||
for_each_obj_symbol(i, sym, table) {
|
||
if (!strcmp(sym->name, name) || sym->type == STT_FILE ||
|
||
sym->sec_index != orig_sym->sec_index)
|
||
continue;
|
||
|
||
- if (!lookup_is_duplicate_symbol(table, sym->name)) {
|
||
+ if (!lookup_is_duplicate_symbol(table, sym->name, objname, 1)) {
|
||
refsym->name = sym->name;
|
||
refsym->value = sym->value;
|
||
+ *offset = (long)orig_sym->value - (long)sym->value;
|
||
+ return 0;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (orig_sym->kaddr == 0)
|
||
+ return 1;
|
||
+
|
||
+ /*find a unique symbol has kaddr*/
|
||
+ for_each_obj_symbol(i, sym, table) {
|
||
+ if (!strcmp(sym->name, name) || sym->type == STT_FILE ||
|
||
+ sym->kaddr == 0)
|
||
+ continue;
|
||
+
|
||
+ if (!lookup_is_duplicate_symbol(table, sym->name, objname, 1)) {
|
||
+ refsym->name = sym->name;
|
||
+ refsym->value = 0;
|
||
+ *offset = (long)orig_sym->kaddr - (long)sym->kaddr;
|
||
return 0;
|
||
}
|
||
}
|
||
diff --git a/kpatch-build/lookup.h b/kpatch-build/lookup.h
|
||
index 5ad9241..00b6ccc 100644
|
||
--- a/kpatch-build/lookup.h
|
||
+++ b/kpatch-build/lookup.h
|
||
@@ -1,6 +1,9 @@
|
||
#ifndef _LOOKUP_H_
|
||
#define _LOOKUP_H_
|
||
|
||
+#include "kpatch-elf.h"
|
||
+#define KSYM_NAME_LEN 128
|
||
+
|
||
struct lookup_table;
|
||
|
||
struct lookup_result {
|
||
@@ -20,7 +23,8 @@ struct lookup_refsym {
|
||
};
|
||
|
||
struct lookup_table *lookup_open(char *symtab_path, char *symvers_path,
|
||
- char *hint, struct sym_compare_type *locals);
|
||
+ char *hint, struct sym_compare_type *locals,
|
||
+ char *kallsyms);
|
||
void lookup_close(struct lookup_table *table);
|
||
int lookup_local_symbol(struct lookup_table *table, char *name,
|
||
struct lookup_result *result);
|
||
@@ -28,8 +32,10 @@ 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);
|
||
char *lookup_exported_symbol_objname(struct lookup_table *table, char *name);
|
||
-int lookup_is_duplicate_symbol(struct lookup_table *table, char *name);
|
||
-int lookup_ref_symbol(struct lookup_table *table, char *name,
|
||
- struct lookup_refsym *refsym);
|
||
+int lookup_is_duplicate_symbol(struct lookup_table *table, char *name,
|
||
+ char *objname, unsigned long pos);
|
||
+int lookup_ref_symbol_offset(struct lookup_table *table, char *name,
|
||
+ struct lookup_refsym *refsym, char *objname,
|
||
+ long *offset);
|
||
|
||
#endif /* _LOOKUP_H_ */
|
||
--
|
||
1.7.5.4
|
||
|