mirror of
https://github.com/dynup/kpatch
synced 2024-12-30 01:02:03 +00:00
new .fixup group size algorithm
The fixup_group_size() function assumes that all .fixup rela groups end with a jmpq instruction. That assumption turns out to be false when you take into account the ____kvm_handle_fault_on_reboot() macro which is used by kvm. This is a new, more reliable method. It turns out that each .fixup group is referenced by the __ex_table section. The new algorithm goes through the __ex_table relas to figure out the size of each .fixup group. Also the .fixup section is now processed before __ex_table, because it needs to access the original __ex_table relas before the unused ones have been stripped. Fixes the following error: ERROR: vmx.o: fixup_group_size: 1554: can't find jump instruction in .fixup section
This commit is contained in:
parent
ba7c905b3a
commit
33cd945b14
@ -154,7 +154,7 @@ struct kpatch_elf {
|
||||
|
||||
struct special_section {
|
||||
char *name;
|
||||
int (*group_size)(struct section *sec, int offset);
|
||||
int (*group_size)(struct kpatch_elf *kelf, int offset);
|
||||
};
|
||||
|
||||
/*******************
|
||||
@ -1529,30 +1529,56 @@ void kpatch_reindex_elements(struct kpatch_elf *kelf)
|
||||
}
|
||||
|
||||
|
||||
int bug_table_group_size(struct section *sec, int offset) { return 12; }
|
||||
int smp_locks_group_size(struct section *sec, int offset) { return 4; }
|
||||
int parainstructions_group_size(struct section *sec, int offset) { return 16; }
|
||||
int ex_table_group_size(struct section *sec, int offset) { return 8; }
|
||||
int altinstructions_group_size(struct section *sec, int offset) { return 12; }
|
||||
int bug_table_group_size(struct kpatch_elf *kelf, int offset) { return 12; }
|
||||
int smp_locks_group_size(struct kpatch_elf *kelf, int offset) { return 4; }
|
||||
int parainstructions_group_size(struct kpatch_elf *kelf, int offset) { return 16; }
|
||||
int ex_table_group_size(struct kpatch_elf *kelf, int offset) { return 8; }
|
||||
int altinstructions_group_size(struct kpatch_elf *kelf, int offset) { return 12; }
|
||||
|
||||
int fixup_group_size(struct section *sec, int offset)
|
||||
/*
|
||||
* The rela groups in the .fixup section vary in size. The beginning of each
|
||||
* .fixup rela group is referenced by the __ex_table section. To find the size
|
||||
* of a .fixup rela group, we have to traverse the __ex_table relas.
|
||||
*/
|
||||
int fixup_group_size(struct kpatch_elf *kelf, int offset)
|
||||
{
|
||||
unsigned char *insn, *start, *end;
|
||||
struct section *sec;
|
||||
struct rela *rela;
|
||||
int found;
|
||||
|
||||
/*
|
||||
* Each fixup group is a collection of instructions. The last
|
||||
* instruction is always 'jmpq'.
|
||||
*/
|
||||
start = sec->data->d_buf + offset;
|
||||
end = start + sec->sh.sh_size;
|
||||
for (insn = start; insn < end; insn++) {
|
||||
/* looking for the pattern "e9 00 00 00 00" */
|
||||
if (*insn == 0xe9 && *(uint32_t *)(insn + 1) == 0)
|
||||
return insn + 5 - start;
|
||||
sec = find_section_by_name(&kelf->sections, ".rela__ex_table");
|
||||
if (!sec)
|
||||
ERROR("missing .rela__ex_table section");
|
||||
|
||||
/* find beginning of this group */
|
||||
found = 0;
|
||||
list_for_each_entry(rela, &sec->relas, list)
|
||||
if (!strcmp(rela->sym->name, ".fixup") &&
|
||||
rela->addend == offset) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
ERROR("can't find .fixup rela group at offset %d\n", offset);
|
||||
|
||||
/* find beginning of next group */
|
||||
found = 0;
|
||||
list_for_each_entry_continue(rela, &sec->relas, list)
|
||||
if (!strcmp(rela->sym->name, ".fixup") &&
|
||||
rela->addend > offset) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
/* last group */
|
||||
struct section *fixupsec;
|
||||
fixupsec = find_section_by_name(&kelf->sections, ".fixup");
|
||||
return fixupsec->sh.sh_size - offset;
|
||||
}
|
||||
|
||||
ERROR("can't find jump instruction in .fixup section");
|
||||
return 0;
|
||||
return rela->addend - offset;
|
||||
}
|
||||
|
||||
struct special_section special_sections[] = {
|
||||
@ -1568,6 +1594,10 @@ struct special_section special_sections[] = {
|
||||
.name = ".parainstructions",
|
||||
.group_size = parainstructions_group_size,
|
||||
},
|
||||
{
|
||||
.name = ".fixup",
|
||||
.group_size = fixup_group_size,
|
||||
},
|
||||
{
|
||||
.name = "__ex_table",
|
||||
.group_size = ex_table_group_size,
|
||||
@ -1576,10 +1606,6 @@ struct special_section special_sections[] = {
|
||||
.name = ".altinstructions",
|
||||
.group_size = altinstructions_group_size,
|
||||
},
|
||||
{
|
||||
.name = ".fixup",
|
||||
.group_size = fixup_group_size,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
@ -1603,7 +1629,8 @@ int should_keep_rela_group(struct section *sec, int start, int size)
|
||||
return found;
|
||||
}
|
||||
|
||||
void kpatch_regenerate_special_section(struct special_section *special,
|
||||
void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
|
||||
struct special_section *special,
|
||||
struct section *sec)
|
||||
{
|
||||
struct rela *rela, *safe;
|
||||
@ -1623,7 +1650,7 @@ void kpatch_regenerate_special_section(struct special_section *special,
|
||||
dest_offset = 0;
|
||||
for ( ; src_offset < sec->base->sh.sh_size; src_offset += group_size) {
|
||||
|
||||
group_size = special->group_size(sec->base, src_offset);
|
||||
group_size = special->group_size(kelf, src_offset);
|
||||
include = should_keep_rela_group(sec, src_offset, group_size);
|
||||
|
||||
if (!include)
|
||||
@ -1811,7 +1838,7 @@ void kpatch_process_special_sections(struct kpatch_elf *kelf)
|
||||
if (!sec)
|
||||
continue;
|
||||
|
||||
kpatch_regenerate_special_section(special, sec);
|
||||
kpatch_regenerate_special_section(kelf, special, sec);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -172,6 +172,14 @@ static inline void list_replace(struct list_head *old,
|
||||
#define list_first_entry(ptr, type, member) \
|
||||
list_entry((ptr)->next, type, member)
|
||||
|
||||
/**
|
||||
* list_next_entry - get the next element in list
|
||||
* @pos: the type * to cursor
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_next_entry(pos, member) \
|
||||
list_entry((pos)->member.next, typeof(*(pos)), member)
|
||||
|
||||
/**
|
||||
* list_for_each_entry - iterate over list of given type
|
||||
* @pos: the type * to use as a loop counter.
|
||||
@ -183,6 +191,20 @@ static inline void list_replace(struct list_head *old,
|
||||
&pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_continue - continue iteration over list of given type
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Continue to iterate over list of given type, continuing after
|
||||
* the current position.
|
||||
*/
|
||||
#define list_for_each_entry_continue(pos, head, member) \
|
||||
for (pos = list_next_entry(pos, member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_next_entry(pos, member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||
* @pos: the type * to use as a loop counter.
|
||||
|
13
test/integration/module-kvm-fixup.patch
Normal file
13
test/integration/module-kvm-fixup.patch
Normal file
@ -0,0 +1,13 @@
|
||||
Index: src/arch/x86/kvm/vmx.c
|
||||
===================================================================
|
||||
--- src.orig/arch/x86/kvm/vmx.c
|
||||
+++ src/arch/x86/kvm/vmx.c
|
||||
@@ -8687,6 +8687,8 @@ static int vmx_check_intercept(struct kv
|
||||
struct x86_instruction_info *info,
|
||||
enum x86_intercept_stage stage)
|
||||
{
|
||||
+ if (!jiffies)
|
||||
+ printk("kpatch vmx_check_intercept\n");
|
||||
return X86EMUL_CONTINUE;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user