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:
Josh Poimboeuf 2014-09-14 21:46:36 -05:00
parent ba7c905b3a
commit 33cd945b14
3 changed files with 89 additions and 27 deletions

View File

@ -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);
}
/*

View File

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

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