libcareplus/0037-kpatch_elf-Split-function-kpatch_apply_relocate_add.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

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