From 80c479726361710a9ac4f328687796a183cf780f Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Mon, 12 Oct 2020 15:56:42 +0800 Subject: [PATCH 38/89] kpatch_process: Split function object_find_patch_region The function object_find_patch_region is arch related. Since process virtual address layout may be different between x86 and aarch64, let's make two separate definations in arch/x86/arch_process.c and arch/aarch64/arch_process.c Signed-off-by: Jiajie Li Signed-off-by: Ying Fang --- src/arch.desc | 1 + src/arch/aarch64/arch_process.c | 120 ++++++++++++++++++++++++++++++++ src/arch/x86/arch_process.c | 108 ++++++++++++++++++++++++++++ src/include/kpatch_process.h | 9 +++ src/kpatch_process.c | 87 ++--------------------- 5 files changed, 242 insertions(+), 83 deletions(-) create mode 100644 src/arch.desc create mode 100644 src/arch/aarch64/arch_process.c create mode 100644 src/arch/x86/arch_process.c diff --git a/src/arch.desc b/src/arch.desc new file mode 100644 index 0000000..9647742 --- /dev/null +++ b/src/arch.desc @@ -0,0 +1 @@ +aarch64 diff --git a/src/arch/aarch64/arch_process.c b/src/arch/aarch64/arch_process.c new file mode 100644 index 0000000..3a64d77 --- /dev/null +++ b/src/arch/aarch64/arch_process.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "include/kpatch_process.h" +#include "include/kpatch_file.h" +#include "include/kpatch_common.h" +#include "include/kpatch_elf.h" +#include "include/kpatch_ptrace.h" +#include "include/list.h" +#include "include/kpatch_log.h" + +/* + * Find region for a patch. Take object's `previous_hole` as a left candidate + * and the next hole as a right candidate. Pace through them until there is + * enough space in the hole for the patch. + * + * Since holes can be much larger than 2GiB take extra caution to allocate + * patch region inside the (-2GiB, +2GiB) range from the original object. + */ +unsigned long +object_find_patch_region(struct object_file *obj, + size_t memsize, + struct vm_hole **hole) +{ + struct list_head *head = &obj->proc->vmaholes; + struct vm_hole *left_hole = obj->previous_hole, + *right_hole = next_hole(left_hole, head); + unsigned long max_distance = 0x80000000; + struct obj_vm_area *sovma; + + unsigned long obj_start, obj_end; + unsigned long region_start = 0, region_end = 0; + + kpdebug("Looking for patch region for '%s'...\n", obj->name); + + sovma = list_first_entry(&obj->vma, struct obj_vm_area, list); + obj_start = sovma->inmem.start; + sovma = list_entry(obj->vma.prev, struct obj_vm_area, list); + obj_end = sovma->inmem.end; + + + max_distance -= memsize; + + /* TODO carefully check for the holes laying between obj_start and + * obj_end, i.e. just after the executable segment of an executable + */ + while (left_hole != NULL && right_hole != NULL) { + if (right_hole != NULL && + right_hole->start - obj_start > max_distance) + right_hole = NULL; + else if (hole_size(right_hole) > memsize) { + region_start = right_hole->start; + region_end = + (right_hole->end - obj_start) <= max_distance ? + right_hole->end - memsize : + obj_start + max_distance; + *hole = right_hole; + break; + } else + right_hole = next_hole(right_hole, head); + + if (left_hole != NULL && + obj_end - left_hole->end > max_distance) + left_hole = NULL; + else if (hole_size(left_hole) > memsize) { + region_start = + (left_hole->start - obj_end) <= max_distance ? + left_hole->start : obj_end > max_distance ? + obj_end - max_distance : 0; + region_end = left_hole->end - memsize; + *hole = left_hole; + break; + } else + left_hole = prev_hole(left_hole, head); + } + + if (region_start == region_end) { + kperr("can't find suitable region for patch on '%s'\n", + obj->name); + return -1UL; + } + + /* + * On aarch64, virtual address of text and data segments may be continuous, + * gap between data segment and process heap may be huge. Need to have + * region_end fixed. Here goes the trick: + * The branch instruction jump size is in the range of +/-128MB. + * So we need to put limitation to the region_end. + */ + region_end = region_start + (0x1<<25); + region_start = random_from_range(region_start >> PAGE_SHIFT, + region_end >> PAGE_SHIFT); + region_start <<= PAGE_SHIFT; + kpdebug("Found patch region for '%s' at %lx\n", obj->name, region_start); + + return region_start; +} + diff --git a/src/arch/x86/arch_process.c b/src/arch/x86/arch_process.c new file mode 100644 index 0000000..ba66134 --- /dev/null +++ b/src/arch/x86/arch_process.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "include/kpatch_process.h" +#include "include/kpatch_file.h" +#include "include/kpatch_common.h" +#include "include/kpatch_elf.h" +#include "include/kpatch_ptrace.h" +#include "include/list.h" +#include "include/kpatch_log.h" + +/* + * Find region for a patch. Take object's `previous_hole` as a left candidate + * and the next hole as a right candidate. Pace through them until there is + * enough space in the hole for the patch. + * + * Since holes can be much larger than 2GiB take extra caution to allocate + * patch region inside the (-2GiB, +2GiB) range from the original object. + */ +unsigned long object_find_patch_region(struct object_file *obj, + size_t memsize, + struct vm_hole **hole) +{ + struct list_head *head = &obj->proc->vmaholes; + struct vm_hole *left_hole = obj->previous_hole, + *right_hole = next_hole(left_hole, head); + unsigned long max_distance = 0x80000000; + struct obj_vm_area *sovma; + + unsigned long obj_start, obj_end; + unsigned long region_start = 0, region_end = 0; + + kpdebug("Looking for patch region for '%s'...\n", obj->name); + + sovma = list_first_entry(&obj->vma, struct obj_vm_area, list); + obj_start = sovma->inmem.start; + sovma = list_entry(obj->vma.prev, struct obj_vm_area, list); + obj_end = sovma->inmem.end; + + + max_distance -= memsize; + + /* TODO carefully check for the holes laying between obj_start and + * obj_end, i.e. just after the executable segment of an executable + */ + while (left_hole != NULL && right_hole != NULL) { + if (right_hole != NULL && + right_hole->start - obj_start > max_distance) + right_hole = NULL; + else if (hole_size(right_hole) > memsize) { + region_start = right_hole->start; + region_end = + (right_hole->end - obj_start) <= max_distance ? + right_hole->end - memsize : + obj_start + max_distance; + *hole = right_hole; + break; + } else + right_hole = next_hole(right_hole, head); + + if (left_hole != NULL && + obj_end - left_hole->end > max_distance) + left_hole = NULL; + else if (hole_size(left_hole) > memsize) { + region_start = + (left_hole->start - obj_end) <= max_distance ? + left_hole->start : obj_end > max_distance ? + obj_end - max_distance : 0; + region_end = left_hole->end - memsize; + *hole = left_hole; + break; + } else + left_hole = prev_hole(left_hole, head); + } + + if (region_start == region_end) { + kperr("can't find suitable region for patch on '%s'\n", + obj->name); + return -1UL; + } + + region_start += PAGE_SIZE; + kpdebug("Found patch region for '%s' at %lx\n", obj->name, region_start); + + return region_start; +} diff --git a/src/include/kpatch_process.h b/src/include/kpatch_process.h index abbb1af..b96a6da 100644 --- a/src/include/kpatch_process.h +++ b/src/include/kpatch_process.h @@ -211,4 +211,13 @@ is_kernel_object_name(char *name) return 0; } +struct vm_hole *next_hole(struct vm_hole *hole, struct list_head *head); +struct vm_hole *prev_hole(struct vm_hole *hole, struct list_head *head); +unsigned long hole_size(struct vm_hole *hole); + +unsigned long random_from_range(unsigned long min, unsigned long max); +unsigned long object_find_patch_region(struct object_file *obj, + size_t memsize, + struct vm_hole **hole); + #endif /* ifndef __KPATCH_PROCESS__ */ diff --git a/src/kpatch_process.c b/src/kpatch_process.c index 3f7f2f6..9561962 100644 --- a/src/kpatch_process.c +++ b/src/kpatch_process.c @@ -965,7 +965,7 @@ vm_hole_split(struct vm_hole *hole, return 0; } -static inline struct vm_hole * +inline struct vm_hole * next_hole(struct vm_hole *hole, struct list_head *head) { if (hole == NULL || hole->list.next == head) @@ -974,7 +974,7 @@ next_hole(struct vm_hole *hole, struct list_head *head) return list_entry(hole->list.next, struct vm_hole, list); } -static inline struct vm_hole * +inline struct vm_hole * prev_hole(struct vm_hole *hole, struct list_head *head) { if (hole == NULL || hole->list.prev == head) @@ -983,7 +983,7 @@ prev_hole(struct vm_hole *hole, struct list_head *head) return list_entry(hole->list.prev, struct vm_hole, list); } -static inline unsigned long +inline unsigned long hole_size(struct vm_hole *hole) { if (hole == NULL) @@ -991,92 +991,13 @@ hole_size(struct vm_hole *hole) return hole->end - hole->start; } -static unsigned long +unsigned long random_from_range(unsigned long min, unsigned long max) { /* TODO this is not uniform nor safe */ return min + random() % (max - min); } -/* - * Find region for a patch. Take object's `previous_hole` as a left candidate - * and the next hole as a right candidate. Pace through them until there is - * enough space in the hole for the patch. - * - * Since holes can be much larger than 2GiB take extra caution to allocate - * patch region inside the (-2GiB, +2GiB) range from the original object. - */ -static unsigned long -object_find_patch_region(struct object_file *obj, - size_t memsize, - struct vm_hole **hole) -{ - struct list_head *head = &obj->proc->vmaholes; - struct vm_hole *left_hole = obj->previous_hole, - *right_hole = next_hole(left_hole, head); - unsigned long max_distance = 0x80000000; - struct obj_vm_area *sovma; - - unsigned long obj_start, obj_end; - unsigned long region_start = 0, region_end = 0; - - kpdebug("Looking for patch region for '%s'...\n", obj->name); - - sovma = list_first_entry(&obj->vma, struct obj_vm_area, list); - obj_start = sovma->inmem.start; - sovma = list_entry(obj->vma.prev, struct obj_vm_area, list); - obj_end = sovma->inmem.end; - - - max_distance -= memsize; - - /* TODO carefully check for the holes laying between obj_start and - * obj_end, i.e. just after the executable segment of an executable - */ - while (left_hole != NULL && right_hole != NULL) { - if (right_hole != NULL && - right_hole->start - obj_start > max_distance) - right_hole = NULL; - else if (hole_size(right_hole) > memsize) { - region_start = right_hole->start; - region_end = - (right_hole->end - obj_start) <= max_distance ? - right_hole->end - memsize : - obj_start + max_distance; - *hole = right_hole; - break; - } else - right_hole = next_hole(right_hole, head); - - if (left_hole != NULL && - obj_end - left_hole->end > max_distance) - left_hole = NULL; - else if (hole_size(left_hole) > memsize) { - region_start = - (left_hole->start - obj_end) <= max_distance ? - left_hole->start : obj_end > max_distance ? - obj_end - max_distance : 0; - region_end = left_hole->end - memsize; - *hole = left_hole; - break; - } else - left_hole = prev_hole(left_hole, head); - } - - if (region_start == region_end) { - kperr("can't find suitable region for patch on '%s'\n", - obj->name); - return -1UL; - } - - region_start = random_from_range(region_start >> PAGE_SHIFT, - region_end >> PAGE_SHIFT); - region_start <<= PAGE_SHIFT; - kpdebug("Found patch region for '%s' at %lx\n", obj->name, region_start); - - return region_start; -} - int kpatch_object_allocate_patch(struct object_file *o, size_t sz) -- 2.23.0