diff --git a/kpatch-build/Makefile b/kpatch-build/Makefile index a874dd3..da96edf 100644 --- a/kpatch-build/Makefile +++ b/kpatch-build/Makefile @@ -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 $@ diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 1c2f584..3ad446c 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -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" @@ -577,39 +576,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 @@ -1477,33 +1443,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->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); -} - static bool is_callback_section(struct section *sec) { static char *callback_sections[] = { @@ -1538,13 +1477,12 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf) struct section *relasec; struct rela *rela; struct symbol *sym; - unsigned int add_off; + long target_off; log_debug("\n"); list_for_each_entry(relasec, &kelf->sections, list) { - if (!is_rela_section(relasec) || - is_debug_section(relasec)) + if (!is_rela_section(relasec) || is_debug_section(relasec)) continue; list_for_each_entry(rela, &relasec->relas, list) { @@ -1573,29 +1511,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 (!is_text_section(relasec->base) || - 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(relasec->base, rela, &insn); - add_off = (unsigned int)((long)insn.next_byte - - (long)relasec->base->data->d_buf - - rela->offset); - } else - ERROR("unhandled rela type %d", rela->type); - break; - default: - ERROR("unsupported arch"); - } + target_off = rela_target_offset(kelf, relasec, rela); /* * Attempt to replace references to unbundled sections @@ -1648,8 +1564,7 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf) * be the same as &var2. */ - } else if (rela->addend + add_off < start || - rela->addend + add_off >= end) + } else if (target_off < start || target_off >= end) continue; log_debug("%s: replacing %s+%ld reference with %s+%ld\n", diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 793cbd8..dba53b5 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -31,6 +31,7 @@ #include #include +#include "asm/insn.h" #include "kpatch-elf.h" /******************* @@ -164,6 +165,103 @@ int offset_of_string(struct list_head *list, char *name) return index; } +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) { diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h index 312de71..3bc6e76 100644 --- a/kpatch-build/kpatch-elf.h +++ b/kpatch-build/kpatch-elf.h @@ -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