mirror of https://github.com/dynup/kpatch
Merge pull request #1269 from jpoimboe/reloc-addend
Fix addend handling and other cleanups for s390
This commit is contained in:
commit
f6e1838ab9
|
@ -31,8 +31,8 @@ all: $(TARGETS)
|
|||
-include $(SOURCES:.c=.d)
|
||||
|
||||
create-diff-object: create-diff-object.o kpatch-elf.o lookup.o $(INSN)
|
||||
create-klp-module: create-klp-module.o kpatch-elf.o
|
||||
create-kpatch-module: create-kpatch-module.o kpatch-elf.o
|
||||
create-klp-module: create-klp-module.o kpatch-elf.o $(INSN)
|
||||
create-kpatch-module: create-kpatch-module.o kpatch-elf.o $(INSN)
|
||||
|
||||
$(PLUGIN): gcc-plugins/ppc64le-plugin.c
|
||||
g++ $(PLUGIN_CFLAGS) $< -o $@
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
|
||||
#include "list.h"
|
||||
#include "lookup.h"
|
||||
#include "asm/insn.h"
|
||||
#include "kpatch-patch.h"
|
||||
#include "kpatch-elf.h"
|
||||
#include "kpatch-intermediate.h"
|
||||
|
@ -85,51 +84,51 @@ struct special_section {
|
|||
* Functions
|
||||
* **********/
|
||||
|
||||
static int is_bundleable(struct symbol *sym)
|
||||
static bool is_bundleable(struct symbol *sym)
|
||||
{
|
||||
if (sym->type == STT_FUNC &&
|
||||
!strncmp(sym->sec->name, ".text.",6) &&
|
||||
!strcmp(sym->sec->name + 6, sym->name))
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
if (sym->type == STT_FUNC &&
|
||||
!strncmp(sym->sec->name, ".text.unlikely.",15) &&
|
||||
(!strcmp(sym->sec->name + 15, sym->name) ||
|
||||
(strstr(sym->name, ".cold") &&
|
||||
!strncmp(sym->sec->name + 15, sym->name, strlen(sym->sec->name) - 15))))
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
if (sym->type == STT_FUNC &&
|
||||
!strncmp(sym->sec->name, ".text.hot.",10) &&
|
||||
!strcmp(sym->sec->name + 10, sym->name))
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
if (sym->type == STT_OBJECT &&
|
||||
!strncmp(sym->sec->name, ".data.",6) &&
|
||||
!strcmp(sym->sec->name + 6, sym->name))
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
if (sym->type == STT_OBJECT &&
|
||||
!strncmp(sym->sec->name, ".data.rel.", 10) &&
|
||||
!strcmp(sym->sec->name + 10, sym->name))
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
if (sym->type == STT_OBJECT &&
|
||||
!strncmp(sym->sec->name, ".data.rel.ro.", 13) &&
|
||||
!strcmp(sym->sec->name + 13, sym->name))
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
if (sym->type == STT_OBJECT &&
|
||||
!strncmp(sym->sec->name, ".rodata.",8) &&
|
||||
!strcmp(sym->sec->name + 8, sym->name))
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
if (sym->type == STT_OBJECT &&
|
||||
!strncmp(sym->sec->name, ".bss.",5) &&
|
||||
!strcmp(sym->sec->name + 5, sym->name))
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Symbol st_others value for powerpc */
|
||||
|
@ -303,6 +302,12 @@ static bool is_dynamic_debug_symbol(struct symbol *sym)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool is_string_literal_section(struct section *sec)
|
||||
{
|
||||
return !strncmp(sec->name, ".rodata.", 8) &&
|
||||
strstr(sec->name, ".str1.");
|
||||
}
|
||||
|
||||
/*
|
||||
* This function detects whether the given symbol is a "special" static local
|
||||
* variable (for lack of a better term).
|
||||
|
@ -506,7 +511,7 @@ static bool rela_equal(struct rela *rela1, struct rela *rela2)
|
|||
return !kpatch_mangled_strcmp(rela_toc1->sym->name, rela_toc2->sym->name);
|
||||
}
|
||||
|
||||
static void kpatch_compare_correlated_rela_section(struct section *sec)
|
||||
static void kpatch_compare_correlated_rela_section(struct section *relasec)
|
||||
{
|
||||
struct rela *rela1, *rela2 = NULL;
|
||||
|
||||
|
@ -514,22 +519,22 @@ static void kpatch_compare_correlated_rela_section(struct section *sec)
|
|||
* On ppc64le, don't compare the .rela.toc section. The .toc and
|
||||
* .rela.toc sections are included as standard elements.
|
||||
*/
|
||||
if (!strcmp(sec->name, ".rela.toc")) {
|
||||
sec->status = SAME;
|
||||
if (!strcmp(relasec->name, ".rela.toc")) {
|
||||
relasec->status = SAME;
|
||||
return;
|
||||
}
|
||||
|
||||
rela2 = list_entry(sec->twin->relas.next, struct rela, list);
|
||||
list_for_each_entry(rela1, &sec->relas, list) {
|
||||
rela2 = list_entry(relasec->twin->relas.next, struct rela, list);
|
||||
list_for_each_entry(rela1, &relasec->relas, list) {
|
||||
if (rela_equal(rela1, rela2)) {
|
||||
rela2 = list_entry(rela2->list.next, struct rela, list);
|
||||
continue;
|
||||
}
|
||||
sec->status = CHANGED;
|
||||
relasec->status = CHANGED;
|
||||
return;
|
||||
}
|
||||
|
||||
sec->status = SAME;
|
||||
relasec->status = SAME;
|
||||
}
|
||||
|
||||
static void kpatch_compare_correlated_nonrela_section(struct section *sec)
|
||||
|
@ -577,39 +582,6 @@ out:
|
|||
log_debug("section %s has changed\n", sec->name);
|
||||
}
|
||||
|
||||
static unsigned int insn_length(struct kpatch_elf *kelf, void *addr)
|
||||
{
|
||||
struct insn decoded_insn;
|
||||
char *insn = addr;
|
||||
|
||||
switch(kelf->arch) {
|
||||
|
||||
case X86_64:
|
||||
insn_init(&decoded_insn, addr, 1);
|
||||
insn_get_length(&decoded_insn);
|
||||
return decoded_insn.length;
|
||||
|
||||
case PPC64:
|
||||
return 4;
|
||||
|
||||
case S390:
|
||||
switch(insn[0] >> 6) {
|
||||
case 0:
|
||||
return 2;
|
||||
case 1:
|
||||
case 2:
|
||||
return 4;
|
||||
case 3:
|
||||
return 6;
|
||||
}
|
||||
|
||||
default:
|
||||
ERROR("unsupported arch");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is not comprehensive, i.e. it doesn't detect immediate loads
|
||||
* to *all* registers. It only detects those which have been found in the wild
|
||||
|
@ -874,7 +846,7 @@ static enum subsection kpatch_subsection_type(struct section *sec)
|
|||
return SUBSECTION_NORMAL;
|
||||
}
|
||||
|
||||
static int kpatch_subsection_changed(struct section *sec1, struct section *sec2)
|
||||
static bool kpatch_subsection_changed(struct section *sec1, struct section *sec2)
|
||||
{
|
||||
return kpatch_subsection_type(sec1) != kpatch_subsection_type(sec2);
|
||||
}
|
||||
|
@ -1138,13 +1110,13 @@ static char *kpatch_section_function_name(struct section *sec)
|
|||
return sec->sym ? sec->sym->name : sec->name;
|
||||
}
|
||||
|
||||
static struct symbol *kpatch_find_uncorrelated_rela(struct section *rela_sec,
|
||||
static struct symbol *kpatch_find_uncorrelated_rela(struct section *relasec,
|
||||
struct symbol *sym)
|
||||
{
|
||||
struct rela *rela, *rela_toc;
|
||||
|
||||
/* find the patched object's corresponding variable */
|
||||
list_for_each_entry(rela, &rela_sec->relas, list) {
|
||||
list_for_each_entry(rela, &relasec->relas, list) {
|
||||
struct symbol *patched_sym;
|
||||
|
||||
rela_toc = toc_rela(rela);
|
||||
|
@ -1198,34 +1170,34 @@ static struct symbol *kpatch_find_static_twin_in_children(struct symbol *parent,
|
|||
* in the base object, find a corresponding usage of a similarly named symbol
|
||||
* in the patched object.
|
||||
*/
|
||||
static struct symbol *kpatch_find_static_twin(struct section *sec,
|
||||
static struct symbol *kpatch_find_static_twin(struct section *relasec,
|
||||
struct symbol *sym)
|
||||
{
|
||||
struct symbol *res;
|
||||
|
||||
if (!sec->twin && sec->base->sym) {
|
||||
if (!relasec->twin && relasec->base->sym) {
|
||||
struct symbol *parent = NULL;
|
||||
|
||||
/*
|
||||
* The static twin might have been in a .part. symbol in the
|
||||
* original object that got removed in the patched object.
|
||||
*/
|
||||
parent = kpatch_get_correlated_parent(sec->base->sym);
|
||||
parent = kpatch_get_correlated_parent(relasec->base->sym);
|
||||
if (parent)
|
||||
sec = parent->sec->rela;
|
||||
relasec = parent->sec->rela;
|
||||
|
||||
}
|
||||
|
||||
if (!sec->twin)
|
||||
if (!relasec->twin)
|
||||
return NULL;
|
||||
|
||||
res = kpatch_find_uncorrelated_rela(sec->twin, sym);
|
||||
res = kpatch_find_uncorrelated_rela(relasec->twin, sym);
|
||||
if (res != NULL)
|
||||
return res;
|
||||
|
||||
/* Look if reference might have moved to child functions' sections */
|
||||
if (sec->twin->base->sym)
|
||||
return kpatch_find_static_twin_in_children(sec->twin->base->sym,
|
||||
if (relasec->twin->base->sym)
|
||||
return kpatch_find_static_twin_in_children(relasec->twin->base->sym,
|
||||
sym);
|
||||
|
||||
return NULL;
|
||||
|
@ -1248,18 +1220,19 @@ static bool kpatch_is_normal_static_local(struct symbol *sym)
|
|||
return true;
|
||||
}
|
||||
|
||||
static struct rela *kpatch_find_static_twin_ref(struct section *rela_sec, struct symbol *sym)
|
||||
static struct rela *kpatch_find_static_twin_ref(struct section *relasec,
|
||||
struct symbol *sym)
|
||||
{
|
||||
struct rela *rela;
|
||||
|
||||
list_for_each_entry(rela, &rela_sec->relas, list) {
|
||||
list_for_each_entry(rela, &relasec->relas, list) {
|
||||
if (rela->sym == sym->twin)
|
||||
return rela;
|
||||
}
|
||||
|
||||
/* Reference to static variable might have moved to child function section */
|
||||
if (rela_sec->base->sym) {
|
||||
struct symbol *parent = rela_sec->base->sym;
|
||||
if (relasec->base->sym) {
|
||||
struct symbol *parent = relasec->base->sym;
|
||||
struct symbol *child;
|
||||
|
||||
list_for_each_entry(child, &parent->children, subfunction_node) {
|
||||
|
@ -1305,7 +1278,7 @@ static void kpatch_correlate_static_local_variables(struct kpatch_elf *orig,
|
|||
struct kpatch_elf *patched)
|
||||
{
|
||||
struct symbol *sym, *patched_sym;
|
||||
struct section *sec;
|
||||
struct section *relasec;
|
||||
struct rela *rela;
|
||||
int bundled, patched_bundled;
|
||||
|
||||
|
@ -1338,14 +1311,14 @@ static void kpatch_correlate_static_local_variables(struct kpatch_elf *orig,
|
|||
* Do the correlations: for each section reference to a static local,
|
||||
* look for a corresponding reference in the section's twin.
|
||||
*/
|
||||
list_for_each_entry(sec, &orig->sections, list) {
|
||||
list_for_each_entry(relasec, &orig->sections, list) {
|
||||
|
||||
if (!is_rela_section(sec) ||
|
||||
is_debug_section(sec) ||
|
||||
!strcmp(sec->name, ".rela.toc"))
|
||||
if (!is_rela_section(relasec) ||
|
||||
is_debug_section(relasec) ||
|
||||
!strcmp(relasec->name, ".rela.toc"))
|
||||
continue;
|
||||
|
||||
list_for_each_entry(rela, &sec->relas, list) {
|
||||
list_for_each_entry(rela, &relasec->relas, list) {
|
||||
|
||||
if (!toc_rela(rela))
|
||||
continue; /* skip toc constants */
|
||||
|
@ -1358,7 +1331,7 @@ static void kpatch_correlate_static_local_variables(struct kpatch_elf *orig,
|
|||
continue;
|
||||
|
||||
bundled = sym == sym->sec->sym;
|
||||
if (bundled && sym->sec == sec->base) {
|
||||
if (bundled && sym->sec == relasec->base) {
|
||||
/*
|
||||
* A rare case where a static local data
|
||||
* structure references itself. There's no
|
||||
|
@ -1371,11 +1344,11 @@ static void kpatch_correlate_static_local_variables(struct kpatch_elf *orig,
|
|||
continue;
|
||||
}
|
||||
|
||||
patched_sym = kpatch_find_static_twin(sec, sym);
|
||||
patched_sym = kpatch_find_static_twin(relasec, sym);
|
||||
if (!patched_sym)
|
||||
DIFF_FATAL("reference to static local variable %s in %s was removed",
|
||||
sym->name,
|
||||
kpatch_section_function_name(sec));
|
||||
kpatch_section_function_name(relasec));
|
||||
|
||||
patched_bundled = patched_sym == patched_sym->sec->sym;
|
||||
if (bundled != patched_bundled)
|
||||
|
@ -1401,23 +1374,23 @@ static void kpatch_correlate_static_local_variables(struct kpatch_elf *orig,
|
|||
* corresponding reference in the patched object (because a static
|
||||
* local can be referenced by more than one section).
|
||||
*/
|
||||
list_for_each_entry(sec, &orig->sections, list) {
|
||||
list_for_each_entry(relasec, &orig->sections, list) {
|
||||
|
||||
if (!is_rela_section(sec) ||
|
||||
is_debug_section(sec))
|
||||
if (!is_rela_section(relasec) ||
|
||||
is_debug_section(relasec))
|
||||
continue;
|
||||
|
||||
list_for_each_entry(rela, &sec->relas, list) {
|
||||
struct section *target_sec = sec;
|
||||
list_for_each_entry(rela, &relasec->relas, list) {
|
||||
struct section *target_sec = relasec;
|
||||
|
||||
sym = rela->sym;
|
||||
if (!kpatch_is_normal_static_local(sym))
|
||||
continue;
|
||||
|
||||
if (!sec->twin && sec->base->sym) {
|
||||
if (!relasec->twin && relasec->base->sym) {
|
||||
struct symbol *parent = NULL;
|
||||
|
||||
parent = kpatch_get_correlated_parent(sec->base->sym);
|
||||
parent = kpatch_get_correlated_parent(relasec->base->sym);
|
||||
if (parent)
|
||||
target_sec = parent->sec->rela;
|
||||
}
|
||||
|
@ -1439,13 +1412,13 @@ static void kpatch_correlate_static_local_variables(struct kpatch_elf *orig,
|
|||
* static locals to see if we need to print any warnings about new
|
||||
* variables.
|
||||
*/
|
||||
list_for_each_entry(sec, &patched->sections, list) {
|
||||
list_for_each_entry(relasec, &patched->sections, list) {
|
||||
|
||||
if (!is_rela_section(sec) ||
|
||||
is_debug_section(sec))
|
||||
if (!is_rela_section(relasec) ||
|
||||
is_debug_section(relasec))
|
||||
continue;
|
||||
|
||||
list_for_each_entry(rela, &sec->relas, list) {
|
||||
list_for_each_entry(rela, &relasec->relas, list) {
|
||||
|
||||
sym = rela->sym;
|
||||
if (!kpatch_is_normal_static_local(sym))
|
||||
|
@ -1456,7 +1429,7 @@ static void kpatch_correlate_static_local_variables(struct kpatch_elf *orig,
|
|||
|
||||
log_normal("WARNING: unable to correlate static local variable %s used by %s, assuming variable is new\n",
|
||||
sym->name,
|
||||
kpatch_section_function_name(sec));
|
||||
kpatch_section_function_name(relasec));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1476,30 +1449,6 @@ static void kpatch_compare_correlated_elements(struct kpatch_elf *kelf)
|
|||
kpatch_compare_symbols(&kelf->symbols);
|
||||
}
|
||||
|
||||
static void rela_insn(const struct section *sec, const struct rela *rela,
|
||||
struct insn *insn)
|
||||
{
|
||||
unsigned long insn_addr, start, end, rela_addr;
|
||||
|
||||
start = (unsigned long)sec->base->data->d_buf;
|
||||
end = start + sec->base->sh.sh_size;
|
||||
|
||||
if (end <= start)
|
||||
ERROR("bad section size");
|
||||
|
||||
rela_addr = start + rela->offset;
|
||||
for (insn_addr = start; insn_addr < end; insn_addr += insn->length) {
|
||||
insn_init(insn, (void *)insn_addr, 1);
|
||||
insn_get_length(insn);
|
||||
if (!insn->length)
|
||||
ERROR("can't decode instruction in section %s at offset 0x%lx",
|
||||
sec->base->name, insn_addr);
|
||||
if (rela_addr >= insn_addr &&
|
||||
rela_addr < insn_addr + insn->length)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_callback_section(struct section *sec) {
|
||||
|
||||
static char *callback_sections[] = {
|
||||
|
@ -1531,23 +1480,34 @@ static bool is_callback_section(struct section *sec) {
|
|||
*/
|
||||
static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
|
||||
{
|
||||
struct section *sec;
|
||||
struct section *relasec;
|
||||
struct rela *rela;
|
||||
struct symbol *sym;
|
||||
unsigned int add_off;
|
||||
long target_off;
|
||||
bool found = false;
|
||||
|
||||
log_debug("\n");
|
||||
|
||||
list_for_each_entry(sec, &kelf->sections, list) {
|
||||
if (!is_rela_section(sec) ||
|
||||
is_debug_section(sec))
|
||||
list_for_each_entry(relasec, &kelf->sections, list) {
|
||||
if (!is_rela_section(relasec) || is_debug_section(relasec))
|
||||
continue;
|
||||
|
||||
list_for_each_entry(rela, &sec->relas, list) {
|
||||
list_for_each_entry(rela, &relasec->relas, list) {
|
||||
|
||||
if (rela->sym->type != STT_SECTION || !rela->sym->sec)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* These sections don't have symbols associated with
|
||||
* them:
|
||||
*/
|
||||
if (!strcmp(rela->sym->name, ".toc") ||
|
||||
!strcmp(rela->sym->name, ".fixup") ||
|
||||
!strcmp(rela->sym->name, ".altinstr_replacement") ||
|
||||
!strcmp(rela->sym->name, ".altinstr_aux") ||
|
||||
!strcmp(rela->sym->name, ".text..refcount"))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Replace references to bundled sections with their
|
||||
* symbols.
|
||||
|
@ -1569,27 +1529,7 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
|
|||
continue;
|
||||
}
|
||||
|
||||
switch(kelf->arch) {
|
||||
case PPC64:
|
||||
add_off = 0;
|
||||
break;
|
||||
case X86_64:
|
||||
if (rela->type == R_X86_64_PC32 ||
|
||||
rela->type == R_X86_64_PLT32) {
|
||||
struct insn insn;
|
||||
rela_insn(sec, rela, &insn);
|
||||
add_off = (unsigned int)((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;
|
||||
break;
|
||||
default:
|
||||
ERROR("unsupported arch");
|
||||
}
|
||||
target_off = rela_target_offset(kelf, relasec, rela);
|
||||
|
||||
/*
|
||||
* Attempt to replace references to unbundled sections
|
||||
|
@ -1605,7 +1545,8 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
|
|||
start = sym->sym.st_value;
|
||||
end = sym->sym.st_value + sym->sym.st_size;
|
||||
|
||||
if (!is_text_section(sym->sec) &&
|
||||
if (is_text_section(relasec->base) &&
|
||||
!is_text_section(sym->sec) &&
|
||||
rela->type == R_X86_64_32S &&
|
||||
rela->addend == (long)sym->sec->sh.sh_size &&
|
||||
end == (long)sym->sec->sh.sh_size) {
|
||||
|
@ -1641,20 +1582,32 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
|
|||
* &(var1+sizeof(var1)) will always
|
||||
* be the same as &var2.
|
||||
*/
|
||||
} else if (target_off == start && target_off == end) {
|
||||
|
||||
} else if (rela->addend + add_off < start ||
|
||||
rela->addend + add_off >= end)
|
||||
/*
|
||||
* Allow replacement for references to
|
||||
* empty symbols.
|
||||
*/
|
||||
|
||||
} else if (target_off < start || target_off >= end)
|
||||
continue;
|
||||
|
||||
log_debug("%s: replacing %s+%ld reference with %s+%ld\n",
|
||||
sec->name,
|
||||
relasec->name,
|
||||
rela->sym->name, rela->addend,
|
||||
sym->name, rela->addend - start);
|
||||
found = true;
|
||||
|
||||
rela->sym = sym;
|
||||
rela->addend -= start;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found && !is_string_literal_section(rela->sym->sec) &&
|
||||
strncmp(rela->sym->name, ".rodata", 7)) {
|
||||
ERROR("%s+0x%x: can't find replacement symbol for %s+%ld reference",
|
||||
relasec->base->name, rela->offset, rela->sym->name, rela->addend);
|
||||
}
|
||||
}
|
||||
}
|
||||
log_debug("\n");
|
||||
|
@ -1772,12 +1725,6 @@ static void kpatch_include_symbol(struct symbol *sym)
|
|||
kpatch_include_section(sym->sec);
|
||||
}
|
||||
|
||||
static bool is_string_literal_section(struct section *sec)
|
||||
{
|
||||
return !strncmp(sec->name, ".rodata.", 8) &&
|
||||
strstr(sec->name, ".str1.");
|
||||
}
|
||||
|
||||
static void kpatch_include_standard_elements(struct kpatch_elf *kelf)
|
||||
{
|
||||
struct section *sec;
|
||||
|
@ -1824,12 +1771,12 @@ static void kpatch_include_standard_elements(struct kpatch_elf *kelf)
|
|||
list_entry(kelf->symbols.next, struct symbol, list)->include = 1;
|
||||
}
|
||||
|
||||
static int kpatch_include_callback_elements(struct kpatch_elf *kelf)
|
||||
static bool kpatch_include_callback_elements(struct kpatch_elf *kelf)
|
||||
{
|
||||
struct section *sec;
|
||||
struct symbol *sym;
|
||||
struct rela *rela;
|
||||
int found = 0;
|
||||
bool found = false;
|
||||
|
||||
/* include load/unload sections */
|
||||
list_for_each_entry(sec, &kelf->sections, list) {
|
||||
|
@ -1837,7 +1784,7 @@ static int kpatch_include_callback_elements(struct kpatch_elf *kelf)
|
|||
continue;
|
||||
|
||||
sec->include = 1;
|
||||
found = 1;
|
||||
found = true;
|
||||
if (is_rela_section(sec)) {
|
||||
/* include callback dependencies */
|
||||
rela = list_entry(sec->relas.next, struct rela, list);
|
||||
|
@ -1941,7 +1888,7 @@ static void kpatch_print_changes(struct kpatch_elf *kelf)
|
|||
|
||||
static void kpatch_migrate_symbols(struct list_head *src,
|
||||
struct list_head *dst,
|
||||
int (*select)(struct symbol *))
|
||||
bool (*select)(struct symbol *))
|
||||
{
|
||||
struct symbol *sym, *safe;
|
||||
|
||||
|
@ -2164,17 +2111,17 @@ static int fixup_barrier_nospec_group_size(struct kpatch_elf *kelf, int offset)
|
|||
*/
|
||||
static int fixup_group_size(struct kpatch_elf *kelf, int offset)
|
||||
{
|
||||
struct section *sec;
|
||||
struct section *relasec;
|
||||
struct rela *rela;
|
||||
int found;
|
||||
|
||||
sec = find_section_by_name(&kelf->sections, ".rela__ex_table");
|
||||
if (!sec)
|
||||
relasec = find_section_by_name(&kelf->sections, ".rela__ex_table");
|
||||
if (!relasec)
|
||||
ERROR("missing .rela__ex_table section");
|
||||
|
||||
/* find beginning of this group */
|
||||
found = 0;
|
||||
list_for_each_entry(rela, &sec->relas, list) {
|
||||
list_for_each_entry(rela, &relasec->relas, list) {
|
||||
if (!strcmp(rela->sym->name, ".fixup") &&
|
||||
rela->addend == offset) {
|
||||
found = 1;
|
||||
|
@ -2187,7 +2134,7 @@ static int fixup_group_size(struct kpatch_elf *kelf, int offset)
|
|||
|
||||
/* find beginning of next group */
|
||||
found = 0;
|
||||
list_for_each_entry_continue(rela, &sec->relas, list) {
|
||||
list_for_each_entry_continue(rela, &relasec->relas, list) {
|
||||
if (!strcmp(rela->sym->name, ".fixup") &&
|
||||
rela->addend > offset) {
|
||||
found = 1;
|
||||
|
@ -2287,7 +2234,7 @@ static struct special_section special_sections[] = {
|
|||
};
|
||||
|
||||
static bool should_keep_jump_label(struct lookup_table *lookup,
|
||||
struct section *sec,
|
||||
struct section *relasec,
|
||||
unsigned int group_offset,
|
||||
unsigned int group_size,
|
||||
int *jump_labels_found)
|
||||
|
@ -2302,7 +2249,7 @@ static bool should_keep_jump_label(struct lookup_table *lookup,
|
|||
* struct. It has three fields: code, target, and key. Each field has
|
||||
* a relocation associated with it.
|
||||
*/
|
||||
list_for_each_entry(rela, &sec->relas, list) {
|
||||
list_for_each_entry(rela, &relasec->relas, list) {
|
||||
if (rela->offset >= group_offset &&
|
||||
rela->offset < group_offset + group_size) {
|
||||
if (i == 0)
|
||||
|
@ -2389,29 +2336,29 @@ static bool should_keep_jump_label(struct lookup_table *lookup,
|
|||
}
|
||||
|
||||
static bool should_keep_rela_group(struct lookup_table *lookup,
|
||||
struct section *sec, unsigned int offset,
|
||||
struct section *relasec, unsigned int offset,
|
||||
unsigned int size, int *jump_labels_found)
|
||||
{
|
||||
struct rela *rela;
|
||||
bool found = false;
|
||||
|
||||
/* check if any relas in the group reference any changed functions */
|
||||
list_for_each_entry(rela, &sec->relas, list) {
|
||||
list_for_each_entry(rela, &relasec->relas, list) {
|
||||
if (rela->offset >= offset &&
|
||||
rela->offset < offset + size &&
|
||||
rela->sym->type == STT_FUNC &&
|
||||
rela->sym->sec->include) {
|
||||
found = true;
|
||||
log_debug("new/changed symbol %s found in special section %s\n",
|
||||
rela->sym->name, sec->name);
|
||||
rela->sym->name, relasec->name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return false;
|
||||
|
||||
if (!strcmp(sec->name, ".rela__jump_table"))
|
||||
return should_keep_jump_label(lookup, sec, offset, size,
|
||||
if (!strcmp(relasec->name, ".rela__jump_table"))
|
||||
return should_keep_jump_label(lookup, relasec, offset, size,
|
||||
jump_labels_found);
|
||||
|
||||
return true;
|
||||
|
@ -2428,13 +2375,13 @@ static void kpatch_update_ex_table_addend(struct kpatch_elf *kelf,
|
|||
int group_size)
|
||||
{
|
||||
struct rela *rela;
|
||||
struct section *sec;
|
||||
struct section *relasec;
|
||||
|
||||
sec = find_section_by_name(&kelf->sections, ".rela__ex_table");
|
||||
if (!sec)
|
||||
relasec = find_section_by_name(&kelf->sections, ".rela__ex_table");
|
||||
if (!relasec)
|
||||
ERROR("missing .rela__ex_table section");
|
||||
|
||||
list_for_each_entry(rela, &sec->relas, list) {
|
||||
list_for_each_entry(rela, &relasec->relas, list) {
|
||||
if (!strcmp(rela->sym->name, ".fixup") &&
|
||||
rela->addend >= src_offset &&
|
||||
rela->addend < src_offset + group_size)
|
||||
|
@ -2445,7 +2392,7 @@ static void kpatch_update_ex_table_addend(struct kpatch_elf *kelf,
|
|||
static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
|
||||
struct lookup_table *lookup,
|
||||
struct special_section *special,
|
||||
struct section *sec)
|
||||
struct section *relasec)
|
||||
{
|
||||
struct rela *rela, *safe;
|
||||
char *src, *dest;
|
||||
|
@ -2454,15 +2401,15 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
|
|||
|
||||
LIST_HEAD(newrelas);
|
||||
|
||||
src = sec->base->data->d_buf;
|
||||
src = relasec->base->data->d_buf;
|
||||
/* alloc buffer for new base section */
|
||||
dest = malloc(sec->base->sh.sh_size);
|
||||
dest = malloc(relasec->base->sh.sh_size);
|
||||
if (!dest)
|
||||
ERROR("malloc");
|
||||
|
||||
/* Restore the stashed r_addend from kpatch_update_ex_table_addend. */
|
||||
if (!strcmp(special->name, "__ex_table")) {
|
||||
list_for_each_entry(rela, &sec->relas, list) {
|
||||
list_for_each_entry(rela, &relasec->relas, list) {
|
||||
if (!strcmp(rela->sym->name, ".fixup"))
|
||||
rela->addend = rela->rela.r_addend;
|
||||
}
|
||||
|
@ -2470,7 +2417,7 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
|
|||
|
||||
src_offset = 0;
|
||||
dest_offset = 0;
|
||||
for ( ; src_offset < sec->base->sh.sh_size; src_offset += group_size) {
|
||||
for ( ; src_offset < relasec->base->sh.sh_size; src_offset += group_size) {
|
||||
|
||||
group_size = special->group_size(kelf, src_offset);
|
||||
|
||||
|
@ -2482,10 +2429,10 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
|
|||
* contains the data but doesn't go past the end of the
|
||||
* section.
|
||||
*/
|
||||
if (src_offset + group_size > sec->base->sh.sh_size)
|
||||
group_size = (unsigned int)(sec->base->sh.sh_size - src_offset);
|
||||
if (src_offset + group_size > relasec->base->sh.sh_size)
|
||||
group_size = (unsigned int)(relasec->base->sh.sh_size - src_offset);
|
||||
|
||||
if (!should_keep_rela_group(lookup, sec, src_offset, group_size,
|
||||
if (!should_keep_rela_group(lookup, relasec, src_offset, group_size,
|
||||
&jump_labels_found))
|
||||
continue;
|
||||
|
||||
|
@ -2494,7 +2441,7 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
|
|||
* aren't sorted (e.g. .rela.fixup), so go through the entire
|
||||
* rela list each time.
|
||||
*/
|
||||
list_for_each_entry_safe(rela, safe, &sec->relas, list) {
|
||||
list_for_each_entry_safe(rela, safe, &relasec->relas, list) {
|
||||
if (rela->offset >= src_offset &&
|
||||
rela->offset < src_offset + group_size) {
|
||||
/* copy rela entry */
|
||||
|
@ -2525,21 +2472,21 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
|
|||
|
||||
if (!dest_offset) {
|
||||
/* no changed or global functions referenced */
|
||||
sec->status = sec->base->status = SAME;
|
||||
sec->include = sec->base->include = 0;
|
||||
relasec->status = relasec->base->status = SAME;
|
||||
relasec->include = relasec->base->include = 0;
|
||||
free(dest);
|
||||
return;
|
||||
}
|
||||
|
||||
/* overwrite with new relas list */
|
||||
list_replace(&newrelas, &sec->relas);
|
||||
list_replace(&newrelas, &relasec->relas);
|
||||
|
||||
/* include both rela and base sections */
|
||||
sec->include = 1;
|
||||
sec->base->include = 1;
|
||||
relasec->include = 1;
|
||||
relasec->base->include = 1;
|
||||
/* include secsym so .kpatch.arch relas can point to section symbols */
|
||||
if (sec->base->secsym)
|
||||
sec->base->secsym->include = 1;
|
||||
if (relasec->base->secsym)
|
||||
relasec->base->secsym->include = 1;
|
||||
|
||||
/*
|
||||
* Update text section data buf and size.
|
||||
|
@ -2547,8 +2494,8 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
|
|||
* The rela section's data buf and size will be regenerated in
|
||||
* kpatch_rebuild_rela_section_data().
|
||||
*/
|
||||
sec->base->data->d_buf = dest;
|
||||
sec->base->data->d_size = dest_offset;
|
||||
relasec->base->data->d_buf = dest;
|
||||
relasec->base->data->d_size = dest_offset;
|
||||
}
|
||||
|
||||
#define ORC_IP_PTR_SIZE 4
|
||||
|
@ -2645,19 +2592,33 @@ next:
|
|||
static void kpatch_check_relocations(struct kpatch_elf *kelf)
|
||||
{
|
||||
struct rela *rela;
|
||||
struct section *sec;
|
||||
Elf_Data *sdata;
|
||||
struct section *relasec;
|
||||
long sec_size;
|
||||
long sec_off;
|
||||
|
||||
list_for_each_entry(sec, &kelf->sections, list) {
|
||||
if (!is_rela_section(sec))
|
||||
list_for_each_entry(relasec, &kelf->sections, list) {
|
||||
if (!is_rela_section(relasec))
|
||||
continue;
|
||||
list_for_each_entry(rela, &sec->relas, list) {
|
||||
if (rela->sym->sec) {
|
||||
sdata = rela->sym->sec->data;
|
||||
if ((long)rela->sym->sym.st_value + rela->addend > (long)sdata->d_size) {
|
||||
ERROR("out-of-range relocation %s+%lx in %s", rela->sym->name,
|
||||
rela->addend, sec->name);
|
||||
}
|
||||
list_for_each_entry(rela, &relasec->relas, list) {
|
||||
if (!rela->sym->sec)
|
||||
continue;
|
||||
|
||||
sec_size = rela->sym->sec->data->d_size;
|
||||
sec_off = (long)rela->sym->sym.st_value +
|
||||
rela_target_offset(kelf, relasec, rela);
|
||||
|
||||
/*
|
||||
* This check isn't perfect: we still allow relocations
|
||||
* to the end of a section. There are real instances
|
||||
* of that, including ORC entries, LOCKDEP=n
|
||||
* zero-length '__key' passing, and the loop edge case
|
||||
* described in kpatch_replace_sections_syms(). For
|
||||
* now, just allow all such cases.
|
||||
*/
|
||||
if (sec_off < 0 || sec_off > sec_size) {
|
||||
ERROR("%s+0x%x: out-of-range relocation %s+%lx",
|
||||
relasec->base->name, rela->offset,
|
||||
rela->sym->name, rela->addend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3056,7 +3017,7 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
|||
|
||||
}
|
||||
|
||||
static int kpatch_is_core_module_symbol(char *name)
|
||||
static bool kpatch_is_core_module_symbol(char *name)
|
||||
{
|
||||
return (!strcmp(name, "kpatch_shadow_alloc") ||
|
||||
!strcmp(name, "kpatch_shadow_free") ||
|
||||
|
@ -3076,11 +3037,11 @@ static int function_ptr_rela(const struct rela *rela)
|
|||
}
|
||||
|
||||
static bool need_dynrela(struct kpatch_elf *kelf, struct lookup_table *table,
|
||||
struct section *sec, const struct rela *rela)
|
||||
struct section *relasec, const struct rela *rela)
|
||||
{
|
||||
struct lookup_result symbol;
|
||||
|
||||
if (is_debug_section(sec))
|
||||
if (is_debug_section(relasec))
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
@ -3092,7 +3053,7 @@ static bool need_dynrela(struct kpatch_elf *kelf, struct lookup_table *table,
|
|||
return false;
|
||||
|
||||
/* v5.13+ kernels use relative jump labels */
|
||||
if (rela->type == R_PPC64_REL64 && strcmp(sec->name, ".rela__jump_table"))
|
||||
if (rela->type == R_PPC64_REL64 && strcmp(relasec->name, ".rela__jump_table"))
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
@ -3249,7 +3210,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|||
char *pmod_name)
|
||||
{
|
||||
int nr, index;
|
||||
struct section *sec, *ksym_sec, *krela_sec;
|
||||
struct section *relasec, *ksym_sec, *krela_sec;
|
||||
struct rela *rela, *rela2, *safe;
|
||||
struct symbol *strsym, *ksym_sec_sym;
|
||||
struct kpatch_symbol *ksyms;
|
||||
|
@ -3261,12 +3222,12 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|||
|
||||
/* count rela entries that need to be dynamic */
|
||||
nr = 0;
|
||||
list_for_each_entry(sec, &kelf->sections, list) {
|
||||
if (!is_rela_section(sec))
|
||||
list_for_each_entry(relasec, &kelf->sections, list) {
|
||||
if (!is_rela_section(relasec))
|
||||
continue;
|
||||
if (!strcmp(sec->name, ".rela.kpatch.funcs"))
|
||||
if (!strcmp(relasec->name, ".rela.kpatch.funcs"))
|
||||
continue;
|
||||
list_for_each_entry(rela, &sec->relas, list) {
|
||||
list_for_each_entry(rela, &relasec->relas, list) {
|
||||
|
||||
/* upper bound on number of kpatch relas and symbols */
|
||||
nr++;
|
||||
|
@ -3282,7 +3243,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|||
* internal symbol function pointer check which is done
|
||||
* via .toc indirection in need_dynrela().
|
||||
*/
|
||||
if (need_dynrela(kelf, table, sec, rela))
|
||||
if (need_dynrela(kelf, table, relasec, rela))
|
||||
toc_rela(rela)->need_dynrela = 1;
|
||||
}
|
||||
}
|
||||
|
@ -3310,12 +3271,12 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|||
|
||||
/* populate sections */
|
||||
index = 0;
|
||||
list_for_each_entry(sec, &kelf->sections, list) {
|
||||
if (!is_rela_section(sec))
|
||||
list_for_each_entry(relasec, &kelf->sections, list) {
|
||||
if (!is_rela_section(relasec))
|
||||
continue;
|
||||
if (!strcmp(sec->name, ".rela.kpatch.funcs") ||
|
||||
!strcmp(sec->name, ".rela.kpatch.relocations") ||
|
||||
!strcmp(sec->name, ".rela.kpatch.symbols"))
|
||||
if (!strcmp(relasec->name, ".rela.kpatch.funcs") ||
|
||||
!strcmp(relasec->name, ".rela.kpatch.relocations") ||
|
||||
!strcmp(relasec->name, ".rela.kpatch.symbols"))
|
||||
continue;
|
||||
|
||||
special = false;
|
||||
|
@ -3323,11 +3284,11 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|||
if ((s->arch & kelf->arch) == 0)
|
||||
continue;
|
||||
|
||||
if (!strcmp(sec->base->name, s->name))
|
||||
if (!strcmp(relasec->base->name, s->name))
|
||||
special = true;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(rela, safe, &sec->relas, list) {
|
||||
list_for_each_entry_safe(rela, safe, &relasec->relas, list) {
|
||||
if (!rela->need_dynrela) {
|
||||
rela->sym->strip = SYMBOL_USED;
|
||||
continue;
|
||||
|
@ -3348,7 +3309,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|||
*/
|
||||
if (!KLP_ARCH && !vmlinux && special)
|
||||
ERROR("unsupported dynrela reference to symbol '%s' in module-specific special section '%s'",
|
||||
rela->sym->name, sec->base->name);
|
||||
rela->sym->name, relasec->base->name);
|
||||
|
||||
if (!lookup_symbol(table, rela->sym, &symbol))
|
||||
ERROR("can't find symbol '%s' in symbol table",
|
||||
|
@ -3394,11 +3355,11 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|||
|
||||
/* add rela to fill in krelas[index].dest field */
|
||||
ALLOC_LINK(rela2, &krela_sec->rela->relas);
|
||||
if (sec->base->secsym)
|
||||
rela2->sym = sec->base->secsym;
|
||||
if (relasec->base->secsym)
|
||||
rela2->sym = relasec->base->secsym;
|
||||
else
|
||||
ERROR("can't create dynrela for section %s (symbol %s): no bundled or section symbol",
|
||||
sec->name, rela->sym->name);
|
||||
relasec->name, rela->sym->name);
|
||||
|
||||
rela2->type = absolute_rela_type(kelf);
|
||||
rela2->addend = rela->offset;
|
||||
|
@ -3706,7 +3667,7 @@ static void kpatch_build_strings_section_data(struct kpatch_elf *kelf)
|
|||
static void kpatch_no_sibling_calls_ppc64le(struct kpatch_elf *kelf)
|
||||
{
|
||||
struct symbol *sym;
|
||||
unsigned int insn;
|
||||
unsigned char *insn;
|
||||
unsigned int offset;
|
||||
|
||||
if (kelf->arch != PPC64)
|
||||
|
@ -3718,7 +3679,7 @@ static void kpatch_no_sibling_calls_ppc64le(struct kpatch_elf *kelf)
|
|||
|
||||
for (offset = 0; offset < sym->sec->data->d_size; offset += 4) {
|
||||
|
||||
insn = *(unsigned int *)(sym->sec->data->d_buf + offset);
|
||||
insn = sym->sec->data->d_buf + offset;
|
||||
|
||||
/*
|
||||
* The instruction 0x48000000 can be assumed to be a
|
||||
|
@ -3733,7 +3694,8 @@ static void kpatch_no_sibling_calls_ppc64le(struct kpatch_elf *kelf)
|
|||
* there's a REL24 relocation for the address which
|
||||
* will be written by the linker or the kernel.
|
||||
*/
|
||||
if (insn != 0x48000000)
|
||||
if (insn[3] != 0x48 || insn[2] != 0x00 ||
|
||||
insn[1] != 0x00 || insn[0] != 0x00)
|
||||
continue;
|
||||
|
||||
/* Make sure it's not a branch-to-self: */
|
||||
|
@ -3833,7 +3795,7 @@ int main(int argc, char *argv[])
|
|||
struct arguments arguments;
|
||||
int num_changed, callbacks_exist, new_globals_exist;
|
||||
struct lookup_table *lookup;
|
||||
struct section *sec, *symtab;
|
||||
struct section *relasec, *symtab;
|
||||
char *orig_obj, *patched_obj, *parent_name;
|
||||
char *parent_symtab, *mod_symvers, *patch_name, *output_obj;
|
||||
|
||||
|
@ -3958,12 +3920,12 @@ int main(int argc, char *argv[])
|
|||
if (!symtab)
|
||||
ERROR("missing .symtab section");
|
||||
|
||||
list_for_each_entry(sec, &kelf_out->sections, list) {
|
||||
if (!is_rela_section(sec))
|
||||
list_for_each_entry(relasec, &kelf_out->sections, list) {
|
||||
if (!is_rela_section(relasec))
|
||||
continue;
|
||||
sec->sh.sh_link = symtab->index;
|
||||
sec->sh.sh_info = sec->base->index;
|
||||
kpatch_rebuild_rela_section_data(sec);
|
||||
relasec->sh.sh_link = symtab->index;
|
||||
relasec->sh.sh_info = relasec->base->index;
|
||||
kpatch_rebuild_rela_section_data(relasec);
|
||||
}
|
||||
kpatch_check_relocations(kelf_out);
|
||||
|
||||
|
|
|
@ -122,37 +122,37 @@ static struct section *find_or_add_klp_relasec(struct kpatch_elf *kelf,
|
|||
struct section *base,
|
||||
char *objname)
|
||||
{
|
||||
struct section *sec;
|
||||
struct section *relasec;
|
||||
char buf[256];
|
||||
|
||||
/* .klp.rela.objname.secname */
|
||||
snprintf(buf, 256, KLP_RELASEC_PREFIX "%s.%s", objname, base->name);
|
||||
|
||||
list_for_each_entry(sec, &kelf->sections, list) {
|
||||
if (!strcmp(sec->name, buf))
|
||||
return sec;
|
||||
list_for_each_entry(relasec, &kelf->sections, list) {
|
||||
if (!strcmp(relasec->name, buf))
|
||||
return relasec;
|
||||
}
|
||||
|
||||
ALLOC_LINK(sec, &kelf->sections);
|
||||
sec->name = strdup(buf);
|
||||
if (!sec->name)
|
||||
ALLOC_LINK(relasec, &kelf->sections);
|
||||
relasec->name = strdup(buf);
|
||||
if (!relasec->name)
|
||||
ERROR("strdup");
|
||||
sec->base = base;
|
||||
relasec->base = base;
|
||||
|
||||
INIT_LIST_HEAD(&sec->relas);
|
||||
INIT_LIST_HEAD(&relasec->relas);
|
||||
|
||||
sec->data = malloc(sizeof(*sec->data));
|
||||
if (!sec->data)
|
||||
relasec->data = malloc(sizeof(*relasec->data));
|
||||
if (!relasec->data)
|
||||
ERROR("malloc");
|
||||
sec->data->d_type = ELF_T_RELA;
|
||||
relasec->data->d_type = ELF_T_RELA;
|
||||
|
||||
/* sh_info and sh_link are set when rebuilding rela sections */
|
||||
sec->sh.sh_type = SHT_RELA;
|
||||
sec->sh.sh_entsize = sizeof(GElf_Rela);
|
||||
sec->sh.sh_addralign = 8;
|
||||
sec->sh.sh_flags = SHF_RELA_LIVEPATCH | SHF_INFO_LINK | SHF_ALLOC;
|
||||
relasec->sh.sh_type = SHT_RELA;
|
||||
relasec->sh.sh_entsize = sizeof(GElf_Rela);
|
||||
relasec->sh.sh_addralign = 8;
|
||||
relasec->sh.sh_flags = SHF_RELA_LIVEPATCH | SHF_INFO_LINK | SHF_ALLOC;
|
||||
|
||||
return sec;
|
||||
return relasec;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -429,7 +429,7 @@ static struct argp argp = { options, parse_opt, args_doc, 0 };
|
|||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct kpatch_elf *kelf;
|
||||
struct section *symtab, *sec;
|
||||
struct section *symtab, *relasec;
|
||||
struct section *ksymsec, *krelasec, *strsec;
|
||||
struct arguments arguments;
|
||||
char *strings;
|
||||
|
@ -493,12 +493,12 @@ int main(int argc, char *argv[])
|
|||
if (!symtab)
|
||||
ERROR("missing .symtab section");
|
||||
|
||||
list_for_each_entry(sec, &kelf->sections, list) {
|
||||
if (!is_rela_section(sec))
|
||||
list_for_each_entry(relasec, &kelf->sections, list) {
|
||||
if (!is_rela_section(relasec))
|
||||
continue;
|
||||
sec->sh.sh_link = symtab->index;
|
||||
sec->sh.sh_info = sec->base->index;
|
||||
kpatch_rebuild_rela_section_data(sec);
|
||||
relasec->sh.sh_link = symtab->index;
|
||||
relasec->sh.sh_info = relasec->base->index;
|
||||
kpatch_rebuild_rela_section_data(relasec);
|
||||
}
|
||||
|
||||
kpatch_create_shstrtab(kelf);
|
||||
|
|
|
@ -97,6 +97,18 @@ logger() {
|
|||
fi
|
||||
}
|
||||
|
||||
trace_on() {
|
||||
if [[ $DEBUG -eq 1 ]] || [[ $DEBUG -ge 3 ]]; then
|
||||
set -o xtrace
|
||||
fi
|
||||
}
|
||||
|
||||
trace_off() {
|
||||
if [[ $DEBUG -eq 1 ]] || [[ $DEBUG -ge 3 ]]; then
|
||||
set +o xtrace
|
||||
fi
|
||||
}
|
||||
|
||||
save_env() {
|
||||
export -p | grep -wv -e 'OLDPWD=' -e 'PWD=' > "$ENVFILE"
|
||||
}
|
||||
|
@ -619,9 +631,7 @@ if [[ ${#PATCH_LIST[@]} -eq 0 ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $DEBUG -eq 1 ]] || [[ $DEBUG -ge 3 ]]; then
|
||||
set -o xtrace
|
||||
fi
|
||||
trace_on
|
||||
|
||||
if [[ -n "$SRCRPM" ]]; then
|
||||
if [[ -n "$ARCHVERSION" ]]; then
|
||||
|
@ -836,9 +846,11 @@ fi
|
|||
|
||||
# kernel option checking
|
||||
|
||||
trace_off "reading .config"
|
||||
# Don't check external file.
|
||||
# shellcheck disable=SC1090
|
||||
source "$CONFIGFILE"
|
||||
trace_on
|
||||
|
||||
[[ "$DISTRO" = openEuler ]] && [[ -z "$CONFIG_LIVEPATCH_PER_TASK_CONSISTENCY" ]] && \
|
||||
die "openEuler kernel doesn't have 'CONFIG_LIVEPATCH_PER_TASK_CONSISTENCY' enabled"
|
||||
|
@ -988,26 +1000,28 @@ fi
|
|||
grep -q vmlinux "$KERNEL_SRCDIR/Module.symvers" || die "truncated $KERNEL_SRCDIR/Module.symvers file"
|
||||
|
||||
if [[ -n "$CONFIG_MODVERSIONS" ]]; then
|
||||
while read -ra sym_line; do
|
||||
if [[ ${#sym_line[@]} -lt 4 ]]; then
|
||||
die "Malformed ${TEMPDIR}/Module.symvers file"
|
||||
fi
|
||||
trace_off "reading Module.symvers"
|
||||
while read -ra sym_line; do
|
||||
if [[ ${#sym_line[@]} -lt 4 ]]; then
|
||||
die "Malformed ${TEMPDIR}/Module.symvers file"
|
||||
fi
|
||||
|
||||
sym=${sym_line[1]}
|
||||
sym=${sym_line[1]}
|
||||
|
||||
read -ra patched_sym_line <<< "$(grep "\s$sym\s" "$BUILDDIR/Module.symvers")"
|
||||
if [[ ${#patched_sym_line[@]} -lt 4 ]]; then
|
||||
die "Malformed symbol entry for ${sym} in ${BUILDDIR}/Module.symvers file"
|
||||
fi
|
||||
read -ra patched_sym_line <<< "$(grep "\s$sym\s" "$BUILDDIR/Module.symvers")"
|
||||
if [[ ${#patched_sym_line[@]} -lt 4 ]]; then
|
||||
die "Malformed symbol entry for ${sym} in ${BUILDDIR}/Module.symvers file"
|
||||
fi
|
||||
|
||||
# Assume that both original and patched symvers have the same format.
|
||||
# In both cases, the symbol should have the same CRC, belong to the same
|
||||
# Module/Namespace and have the same export type.
|
||||
if [[ ${#sym_line[@]} -ne ${#patched_sym_line[@]} || \
|
||||
"${sym_line[*]}" != "${patched_sym_line[*]}" ]]; then
|
||||
warn "Version disagreement for symbol ${sym}"
|
||||
fi
|
||||
done < "${TEMPDIR}/Module.symvers"
|
||||
# Assume that both original and patched symvers have the same format.
|
||||
# In both cases, the symbol should have the same CRC, belong to the same
|
||||
# Module/Namespace and have the same export type.
|
||||
if [[ ${#sym_line[@]} -ne ${#patched_sym_line[@]} || \
|
||||
"${sym_line[*]}" != "${patched_sym_line[*]}" ]]; then
|
||||
warn "Version disagreement for symbol ${sym}"
|
||||
fi
|
||||
done < "${TEMPDIR}/Module.symvers"
|
||||
trace_on
|
||||
fi
|
||||
|
||||
# Read as words, no quotes.
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "asm/insn.h"
|
||||
#include "kpatch-elf.h"
|
||||
|
||||
/*******************
|
||||
|
@ -53,18 +54,18 @@ char *status_str(enum status status)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int is_rela_section(struct section *sec)
|
||||
bool is_rela_section(struct section *sec)
|
||||
{
|
||||
return (sec->sh.sh_type == SHT_RELA);
|
||||
}
|
||||
|
||||
int is_text_section(struct section *sec)
|
||||
bool is_text_section(struct section *sec)
|
||||
{
|
||||
return (sec->sh.sh_type == SHT_PROGBITS &&
|
||||
(sec->sh.sh_flags & SHF_EXECINSTR));
|
||||
}
|
||||
|
||||
int is_debug_section(struct section *sec)
|
||||
bool is_debug_section(struct section *sec)
|
||||
{
|
||||
char *name;
|
||||
if (is_rela_section(sec))
|
||||
|
@ -164,7 +165,105 @@ int offset_of_string(struct list_head *list, char *name)
|
|||
return index;
|
||||
}
|
||||
|
||||
void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec)
|
||||
static void rela_insn(const struct section *sec, const struct rela *rela,
|
||||
struct insn *insn)
|
||||
{
|
||||
unsigned long insn_addr, start, end, rela_addr;
|
||||
|
||||
start = (unsigned long)sec->data->d_buf;
|
||||
end = start + sec->sh.sh_size;
|
||||
|
||||
if (end <= start)
|
||||
ERROR("bad section size");
|
||||
|
||||
rela_addr = start + rela->offset;
|
||||
for (insn_addr = start; insn_addr < end; insn_addr += insn->length) {
|
||||
insn_init(insn, (void *)insn_addr, 1);
|
||||
insn_get_length(insn);
|
||||
if (!insn->length)
|
||||
ERROR("can't decode instruction in section %s at offset 0x%lx",
|
||||
sec->name, insn_addr);
|
||||
if (rela_addr >= insn_addr &&
|
||||
rela_addr < insn_addr + insn->length)
|
||||
return;
|
||||
}
|
||||
|
||||
ERROR("can't find instruction for rela at %s+0x%x",
|
||||
sec->name, rela->offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the addend, adjusted for any PC-relative relocation trickery, to
|
||||
* point to the relevant symbol offset.
|
||||
*/
|
||||
long rela_target_offset(struct kpatch_elf *kelf, struct section *relasec,
|
||||
struct rela *rela)
|
||||
{
|
||||
long add_off;
|
||||
struct section *sec = relasec->base;
|
||||
|
||||
switch(kelf->arch) {
|
||||
case PPC64:
|
||||
add_off = 0;
|
||||
break;
|
||||
case X86_64:
|
||||
if (!is_text_section(sec) ||
|
||||
rela->type == R_X86_64_64 ||
|
||||
rela->type == R_X86_64_32S)
|
||||
add_off = 0;
|
||||
else if (rela->type == R_X86_64_PC32 ||
|
||||
rela->type == R_X86_64_PLT32 ||
|
||||
rela->type == R_X86_64_NONE) {
|
||||
struct insn insn;
|
||||
rela_insn(sec, rela, &insn);
|
||||
add_off = (long)insn.next_byte -
|
||||
(long)sec->data->d_buf -
|
||||
rela->offset;
|
||||
} else
|
||||
ERROR("unhandled rela type %d", rela->type);
|
||||
break;
|
||||
default:
|
||||
ERROR("unsupported arch\n");
|
||||
}
|
||||
|
||||
return rela->addend + add_off;
|
||||
}
|
||||
|
||||
unsigned int insn_length(struct kpatch_elf *kelf, void *addr)
|
||||
{
|
||||
struct insn decoded_insn;
|
||||
char *insn = addr;
|
||||
|
||||
switch(kelf->arch) {
|
||||
|
||||
case X86_64:
|
||||
insn_init(&decoded_insn, addr, 1);
|
||||
insn_get_length(&decoded_insn);
|
||||
return decoded_insn.length;
|
||||
|
||||
case PPC64:
|
||||
return 4;
|
||||
|
||||
case S390:
|
||||
switch(insn[0] >> 6) {
|
||||
case 0:
|
||||
return 2;
|
||||
case 1:
|
||||
case 2:
|
||||
return 4;
|
||||
case 3:
|
||||
return 6;
|
||||
}
|
||||
|
||||
default:
|
||||
ERROR("unsupported arch");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kpatch_create_rela_list(struct kpatch_elf *kelf,
|
||||
struct section *relasec)
|
||||
{
|
||||
int index = 0, skip = 0;
|
||||
struct rela *rela;
|
||||
|
@ -172,28 +271,28 @@ void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec)
|
|||
unsigned long rela_nr;
|
||||
|
||||
/* find matching base (text/data) section */
|
||||
sec->base = find_section_by_index(&kelf->sections, sec->sh.sh_info);
|
||||
if (!sec->base)
|
||||
ERROR("can't find base section for rela section %s", sec->name);
|
||||
relasec->base = find_section_by_index(&kelf->sections, relasec->sh.sh_info);
|
||||
if (!relasec->base)
|
||||
ERROR("can't find base section for rela section %s", relasec->name);
|
||||
|
||||
/* create reverse link from base section to this rela section */
|
||||
sec->base->rela = sec;
|
||||
relasec->base->rela = relasec;
|
||||
|
||||
rela_nr = sec->sh.sh_size / sec->sh.sh_entsize;
|
||||
rela_nr = relasec->sh.sh_size / relasec->sh.sh_entsize;
|
||||
|
||||
log_debug("\n=== rela list for %s (%ld entries) ===\n",
|
||||
sec->base->name, rela_nr);
|
||||
relasec->base->name, rela_nr);
|
||||
|
||||
if (is_debug_section(sec)) {
|
||||
if (is_debug_section(relasec)) {
|
||||
log_debug("skipping rela listing for .debug_* section\n");
|
||||
skip = 1;
|
||||
}
|
||||
|
||||
/* read and store the rela entries */
|
||||
while (rela_nr--) {
|
||||
ALLOC_LINK(rela, &sec->relas);
|
||||
ALLOC_LINK(rela, &relasec->relas);
|
||||
|
||||
if (!gelf_getrela(sec->data, index, &rela->rela))
|
||||
if (!gelf_getrela(relasec->data, index, &rela->rela))
|
||||
ERROR("gelf_getrela");
|
||||
index++;
|
||||
|
||||
|
@ -206,7 +305,9 @@ void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec)
|
|||
ERROR("could not find rela entry symbol\n");
|
||||
if (rela->sym->sec &&
|
||||
(rela->sym->sec->sh.sh_flags & SHF_STRINGS)) {
|
||||
rela->string = rela->sym->sec->data->d_buf + rela->addend;
|
||||
rela->string = rela->sym->sec->data->d_buf +
|
||||
rela->sym->sym.st_value +
|
||||
rela_target_offset(kelf, relasec, rela);
|
||||
if (!rela->string)
|
||||
ERROR("could not lookup rela string for %s+%ld",
|
||||
rela->sym->name, rela->addend);
|
||||
|
@ -223,7 +324,7 @@ void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec)
|
|||
}
|
||||
}
|
||||
|
||||
void kpatch_create_section_list(struct kpatch_elf *kelf)
|
||||
static void kpatch_create_section_list(struct kpatch_elf *kelf)
|
||||
{
|
||||
Elf_Scn *scn = NULL;
|
||||
struct section *sec;
|
||||
|
@ -277,7 +378,7 @@ void kpatch_create_section_list(struct kpatch_elf *kelf)
|
|||
ERROR("expected NULL");
|
||||
}
|
||||
|
||||
void kpatch_create_symbol_list(struct kpatch_elf *kelf)
|
||||
static void kpatch_create_symbol_list(struct kpatch_elf *kelf)
|
||||
{
|
||||
struct section *symtab;
|
||||
struct symbol *sym;
|
||||
|
@ -346,7 +447,7 @@ struct kpatch_elf *kpatch_elf_open(const char *name)
|
|||
Elf *elf;
|
||||
int fd;
|
||||
struct kpatch_elf *kelf;
|
||||
struct section *sec;
|
||||
struct section *relasec;
|
||||
GElf_Ehdr ehdr;
|
||||
|
||||
fd = open(name, O_RDONLY);
|
||||
|
@ -368,16 +469,6 @@ struct kpatch_elf *kpatch_elf_open(const char *name)
|
|||
/* read and store section, symbol entries from file */
|
||||
kelf->elf = elf;
|
||||
kelf->fd = fd;
|
||||
kpatch_create_section_list(kelf);
|
||||
kpatch_create_symbol_list(kelf);
|
||||
|
||||
/* for each rela section, read and store the rela entries */
|
||||
list_for_each_entry(sec, &kelf->sections, list) {
|
||||
if (!is_rela_section(sec))
|
||||
continue;
|
||||
INIT_LIST_HEAD(&sec->relas);
|
||||
kpatch_create_rela_list(kelf, sec);
|
||||
}
|
||||
|
||||
if (!gelf_getehdr(kelf->elf, &ehdr))
|
||||
ERROR("gelf_getehdr");
|
||||
|
@ -391,6 +482,18 @@ struct kpatch_elf *kpatch_elf_open(const char *name)
|
|||
default:
|
||||
ERROR("Unsupported target architecture");
|
||||
}
|
||||
|
||||
kpatch_create_section_list(kelf);
|
||||
kpatch_create_symbol_list(kelf);
|
||||
|
||||
/* for each rela section, read and store the rela entries */
|
||||
list_for_each_entry(relasec, &kelf->sections, list) {
|
||||
if (!is_rela_section(relasec))
|
||||
continue;
|
||||
INIT_LIST_HEAD(&relasec->relas);
|
||||
kpatch_create_rela_list(kelf, relasec);
|
||||
}
|
||||
|
||||
return kelf;
|
||||
}
|
||||
|
||||
|
@ -442,22 +545,22 @@ next:
|
|||
}
|
||||
}
|
||||
|
||||
int is_null_sym(struct symbol *sym)
|
||||
bool is_null_sym(struct symbol *sym)
|
||||
{
|
||||
return !strlen(sym->name);
|
||||
}
|
||||
|
||||
int is_file_sym(struct symbol *sym)
|
||||
bool is_file_sym(struct symbol *sym)
|
||||
{
|
||||
return sym->type == STT_FILE;
|
||||
}
|
||||
|
||||
int is_local_func_sym(struct symbol *sym)
|
||||
bool is_local_func_sym(struct symbol *sym)
|
||||
{
|
||||
return sym->bind == STB_LOCAL && sym->type == STT_FUNC;
|
||||
}
|
||||
|
||||
int is_local_sym(struct symbol *sym)
|
||||
bool is_local_sym(struct symbol *sym)
|
||||
{
|
||||
return sym->bind == STB_LOCAL;
|
||||
}
|
||||
|
|
|
@ -129,9 +129,9 @@ struct kpatch_elf {
|
|||
* Helper functions
|
||||
******************/
|
||||
char *status_str(enum status status);
|
||||
int is_rela_section(struct section *sec);
|
||||
int is_text_section(struct section *sec);
|
||||
int is_debug_section(struct section *sec);
|
||||
bool is_rela_section(struct section *sec);
|
||||
bool is_text_section(struct section *sec);
|
||||
bool is_debug_section(struct section *sec);
|
||||
|
||||
struct section *find_section_by_index(struct list_head *list, unsigned int index);
|
||||
struct section *find_section_by_name(struct list_head *list, const char *name);
|
||||
|
@ -152,6 +152,9 @@ struct rela *find_rela_by_offset(struct section *relasec, unsigned int offset);
|
|||
|
||||
unsigned int absolute_rela_type(struct kpatch_elf *kelf);
|
||||
int offset_of_string(struct list_head *list, char *name);
|
||||
long rela_target_offset(struct kpatch_elf *kelf, struct section *relasec,
|
||||
struct rela *rela);
|
||||
unsigned int insn_length(struct kpatch_elf *kelf, void *addr);
|
||||
|
||||
#ifndef R_PPC64_ENTRY
|
||||
#define R_PPC64_ENTRY 118
|
||||
|
@ -160,16 +163,13 @@ int offset_of_string(struct list_head *list, char *name);
|
|||
/*************
|
||||
* Functions
|
||||
* **********/
|
||||
void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec);
|
||||
void kpatch_create_section_list(struct kpatch_elf *kelf);
|
||||
void kpatch_create_symbol_list(struct kpatch_elf *kelf);
|
||||
struct kpatch_elf *kpatch_elf_open(const char *name);
|
||||
void kpatch_dump_kelf(struct kpatch_elf *kelf);
|
||||
|
||||
int is_null_sym(struct symbol *sym);
|
||||
int is_file_sym(struct symbol *sym);
|
||||
int is_local_func_sym(struct symbol *sym);
|
||||
int is_local_sym(struct symbol *sym);
|
||||
bool is_null_sym(struct symbol *sym);
|
||||
bool is_file_sym(struct symbol *sym);
|
||||
bool is_local_func_sym(struct symbol *sym);
|
||||
bool is_local_sym(struct symbol *sym);
|
||||
|
||||
void print_strtab(char *buf, size_t size);
|
||||
void kpatch_create_shstrtab(struct kpatch_elf *kelf);
|
||||
|
|
Loading…
Reference in New Issue