Merge pull request #1269 from jpoimboe/reloc-addend

Fix addend handling and other cleanups for s390
This commit is contained in:
Josh Poimboeuf 2022-05-19 18:25:52 -05:00 committed by GitHub
commit f6e1838ab9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 392 additions and 313 deletions

View File

@ -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 $@

View File

@ -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);

View File

@ -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);

View File

@ -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.

View File

@ -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;
}

View File

@ -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);