Add related code which make libcareplus can run basic demo on aarch64. Signed-off-by: Jiajie Li <lijiajie11@huawei.com>
415 lines
13 KiB
Diff
415 lines
13 KiB
Diff
From 69837926282fc65d41be15390d9a125da97adc54 Mon Sep 17 00:00:00 2001
|
|
From: Jiajie Li <lijiajie11@huawei.com>
|
|
Date: Mon, 12 Oct 2020 15:37:08 +0800
|
|
Subject: [PATCH 37/89] kpatch_elf: Split function kpatch_apply_relocate_add
|
|
|
|
The function kpatch_apply_relocate_add is arch related. To support multi-arch
|
|
let's rename it with kpatch_arch_apply_relocate_add, and make the defination
|
|
in arch/x86/arch_elf.c and arch/aarch64/arch_elf.c
|
|
|
|
Signed-off-by: Jiajie Li <lijiajie11@huawei.com>
|
|
Signed-off-by: Ying Fang <fangying1@huawei.com>
|
|
---
|
|
src/arch/aarch64/arch_elf.c | 100 ++++++++++++++++++++++++++++++++++++
|
|
src/arch/x86/arch_elf.c | 89 ++++++++++++++++++++++++++++++++
|
|
src/include/bitops.h | 27 ++++++++++
|
|
src/include/kpatch_elf.h | 5 ++
|
|
src/kpatch_elf.c | 94 ++-------------------------------
|
|
5 files changed, 224 insertions(+), 91 deletions(-)
|
|
create mode 100644 src/include/bitops.h
|
|
|
|
diff --git a/src/arch/aarch64/arch_elf.c b/src/arch/aarch64/arch_elf.c
|
|
index b977489..deacb6f 100644
|
|
--- a/src/arch/aarch64/arch_elf.c
|
|
+++ b/src/arch/aarch64/arch_elf.c
|
|
@@ -14,6 +14,106 @@
|
|
#include "include/kpatch_file.h"
|
|
#include "include/kpatch_ptrace.h"
|
|
#include "include/kpatch_log.h"
|
|
+#include "include/bitops.h"
|
|
+
|
|
+static int kpatch_arch_apply_relocate(GElf_Rela *r, GElf_Sym *s,
|
|
+ void *loc, void *loc2, unsigned long val)
|
|
+{
|
|
+ switch (GELF_R_TYPE(r->r_info)) {
|
|
+ case R_AARCH64_ABS64:
|
|
+ *(unsigned long *)loc = val;
|
|
+ kpdebug("R_AARCH64_ABS64: loc=0x%x, val =0x%lx\n",*(unsigned int*)loc,val);
|
|
+ break;
|
|
+ case R_AARCH64_ADD_ABS_LO12_NC: {
|
|
+ //ADD ins
|
|
+ kpdebug("R_AARCH64_ADD_ABS_LO12_NC: val=0x%lx\n", val);
|
|
+ val = val & 0xfff;
|
|
+ uint32_t mask = 0xfff << 10;
|
|
+ *(unsigned int*)loc &= ~mask;
|
|
+ or_32(loc, (val & 0xfff) << 10);
|
|
+ kpdebug("R_AARCH64_ADD_ABS_LO12_NC: loc=0x%x, val =0x%lx\n",*(unsigned int*)loc,val);
|
|
+ break;
|
|
+ }
|
|
+ case R_AARCH64_CALL26: {
|
|
+ // TODO bl ins
|
|
+ kpdebug("R_AARCH64_CALL26: val=0x%lx\n", val);
|
|
+ val -= (unsigned long)loc2;
|
|
+ uint32_t mask = 0x03FFFFFF;;
|
|
+ *(unsigned int*)loc &= ~mask;
|
|
+ or_32(loc, (val >> 2) & mask);
|
|
+ kpdebug("R_AARCH64_CALL26: loc=0x%x, val =0x%lx\n",*(unsigned int*)loc, val);
|
|
+ break;
|
|
+ }
|
|
+ case R_AARCH64_ADR_PREL_PG_HI21: {
|
|
+ // TODO ADRP ins
|
|
+ kpdebug("RR_AARCH64_ADR_PREL_PG_HI21: val=0x%lx\n", val);
|
|
+ val = (val >> 12) - ((unsigned long)loc2 >> 12);
|
|
+ kpdebug("val=0x%lx\n",val);
|
|
+ uint32_t immLo = (val & 0x3) << 29;
|
|
+ uint32_t immHi = (val & 0x1FFFFC) << 3;
|
|
+ uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3);
|
|
+ *(unsigned int*)loc = (*(unsigned int*)loc & ~mask) | immLo | immHi;
|
|
+ //*(unsigned int*)loc &= 0x7fffffff;
|
|
+ kpdebug("lo=0x%x hi=0x%x\n",immLo,immHi);
|
|
+ kpdebug("R_AARCH64_ADR_PREL_PG_HI21: loc=0x%x, val=0x%lx\n", *(unsigned int *)loc, val);
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ kperr("unknown relocation type: %lx\n", r->r_info);
|
|
+ return -1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int kpatch_arch_apply_relocate_add(struct object_file *o, GElf_Shdr *relsec)
|
|
+{
|
|
+ struct kpatch_file *kp = o->kpfile.patch;
|
|
+ GElf_Ehdr *ehdr = (void *)kp + kp->kpatch_offset;
|
|
+ GElf_Shdr *shdr = (void *)ehdr + ehdr->e_shoff, *symhdr;
|
|
+ GElf_Rela *relocs = (void *)ehdr + relsec->sh_offset;
|
|
+ GElf_Shdr *tshdr = shdr + relsec->sh_info;
|
|
+ void *t = (void *)ehdr + shdr[relsec->sh_info].sh_offset;
|
|
+ void *tshdr2 = (void *)shdr[relsec->sh_info].sh_addr;
|
|
+ int i, is_kpatch_info;
|
|
+ const char *scnname;
|
|
+
|
|
+ for (i = 1; i < ehdr->e_shnum; i++) {
|
|
+ if (shdr[i].sh_type == SHT_SYMTAB)
|
|
+ symhdr = &shdr[i];
|
|
+ }
|
|
+
|
|
+ scnname = secname(ehdr, shdr + relsec->sh_info);
|
|
+ kpdebug("applying relocations to '%s'\n", scnname);
|
|
+ is_kpatch_info = strcmp(scnname, ".kpatch.info") == 0;
|
|
+
|
|
+ for (i = 0; i < relsec->sh_size / sizeof(*relocs); i++) {
|
|
+ GElf_Rela *r = relocs + i;
|
|
+ GElf_Sym *s;
|
|
+ unsigned long val;
|
|
+ void *loc, *loc2;
|
|
+
|
|
+ if (r->r_offset < 0 || r->r_offset >= tshdr->sh_size)
|
|
+ kpfatalerror("Relocation offset for section '%s'"
|
|
+ " is at 0x%lx beyond the section size 0x%lx\n",
|
|
+ scnname, r->r_offset, tshdr->sh_size);
|
|
+
|
|
+ /* Location in our address space */
|
|
+ loc = t + r->r_offset;
|
|
+ /* Location in target process address space (for relative addressing) */
|
|
+ loc2 = tshdr2 + r->r_offset;
|
|
+ s = (GElf_Sym *)((void *)ehdr + symhdr->sh_offset) + GELF_R_SYM(r->r_info);
|
|
+ val = s->st_value + r->r_addend;
|
|
+
|
|
+ if (is_kpatch_info && is_undef_symbol(s)) {
|
|
+ val = s->st_size;
|
|
+ }
|
|
+
|
|
+ kpatch_arch_apply_relocate(r, s, loc, loc2, val);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
|
|
#define JMP_TABLE_JUMP 0xd61f022058000051 /* ldr x17 #8; br x17 */
|
|
unsigned long kpatch_arch_add_jmp_entry(struct object_file *o, unsigned long addr)
|
|
diff --git a/src/arch/x86/arch_elf.c b/src/arch/x86/arch_elf.c
|
|
index ef5564e..52de117 100644
|
|
--- a/src/arch/x86/arch_elf.c
|
|
+++ b/src/arch/x86/arch_elf.c
|
|
@@ -15,6 +15,95 @@
|
|
#include "include/kpatch_ptrace.h"
|
|
#include "include/kpatch_log.h"
|
|
|
|
+int kpatch_arch_apply_relocate_add(struct object_file *o, GElf_Shdr *relsec)
|
|
+{
|
|
+ struct kpatch_file *kp = o->kpfile.patch;
|
|
+ GElf_Ehdr *ehdr = (void *)kp + kp->kpatch_offset;
|
|
+ GElf_Shdr *shdr = (void *)ehdr + ehdr->e_shoff, *symhdr;
|
|
+ GElf_Rela *relocs = (void *)ehdr + relsec->sh_offset;
|
|
+ GElf_Shdr *tshdr = shdr + relsec->sh_info;
|
|
+ void *t = (void *)ehdr + shdr[relsec->sh_info].sh_offset;
|
|
+ void *tshdr2 = (void *)shdr[relsec->sh_info].sh_addr;
|
|
+ int i, is_kpatch_info;
|
|
+ const char *scnname;
|
|
+
|
|
+ for (i = 1; i < ehdr->e_shnum; i++) {
|
|
+ if (shdr[i].sh_type == SHT_SYMTAB)
|
|
+ symhdr = &shdr[i];
|
|
+ }
|
|
+
|
|
+ scnname = secname(ehdr, shdr + relsec->sh_info);
|
|
+ kpdebug("applying relocations to '%s'\n", scnname);
|
|
+ is_kpatch_info = strcmp(scnname, ".kpatch.info") == 0;
|
|
+
|
|
+ for (i = 0; i < relsec->sh_size / sizeof(*relocs); i++) {
|
|
+ GElf_Rela *r = relocs + i;
|
|
+ GElf_Sym *s;
|
|
+ unsigned long val;
|
|
+ void *loc, *loc2;
|
|
+
|
|
+ if (r->r_offset < 0 || r->r_offset >= tshdr->sh_size)
|
|
+ kpfatalerror("Relocation offset for section '%s'"
|
|
+ " is at 0x%lx beyond the section size 0x%lx\n",
|
|
+ scnname, r->r_offset, tshdr->sh_size);
|
|
+
|
|
+ /* Location in our address space */
|
|
+ loc = t + r->r_offset;
|
|
+ /* Location in target process address space (for relative addressing) */
|
|
+ loc2 = tshdr2 + r->r_offset;
|
|
+ s = (GElf_Sym *)((void *)ehdr + symhdr->sh_offset) + GELF_R_SYM(r->r_info);
|
|
+ val = s->st_value + r->r_addend;
|
|
+
|
|
+ if (is_kpatch_info && is_undef_symbol(s)) {
|
|
+ val = s->st_size;
|
|
+ }
|
|
+
|
|
+ switch (GELF_R_TYPE(r->r_info)) {
|
|
+ case R_X86_64_NONE:
|
|
+ break;
|
|
+ case R_X86_64_64:
|
|
+ *(unsigned long *)loc = val;
|
|
+ break;
|
|
+ case R_X86_64_32:
|
|
+ *(unsigned int *)loc = val;
|
|
+ break;
|
|
+ case R_X86_64_32S:
|
|
+ *(signed int *)loc = val;
|
|
+ break;
|
|
+ case R_X86_64_GOTTPOFF:
|
|
+ case R_X86_64_GOTPCREL:
|
|
+ case R_X86_64_REX_GOTPCRELX:
|
|
+ case R_X86_64_GOTPCRELX:
|
|
+ if (is_undef_symbol(s)) {
|
|
+ /* This is an undefined symbol,
|
|
+ * use jmp table as the GOT */
|
|
+ val += sizeof(unsigned long);
|
|
+ } else if (GELF_ST_TYPE(s->st_info) == STT_TLS) {
|
|
+ /* This is GOTTPOFF that already points
|
|
+ * to an appropriate GOT entry in the
|
|
+ * patient's memory.
|
|
+ */
|
|
+ val = r->r_addend + o->load_offset - 4;
|
|
+ }
|
|
+ /* FALLTHROUGH */
|
|
+ case R_X86_64_PC32:
|
|
+ val -= (unsigned long)loc2;
|
|
+ *(unsigned int *)loc = val;
|
|
+ break;
|
|
+ case R_X86_64_TPOFF64:
|
|
+ case R_X86_64_TPOFF32:
|
|
+ kperr("TPOFF32/TPOFF64 should not be present\n");
|
|
+ break;
|
|
+ default:
|
|
+ kperr("unknown relocation type: %lx\n", r->r_info);
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
#define JMP_TABLE_JUMP 0x90900000000225ff /* jmp [rip+2]; nop; nop */
|
|
unsigned long kpatch_arch_add_jmp_entry(struct object_file *o, unsigned long addr)
|
|
{
|
|
diff --git a/src/include/bitops.h b/src/include/bitops.h
|
|
new file mode 100644
|
|
index 0000000..aab1679
|
|
--- /dev/null
|
|
+++ b/src/include/bitops.h
|
|
@@ -0,0 +1,27 @@
|
|
+#ifndef BITOPS_H
|
|
+#define BITOPS_H
|
|
+
|
|
+#define BITS_PER_BYTE CHAR_BIT
|
|
+#define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE)
|
|
+
|
|
+#define BIT(nr) (1UL << (nr))
|
|
+#define BIT_ULL(nr) (1ULL << (nr))
|
|
+#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
|
|
+#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
|
|
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
|
|
+
|
|
+static inline void or_32(void *addr, unsigned int val)
|
|
+{
|
|
+ *(unsigned int*) addr = *(unsigned int*)addr | val;
|
|
+}
|
|
+
|
|
+static inline void or_64(void *addr, unsigned long val)
|
|
+{
|
|
+ *(unsigned long*) addr = *(unsigned long*)addr | val;
|
|
+}
|
|
+
|
|
+static inline void and_32(void *addr, unsigned int val)
|
|
+{
|
|
+ *(unsigned int*) addr = *(unsigned int*)addr & val;
|
|
+}
|
|
+#endif
|
|
diff --git a/src/include/kpatch_elf.h b/src/include/kpatch_elf.h
|
|
index 7e5d8c3..74efe04 100644
|
|
--- a/src/include/kpatch_elf.h
|
|
+++ b/src/include/kpatch_elf.h
|
|
@@ -1,6 +1,7 @@
|
|
#ifndef __KPATCH_ELF__
|
|
#define __KPATCH_ELF__
|
|
|
|
+#include <gelf.h>
|
|
#include "kpatch_process.h"
|
|
|
|
const char *kpatch_get_buildid(struct object_file *o);
|
|
@@ -45,4 +46,8 @@ struct kpatch_jmp_table {
|
|
|
|
unsigned long kpatch_arch_add_jmp_entry(struct object_file *o, unsigned long addr);
|
|
|
|
+char *secname(GElf_Ehdr *ehdr, GElf_Shdr *s);
|
|
+int is_undef_symbol(const Elf64_Sym *sym);
|
|
+int kpatch_arch_apply_relocate_add(struct object_file *o, GElf_Shdr *relsec);
|
|
+
|
|
#endif
|
|
diff --git a/src/kpatch_elf.c b/src/kpatch_elf.c
|
|
index 21ba496..5506292 100644
|
|
--- a/src/kpatch_elf.c
|
|
+++ b/src/kpatch_elf.c
|
|
@@ -410,7 +410,7 @@ out:
|
|
return rv;
|
|
}
|
|
|
|
-static char *secname(GElf_Ehdr *ehdr, GElf_Shdr *s)
|
|
+char *secname(GElf_Ehdr *ehdr, GElf_Shdr *s)
|
|
{
|
|
GElf_Shdr *shdr = (void *)ehdr + ehdr->e_shoff;
|
|
char *str = (void *)ehdr + shdr[ehdr->e_shstrndx].sh_offset;
|
|
@@ -450,7 +450,7 @@ struct kpatch_jmp_table *kpatch_new_jmp_table(int entries)
|
|
return jtbl;
|
|
}
|
|
|
|
-static inline int
|
|
+inline int
|
|
is_undef_symbol(const Elf64_Sym *sym)
|
|
{
|
|
return sym->st_shndx == SHN_UNDEF || sym->st_shndx >= SHN_LORESERVE;
|
|
@@ -805,94 +805,6 @@ int kpatch_resolve(struct object_file *o)
|
|
return 0;
|
|
}
|
|
|
|
-static int kpatch_apply_relocate_add(struct object_file *o, GElf_Shdr *relsec)
|
|
-{
|
|
- struct kpatch_file *kp = o->kpfile.patch;
|
|
- GElf_Ehdr *ehdr = (void *)kp + kp->kpatch_offset;
|
|
- GElf_Shdr *shdr = (void *)ehdr + ehdr->e_shoff, *symhdr;
|
|
- GElf_Rela *relocs = (void *)ehdr + relsec->sh_offset;
|
|
- GElf_Shdr *tshdr = shdr + relsec->sh_info;
|
|
- void *t = (void *)ehdr + shdr[relsec->sh_info].sh_offset;
|
|
- void *tshdr2 = (void *)shdr[relsec->sh_info].sh_addr;
|
|
- int i, is_kpatch_info;
|
|
- const char *scnname;
|
|
-
|
|
- for (i = 1; i < ehdr->e_shnum; i++) {
|
|
- if (shdr[i].sh_type == SHT_SYMTAB)
|
|
- symhdr = &shdr[i];
|
|
- }
|
|
-
|
|
- scnname = secname(ehdr, shdr + relsec->sh_info);
|
|
- kpdebug("applying relocations to '%s'\n", scnname);
|
|
- is_kpatch_info = strcmp(scnname, ".kpatch.info") == 0;
|
|
-
|
|
- for (i = 0; i < relsec->sh_size / sizeof(*relocs); i++) {
|
|
- GElf_Rela *r = relocs + i;
|
|
- GElf_Sym *s;
|
|
- unsigned long val;
|
|
- void *loc, *loc2;
|
|
-
|
|
- if (r->r_offset < 0 || r->r_offset >= tshdr->sh_size)
|
|
- kpfatalerror("Relocation offset for section '%s'"
|
|
- " is at 0x%lx beyond the section size 0x%lx\n",
|
|
- scnname, r->r_offset, tshdr->sh_size);
|
|
-
|
|
- /* Location in our address space */
|
|
- loc = t + r->r_offset;
|
|
- /* Location in target process address space (for relative addressing) */
|
|
- loc2 = tshdr2 + r->r_offset;
|
|
- s = (GElf_Sym *)((void *)ehdr + symhdr->sh_offset) + GELF_R_SYM(r->r_info);
|
|
- val = s->st_value + r->r_addend;
|
|
-
|
|
- if (is_kpatch_info && is_undef_symbol(s)) {
|
|
- val = s->st_size;
|
|
- }
|
|
-
|
|
- switch (GELF_R_TYPE(r->r_info)) {
|
|
- case R_X86_64_NONE:
|
|
- break;
|
|
- case R_X86_64_64:
|
|
- *(unsigned long *)loc = val;
|
|
- break;
|
|
- case R_X86_64_32:
|
|
- *(unsigned int *)loc = val;
|
|
- break;
|
|
- case R_X86_64_32S:
|
|
- *(signed int *)loc = val;
|
|
- break;
|
|
- case R_X86_64_GOTTPOFF:
|
|
- case R_X86_64_GOTPCREL:
|
|
- case R_X86_64_REX_GOTPCRELX:
|
|
- case R_X86_64_GOTPCRELX:
|
|
- if (is_undef_symbol(s)) {
|
|
- /* This is an undefined symbol,
|
|
- * use jmp table as the GOT */
|
|
- val += sizeof(unsigned long);
|
|
- } else if (GELF_ST_TYPE(s->st_info) == STT_TLS) {
|
|
- /* This is GOTTPOFF that already points
|
|
- * to an appropriate GOT entry in the
|
|
- * patient's memory.
|
|
- */
|
|
- val = r->r_addend + o->load_offset - 4;
|
|
- }
|
|
- /* FALLTHROUGH */
|
|
- case R_X86_64_PC32:
|
|
- val -= (unsigned long)loc2;
|
|
- *(unsigned int *)loc = val;
|
|
- break;
|
|
- case R_X86_64_TPOFF64:
|
|
- case R_X86_64_TPOFF32:
|
|
- kperr("TPOFF32/TPOFF64 should not be present\n");
|
|
- break;
|
|
- default:
|
|
- kperr("unknown relocation type: %lx\n", r->r_info);
|
|
- return -1;
|
|
- }
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
int kpatch_relocate(struct object_file *o)
|
|
{
|
|
GElf_Ehdr *ehdr;
|
|
@@ -907,7 +819,7 @@ int kpatch_relocate(struct object_file *o)
|
|
GElf_Shdr *s = shdr + i;
|
|
|
|
if (s->sh_type == SHT_RELA)
|
|
- ret = kpatch_apply_relocate_add(o, s);
|
|
+ ret = kpatch_arch_apply_relocate_add(o, s);
|
|
else if (shdr->sh_type == SHT_REL) {
|
|
kperr("TODO: handle SHT_REL\n");
|
|
return -1;
|
|
--
|
|
2.23.0
|
|
|