create-diff-object: move addend math to a new function

Split out the addend offset math into a separate function so it can be
used elsewhere.

Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
This commit is contained in:
Josh Poimboeuf 2022-05-11 16:25:13 -07:00
parent bec6488af6
commit 01427d50a1
4 changed files with 107 additions and 91 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"
@ -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",

View File

@ -31,6 +31,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#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)
{

View File

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