From 69837926282fc65d41be15390d9a125da97adc54 Mon Sep 17 00:00:00 2001 From: Jiajie Li 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 Signed-off-by: Ying Fang --- 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 #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