Add related code which make libcareplus can run basic demo on aarch64. Signed-off-by: Jiajie Li <lijiajie11@huawei.com>
417 lines
12 KiB
Diff
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
|
|
|