libcareplus/0038-kpatch_process-Split-function-object_find_patch_regi.patch
Jiajie Li 2b0fff2855 AArch64 support: add aarch64 support for libcareplus
Add related code which make libcareplus can run basic demo on aarch64.

Signed-off-by: Jiajie Li <lijiajie11@huawei.com>
2021-02-10 09:18:56 +08:00

417 lines
12 KiB
Diff

From 80c479726361710a9ac4f328687796a183cf780f Mon Sep 17 00:00:00 2001
From: Jiajie Li <lijiajie11@huawei.com>
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 <lijiajie11@huawei.com>
Signed-off-by: Ying Fang <fangying1@huawei.com>
---
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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <regex.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/vfs.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ptrace.h>
+
+#include <gelf.h>
+#include <libunwind.h>
+#include <libunwind-ptrace.h>
+
+#include <sys/socket.h>
+
+#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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <regex.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/vfs.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ptrace.h>
+
+#include <gelf.h>
+#include <libunwind.h>
+#include <libunwind-ptrace.h>
+
+#include <sys/socket.h>
+
+#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