From 8ee86fbed7c4b8e1f13c3ce96ae16ff5ef56d281 Mon Sep 17 00:00:00 2001 From: Jvle Date: Mon, 3 Mar 2025 10:14:04 +0800 Subject: [PATCH 1/5] riscv64: sync riscv64 support from master to 2503 sync from 64e3f03f467be4756d546f68ecbfce5ec93e0d42 Signed-off-by: Jvle --- upatch-diff/create-diff-object.c | 28 ++- upatch-diff/elf-common.c | 40 +++- upatch-diff/elf-common.h | 2 + upatch-diff/elf-insn.c | 8 + upatch-diff/elf-insn.h | 5 + upatch-diff/upatch-elf.c | 10 + upatch-diff/upatch-elf.h | 3 +- upatch-manage/arch/riscv64/insn.h | 123 ++++++++++++ upatch-manage/arch/riscv64/process.h | 28 +++ upatch-manage/arch/riscv64/ptrace.c | 207 ++++++++++++++++++++ upatch-manage/arch/riscv64/relocation.c | 242 ++++++++++++++++++++++++ upatch-manage/arch/riscv64/resolve.c | 154 +++++++++++++++ upatch-manage/upatch-patch.c | 22 +++ upatch-manage/upatch-ptrace.c | 4 + upatch-manage/upatch-ptrace.h | 8 + 15 files changed, 881 insertions(+), 3 deletions(-) create mode 100644 upatch-manage/arch/riscv64/insn.h create mode 100644 upatch-manage/arch/riscv64/process.h create mode 100644 upatch-manage/arch/riscv64/ptrace.c create mode 100644 upatch-manage/arch/riscv64/relocation.c create mode 100644 upatch-manage/arch/riscv64/resolve.c diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c index 9e4504e9..886d8b21 100644 --- a/upatch-diff/create-diff-object.c +++ b/upatch-diff/create-diff-object.c @@ -434,7 +434,8 @@ static void replace_section_syms(struct upatch_elf *uelf) !is_text_section(sym->sec) && (rela->type == R_X86_64_32S || rela->type == R_X86_64_32 || - rela->type == R_AARCH64_ABS64) && + rela->type == R_AARCH64_ABS64 || + rela->type == R_RISCV_64) && rela->addend == (long)sym->sec->sh.sh_size && end == (long)sym->sec->sh.sh_size) { ERROR("Relocation refer end of data sections."); @@ -590,6 +591,12 @@ static void include_symbol(struct symbol *sym) if ((sym->status != SAME) || (sym->type == STT_SECTION)) { include_section(sym->sec); } +#ifdef __riscv + /* .L symbols not exist in EXE. If they are included, so are their sections. */ + else if (sym->sec && !sym->sec->include && !strncmp(sym->name, ".L", 2)) { + include_section(sym->sec); + } +#endif } static void include_section(struct section *sec) @@ -749,6 +756,23 @@ static void verify_patchability(struct upatch_elf *uelf) } } +/* + * These types are for linker optimization and memory layout. + * They have no associated symbols and their names are empty + * string which would mismatch running-elf symbols in later + * lookup_relf(). Drop these useless items now. + */ +static void rv_drop_useless_rela(struct section *relasec) +{ + struct rela *rela, *saferela; + list_for_each_entry_safe(rela, saferela, &relasec->relas, list) + if (rela->type == R_RISCV_RELAX || rela->type == R_RISCV_ALIGN) { + list_del(&rela->list); + memset(rela, 0, sizeof(*rela)); + free(rela); + } +} + static void migrate_included_elements(struct upatch_elf *uelf_patched, struct upatch_elf *uelf_out) { @@ -776,6 +800,8 @@ static void migrate_included_elements(struct upatch_elf *uelf_patched, if (sec->sym && !sec->sym->include) { sec->sym = NULL; // break link to non-included section symbol } + } else if (uelf_patched->arch == RISCV64) { + rv_drop_useless_rela(sec); } } diff --git a/upatch-diff/elf-common.c b/upatch-diff/elf-common.c index c5d8f619..eb0d0738 100644 --- a/upatch-diff/elf-common.c +++ b/upatch-diff/elf-common.c @@ -26,6 +26,7 @@ #include "elf-common.h" + static bool is_dynamic_debug_symbol(struct symbol *sym) { static const char *SEC_NAMES[] = { @@ -113,6 +114,35 @@ bool is_special_static_section(struct section *sec) return is_special_static_symbol(sym); } +#ifdef __riscv +/* + * .L local symbols are named as ".L" + "class prefix" + "number". + * The numbers are volatile due to code change. + * Compare class prefix(composed of letters) only. + */ +static int mangled_strcmp_dot_L(char *str1, char *str2) +{ + if (!*str2 || strncmp(str2, ".L", 2)) { + return 1; + } + + /* RISCV_FAKE_LABEL_NAME matched exactly */ + if (!strcmp(str1, ".L0 ") || !strcmp(str2, ".L0 ")) { + return strcmp(str1, str2); + } + + char *p = str1 + 2; + char *q = str2 + 2; + while (*p < '0' || *p > '9') p++; + while (*q < '0' || *q > '9') q++; + if ((p - str1 != q - str2) || strncmp(str1, str2, p - str1)) { + return 1; + } + + return 0; +} +#endif + int mangled_strcmp(char *str1, char *str2) { /* @@ -122,6 +152,12 @@ int mangled_strcmp(char *str1, char *str2) return strcmp(str1, str2); } +#ifdef __riscv + if (!strncmp(str1, ".L", 2)) { + return mangled_strcmp_dot_L(str1, str2); + } +#endif + while (*str1 == *str2) { if (!*str2) { return 0; @@ -192,6 +228,8 @@ bool is_gcc6_localentry_bundled_sym(struct upatch_elf *uelf) return false; case X86_64: return false; + case RISCV64: + return false; default: ERROR("unsupported arch"); } @@ -201,7 +239,7 @@ bool is_gcc6_localentry_bundled_sym(struct upatch_elf *uelf) bool is_mapping_symbol(struct upatch_elf *uelf, struct symbol *sym) { - if (uelf->arch != AARCH64) { + if ((uelf->arch != AARCH64) && (uelf->arch != RISCV64)) { return false; } if (sym->name && sym->name[0] == '$' && diff --git a/upatch-diff/elf-common.h b/upatch-diff/elf-common.h index 3cebd32c..f252cb95 100644 --- a/upatch-diff/elf-common.h +++ b/upatch-diff/elf-common.h @@ -278,6 +278,8 @@ static inline unsigned int absolute_rela_type(struct upatch_elf *uelf) return R_AARCH64_ABS64; case X86_64: return R_X86_64_64; + case RISCV64: + return R_RISCV_64; default: ERROR("unsupported arch"); } diff --git a/upatch-diff/elf-insn.c b/upatch-diff/elf-insn.c index 6fabd887..466a00c9 100644 --- a/upatch-diff/elf-insn.c +++ b/upatch-diff/elf-insn.c @@ -67,6 +67,8 @@ long rela_target_offset(struct upatch_elf *uelf, struct section *relasec, struct long add_off; switch (uelf->arch) { + case RISCV64: + /* fall through */ case AARCH64: add_off = 0; break; @@ -106,6 +108,12 @@ unsigned int insn_length(struct upatch_elf *uelf, void *addr) insn_init(&decoded_insn, addr, 1); insn_get_length(&decoded_insn); return decoded_insn.length; + case RISCV64: + /* LSB 2 bits distinguish insn size. Now only RV32, RVC supported. */ + if ((*(char *)addr & 0x3) == 0x3) { + return RISCV64_INSN_LEN_4; + } + return RISCV64_INSN_LEN_2; default: ERROR("unsupported arch"); } diff --git a/upatch-diff/elf-insn.h b/upatch-diff/elf-insn.h index ac48ad03..fdbc2e34 100644 --- a/upatch-diff/elf-insn.h +++ b/upatch-diff/elf-insn.h @@ -32,8 +32,13 @@ struct section; struct rela; struct insn; +// arm #define ARM64_INSTR_LEN 4 +// riscv +#define RISCV64_INSN_LEN_4 4 +#define RISCV64_INSN_LEN_2 2 + void rela_insn(const struct section *sec, const struct rela *rela, struct insn *insn); /* diff --git a/upatch-diff/upatch-elf.c b/upatch-diff/upatch-elf.c index c21b6ecc..331504bb 100644 --- a/upatch-diff/upatch-elf.c +++ b/upatch-diff/upatch-elf.c @@ -321,6 +321,16 @@ void uelf_open(struct upatch_elf *uelf, const char *name) case EM_X86_64: uelf->arch = X86_64; break; + case EM_RISCV: + /* + | Val | Macros | Description | + | 1 | ELFCLASS32 | riscv32 | + | 2 | ELFCLASS64 | riscv64 | + */ + if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { + uelf->arch = RISCV64; + } + break; default: ERROR("Unsupported architecture"); } diff --git a/upatch-diff/upatch-elf.h b/upatch-diff/upatch-elf.h index 858cfa92..d1edbf8b 100644 --- a/upatch-diff/upatch-elf.h +++ b/upatch-diff/upatch-elf.h @@ -41,6 +41,7 @@ struct symbol; enum architecture { X86_64 = 0x1 << 0, AARCH64 = 0x1 << 1, + RISCV64 = 0x1 << 2, }; enum data_source { @@ -149,4 +150,4 @@ struct string { void uelf_open(struct upatch_elf *uelf, const char *name); void uelf_close(struct upatch_elf *uelf); -#endif +#endif \ No newline at end of file diff --git a/upatch-manage/arch/riscv64/insn.h b/upatch-manage/arch/riscv64/insn.h new file mode 100644 index 00000000..8ab4daf7 --- /dev/null +++ b/upatch-manage/arch/riscv64/insn.h @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 laokz + */ + +#ifndef _ARCH_RISCV64_INSN_H +#define _ARCH_RISCV64_INSN_H + +static inline unsigned set_utype_imm(unsigned ins, unsigned long imm) +{ + /* + imm[11] to counteract lo12 sign extension in next instruction */ + unsigned long temp_imm = imm; + temp_imm += (temp_imm & 0x800) << 1; + return (temp_imm & 0xfffff000) | (ins & 0xfff); +} + +static inline unsigned set_itype_imm(unsigned ins, unsigned long imm) +{ + return ((imm & 0xfff) << 20) | (ins & 0xfffff); +} + +static inline unsigned set_stype_imm(unsigned ins, unsigned long imm) +{ + /* rs2 rs1 func opcode + ins: imm[11-8,7-5] 1,1111, 1111,1 111, imm[4-1,0] 111,1111 + ins mask 0 1 f f f 0 7 f + + imm bit no. 11-----5 4---0 + 1111,111 1,1111 + imm mask fe0 1f + + ==>imm bit no. 31----25 11--7 + */ + return (ins & 0x1fff07f) | + ((imm & 0xfe0) << (31 - 11)) | ((imm & 0x1f) << (11 - 4)); +} + +static inline unsigned set_jtype_imm(unsigned ins, unsigned long imm) +{ + /* + imm bit no. 20 19------12 11 10---------1 + 1, 1111,1111, 1 111,1111,111 0 + mask 100000 ff000 800 7fe + + ==>imm bit no. 31 19------12 20 30---------21 + */ + return (ins & 0xfff) | + ((imm & 0x100000) << (31 - 20)) | (imm & 0xff000) | + ((imm & 0x800) << (20 - 11)) | ((imm & 0x7fe) << (30 - 10)); +} + +static inline unsigned set_btype_imm(unsigned ins, unsigned long imm) +{ + /* rs2 rs1 func opcode + ins: imm[12 10-8,7-5] 1,1111, 1111,1 111, imm[4-1,11] 111,1111 + ins mask 0 1 f f f 0 7 f + + imm bit no. 12 11 10----5 4--1 + 1, 1 111,111 1,111 0 + imm mask 1000 800 7e0 1e + + ==>imm bit no. 31 7 30---25 11-8 + */ + return (ins & 0x01fff07f) | + ((imm & 0x1000) << (31 - 12)) | ((imm & 0x800) >> (11 - 7)) | + ((imm & 0x7e0) << (30 - 10)) | ((imm & 0x1e) << (11 - 4)); +} + +static inline unsigned short set_cjtype_imm(unsigned short ins, unsigned long imm) +{ + /* funct3 imm opcode + ins: 111 offset[11,4 9 8 10, 6 7 3 2, 1 5] 11 + ins mask e 0 0 3 + + imm bit no. 11 10 9-8 7 6 5 4 3-1 + 1 1 11, 1 1 1 1, 111 0 + imm mask 800 400 300 80 40 20 10 e + + ==>imm bit no. 12 8 10-9 6 7 2 11 5-3 + */ + return (ins & 0xe003) | + ((imm & 0x800) << (12 - 11)) | ((imm & 0x400) >> (10 - 8)) | + ((imm & 0x300) << (10 - 9)) | ((imm & 0x80) >> (7 - 6)) | + ((imm & 0x40) << (7 - 6)) | ((imm & 0x20) >> (5 - 2)) | + ((imm & 0x10) << (11 - 4)) | ((imm & 0xe) << (5 - 3)); +} + +/* only support C.LUI */ +static inline unsigned short set_citype_imm(unsigned short ins, unsigned long imm) +{ + /* funct3 imm[17] rd imm[16:12] opcode + ins: 111 imm[17], 1111,1 imm[16-14,13-12] 11 + ins mask e f 8 3 + + imm bit no. 17 16--12 + 1 1,1111 + imm mask 20000 1f000 + + ==>imm bit no. 12 6---2 + */ + return (ins & 0xef83) | + ((imm & 0x20000) >> (17 - 12)) | ((imm & 0x1f000) >> (16 - 6)); +} + +/* only support C.BEQZ C.BNEZ */ +static inline unsigned short set_cbtype_imm(unsigned short ins, unsigned long imm) +{ + /* funct3 imm[8 4 3] rs imm[7 6 2 1 5] opcode + ins: 111 0,0 0 11,1 0 0 0,0 0 11 + ins mask e 3 8 3 + + imm bit no. 8 , 7 6 5 4,3 2 1 0 + imm mask 100 c0 20 18 6 + + ==>imm bit no. 12 6-5 2 11-10 4-3 + */ + return (ins & 0xe383) | + ((imm & 0x100) << (12 - 8)) | ((imm & 0xc0) >> (7 - 6)) | + ((imm & 0x20) >> (5 - 2)) | ((imm & 0x18) << (11 - 4)) | + ((imm & 0x6) << (4 - 2)); +} + +#endif diff --git a/upatch-manage/arch/riscv64/process.h b/upatch-manage/arch/riscv64/process.h new file mode 100644 index 00000000..91926fa2 --- /dev/null +++ b/upatch-manage/arch/riscv64/process.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * upatch-manage + * Copyright (C) 2024 ISCAS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __PROCESS__ +#define __PROCESS__ + +#ifndef MAX_DISTANCE +#define MAX_DISTANCE 0x80000000 +#endif + +#endif diff --git a/upatch-manage/arch/riscv64/ptrace.c b/upatch-manage/arch/riscv64/ptrace.c new file mode 100644 index 00000000..c5691c94 --- /dev/null +++ b/upatch-manage/arch/riscv64/ptrace.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * upatch-manage + * Copyright (C) 2024 ISCAS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include + +#include "upatch-ptrace.h" + +int upatch_arch_reg_init(int pid, unsigned long *sp, unsigned long *pc) +{ + struct iovec regs_iov; + struct user_regs_struct regs; + + regs_iov.iov_base = ®s; + regs_iov.iov_len = sizeof(regs); + + if (ptrace(PTRACE_GETREGSET, pid, + (void *)NT_PRSTATUS, (void *)®s_iov) < 0) { + log_error("Cannot get regs from %d\n", pid); + return -1; + } + *sp = (unsigned long)regs.sp; + *pc = (unsigned long)regs.pc; + return 0; +} + +static long read_gregs(int pid, struct user_regs_struct *regs) +{ + struct iovec data = {regs, sizeof(*regs)}; + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &data) == -1) { + log_error("ptrace(PTRACE_GETREGSET)"); + return -1; + } + return 0; +} + +static long write_gregs(int pid, struct user_regs_struct *regs) +{ + struct iovec data = {regs, sizeof(*regs)}; + if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &data) == -1) { + log_error("ptrace(PTRACE_SETREGSET)"); + return -1; + } + return 0; +} + +long upatch_arch_syscall_remote(struct upatch_ptrace_ctx *pctx, int nr, + unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + unsigned long arg5, unsigned long arg6, + unsigned long *res) +{ + struct user_regs_struct regs; + unsigned char syscall[] = { + 0x73, 0x00, 0x00, 0x00, // ecall + 0x73, 0x00, 0x10, 0x00, // ebreak + }; + long ret; + + log_debug("Executing syscall %d (pid %d)...\n", nr, pctx->pid); + regs.a7 = (unsigned long)nr; + regs.a0 = arg1; + regs.a1 = arg2; + regs.a2 = arg3; + regs.a3 = arg4; + regs.a4 = arg5; + regs.a5 = arg6; + + ret = upatch_execute_remote(pctx, syscall, sizeof(syscall), ®s); + if (ret == 0) + *res = regs.a0; + + return ret; +} + +long upatch_arch_execute_remote_func(struct upatch_ptrace_ctx *pctx, + const unsigned char *code, size_t codelen, + struct user_regs_struct *pregs, + int (*func)(struct upatch_ptrace_ctx *pctx, + const void *data), + const void *data) +{ + struct user_regs_struct orig_regs, regs; + unsigned char orig_code[codelen]; + long ret; + struct upatch_process *proc = pctx->proc; + unsigned long libc_base = proc->libc_base; + + ret = read_gregs(pctx->pid, &orig_regs); + if (ret < 0) { + return -1; + } + ret = upatch_process_mem_read(proc, libc_base, + (unsigned long *)orig_code, codelen); + if (ret < 0) { + log_error("can't peek original code - %d\n", pctx->pid); + return -1; + } + ret = upatch_process_mem_write(proc, (const unsigned long *)code, libc_base, + codelen); + if (ret < 0) { + log_error("can't poke syscall code - %d\n", pctx->pid); + goto poke_back; + } + + regs = orig_regs; + regs.pc = libc_base; + + copy_regs(®s, pregs); + + ret = write_gregs(pctx->pid, ®s); + if (ret < 0) { + goto poke_back; + } + + ret = func(pctx, data); + if (ret < 0) { + log_error("failed call to func\n"); + goto poke_back; + } + + ret = read_gregs(pctx->pid, ®s); + if (ret < 0) { + goto poke_back; + } + + ret = write_gregs(pctx->pid, &orig_regs); + if (ret < 0) { + goto poke_back; + } + + *pregs = regs; + +poke_back: + upatch_process_mem_write(proc, (unsigned long *)orig_code, libc_base, + codelen); + return ret; +} + +void copy_regs(struct user_regs_struct *dst, struct user_regs_struct *src) +{ +#define COPY_REG(x) dst->x = src->x + COPY_REG(a0); + COPY_REG(a1); + COPY_REG(a2); + COPY_REG(a3); + COPY_REG(a4); + COPY_REG(a5); + COPY_REG(a6); + COPY_REG(a7); +#undef COPY_REG +} + +#define UPATCH_INSN_LEN 8 +#define UPATCH_ADDR_LEN 8 +#define ORIGIN_INSN_LEN (UPATCH_INSN_LEN + UPATCH_ADDR_LEN) + +size_t get_origin_insn_len() +{ + return ORIGIN_INSN_LEN; +} + +size_t get_upatch_insn_len() +{ + return UPATCH_INSN_LEN; +} + +size_t get_upatch_addr_len() +{ + return UPATCH_ADDR_LEN; +} + +/* + * On RISC-V, there must be 3 instructors(12 bytes) to jump to + * arbitrary address. The core upatch-manage limit jump instructor + * to one long(8 bytes), for us is +-2G range. + */ +unsigned long get_new_insn(unsigned long old_addr, unsigned long new_addr) +{ + unsigned long offset; + unsigned int insn0, insn4; + + offset = new_addr - old_addr; + offset += (offset & 0x800) << 1; + insn0 = 0xf97 | (offset & 0xfffff000); // auipc t6, off[20] + insn4 = 0xf8067 | ((offset & 0xfff) << 20); // jalr zero, off[12](t6) + return (unsigned long)(insn0 | ((unsigned long)insn4 << 32)); +} diff --git a/upatch-manage/arch/riscv64/relocation.c b/upatch-manage/arch/riscv64/relocation.c new file mode 100644 index 00000000..6c28bf8c --- /dev/null +++ b/upatch-manage/arch/riscv64/relocation.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * upatch-manage + * Copyright (C) 2024 ISCAS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include "insn.h" +#include "upatch-relocation.h" +#include "upatch-resolve.h" + +/* + * In PCREL_LO12 relocation entity, its corresponding symbol's value + * points to the ..._HI20 instruction, where the LO12 part of the + * immediate is part of the ..._HI20 symbol value. + */ +static unsigned long find_pcrel_hi_value(GElf_Rela *r, int idx, GElf_Sym *st, unsigned long v) +{ + int i = idx; + r--; + for (; i > 0; i--, r--) { + if ((r->r_offset == v) && + ((GELF_R_TYPE(r->r_info) == R_RISCV_PCREL_HI20) || + (GELF_R_TYPE(r->r_info) == R_RISCV_TLS_GOT_HI20) || + (GELF_R_TYPE(r->r_info) == R_RISCV_TLS_GD_HI20) || + (GELF_R_TYPE(r->r_info) == R_RISCV_GOT_HI20))) + return st[GELF_R_SYM(r->r_info)].st_value; + } + + /* should never happen */ + log_error("Not found no. %d rela's corresponding HI20\n", idx); + return 0; +} + +/* + * The patch is a .o file, has only static relocations, all symbols + * have been resolved with our jump table act as got/plt. + */ +int apply_relocate_add(struct upatch_elf *uelf, unsigned int symindex, + unsigned int relsec) +{ + unsigned int i; + GElf_Sym *sym, *symtab; + char const *sym_name; + unsigned long uloc_sec; + void *loc; + void *uloc; + u64 val; + GElf_Shdr *shdrs = (void *)uelf->info.shdrs; + GElf_Rela *rel = (void *)shdrs[relsec].sh_addr; + + symtab = (GElf_Sym *)shdrs[symindex].sh_addr; + for (i = 0; i < shdrs[relsec].sh_size / sizeof(*rel); i++) { + /* loc corresponds to P in the kernel space */ + loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + + /* uloc corresponds P in user space */ + uloc_sec = shdrs[shdrs[relsec].sh_info].sh_addralign; + uloc = (void *)uloc_sec + rel[i].r_offset; + + /* sym is the ELF symbol we're referring to */ + sym = symtab + GELF_R_SYM(rel[i].r_info); + if (GELF_ST_TYPE(sym->st_info) == STT_SECTION && + sym->st_shndx < uelf->info.hdr->e_shnum) + sym_name = uelf->info.shstrtab + + shdrs[sym->st_shndx].sh_name; + else + sym_name = uelf->strtab + sym->st_name; + + /* val corresponds to (S + A) */ + val = (s64)(sym->st_value + rel[i].r_addend); + log_debug( + "upatch: reloc symbol, name=%s, k_addr=0x%lx, u_addr=0x%lx, " + "r_offset=0x%lx, st_value=0x%lx, r_addend=0x%lx \n", + sym_name, shdrs[shdrs[relsec].sh_info].sh_addr, + uloc_sec, rel[i].r_offset, sym->st_value, rel[i].r_addend); + + /* Perform the static relocation. */ + switch (GELF_R_TYPE(rel[i].r_info)) { + case R_RISCV_NONE: + case R_RISCV_TPREL_ADD: + break; + + case R_RISCV_64: + *(unsigned long *)loc = val; + break; + + /* seems no need to recalculate as it should confined in the same func */ + case R_RISCV_BRANCH: + val -= (unsigned long)uloc; + if ((signed)val >= 4096 || (signed)val < -4096) + goto overflow; + *(unsigned *)loc = set_btype_imm(*(unsigned *)loc, val); + break; + + case R_RISCV_JAL: + val -= (unsigned long)uloc; + if ((signed)val >= (1 << 20) || (signed)val < -(1 << 20)) + goto overflow; + *(unsigned *)loc = set_jtype_imm(*(unsigned *)loc, val); + break; + + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: + // in our jump table, must not overflow + val -= (unsigned long)uloc; + *(unsigned *)loc = set_utype_imm(*(unsigned *)loc, val); + *(unsigned *)(loc + 4) = set_itype_imm(*(unsigned *)(loc + 4), val); + break; + + case R_RISCV_GOT_HI20: + case R_RISCV_TLS_GOT_HI20: + case R_RISCV_TLS_GD_HI20: + case R_RISCV_PCREL_HI20: + val -= (unsigned long)uloc; // fall through + case R_RISCV_HI20: + case R_RISCV_TPREL_HI20: + if ((long)val != (long)(int)val) + goto overflow; + *(unsigned *)loc = set_utype_imm(*(unsigned *)loc, val); + break; + + case R_RISCV_PCREL_LO12_I: + val = find_pcrel_hi_value(rel + i, i, symtab, sym->st_value - uloc_sec); + if (val == 0) + goto overflow; + val -= sym->st_value; // fall through + case R_RISCV_LO12_I: + case R_RISCV_TPREL_LO12_I: + *(unsigned *)loc = set_itype_imm(*(unsigned *)loc, val); + break; + + case R_RISCV_PCREL_LO12_S: + val = find_pcrel_hi_value(rel + i, i, symtab, sym->st_value - uloc_sec); + if (val == 0) + goto overflow; + val -= sym->st_value; // fall through + case R_RISCV_LO12_S: + case R_RISCV_TPREL_LO12_S: + *(unsigned *)loc = set_stype_imm(*(unsigned *)loc, val); + break; + + /* inner function label calculation, must not overflow */ + case R_RISCV_ADD8: + *(char *)loc += val; + break; + case R_RISCV_ADD16: + *(short *)loc += val; + break; + case R_RISCV_ADD32: + *(int *)loc += val; + break; + case R_RISCV_ADD64: + *(long *)loc += val; + break; + + case R_RISCV_SUB8: + *(char *)loc -= val; + break; + case R_RISCV_SUB16: + *(short *)loc -= val; + break; + case R_RISCV_SUB32: + *(int *)loc -= val; + break; + case R_RISCV_SUB64: + *(long *)loc -= val; + break; + + case R_RISCV_RVC_BRANCH: + val -= (unsigned long)uloc; + if ((signed)val >= 256 || (signed)val < -256) + goto overflow; + *(unsigned short *)loc = set_cbtype_imm(*(unsigned short *)loc, val); + break; + + case R_RISCV_RVC_JUMP: + val -= (unsigned long)uloc; + if ((signed)val >= 2048 || (signed)val < -2048) + goto overflow; + *(unsigned short *)loc = set_cjtype_imm(*(unsigned short *)loc, val); + break; + + case R_RISCV_RVC_LUI: + if ((signed)val >= (1 << 17) || (signed)val < -(1 << 17) || (val & 0x3f000) == 0) + goto overflow; + *(unsigned short *)loc = set_citype_imm(*(unsigned short *)loc, val); + break; + + case R_RISCV_SET8: + *(char *)loc = val; + break; + case R_RISCV_SET16: + *(short *)loc = val; + break; + case R_RISCV_32_PCREL: + case R_RISCV_PLT32: + val -= (unsigned long)uloc; // fall through + case R_RISCV_32: + case R_RISCV_SET32: + if ((long)val != (long)(int)val) + goto overflow; + *(int *)loc = val; + break; + + case R_RISCV_SUB6: + char w6 = (*(char *)loc - (char)val) & 0x3f; + *(char *)loc = (*(char *)loc & 0xc0) | w6; + break; + case R_RISCV_SET6: + *(char *)loc = (*(char *)loc & 0xc0) | (val & 0x3f); + break; + + default: + log_error("upatch: unsupported RELA relocation: %lu\n", + GELF_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; + +overflow: + log_error("upatch: overflow in relocation type %d val %lx\n", + (int)GELF_R_TYPE(rel[i].r_info), val); + return -ENOEXEC; +} diff --git a/upatch-manage/arch/riscv64/resolve.c b/upatch-manage/arch/riscv64/resolve.c new file mode 100644 index 00000000..127fcc00 --- /dev/null +++ b/upatch-manage/arch/riscv64/resolve.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * upatch-manage + * Copyright (C) 2024 ISCAS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +#include "log.h" +#include "upatch-ptrace.h" +#include "upatch-resolve.h" + +/* + * auipc t6,0x0 + * ld t6,16(t6) # addr + * jr t6 + * undefined + */ +#define RISCV64_JMP_TABLE_JUMP0 0x010fbf8300000f97 +#define RISCV64_JMP_TABLE_JUMP1 0x000f8067 + +struct upatch_jmp_table_entry { + unsigned long inst[2]; + unsigned long addr[2]; +}; + +unsigned int get_jmp_table_entry() +{ + return sizeof(struct upatch_jmp_table_entry); +} + +static unsigned long setup_jmp_table(struct upatch_elf *uelf, + unsigned long jmp_addr, + unsigned long origin_addr) +{ + struct upatch_jmp_table_entry *table = + uelf->core_layout.kbase + uelf->jmp_offs; + unsigned int index = uelf->jmp_cur_entry; + if (index >= uelf->jmp_max_entry) { + log_error("jmp table overflow\n"); + return 0; + } + + table[index].inst[0] = RISCV64_JMP_TABLE_JUMP0; + table[index].inst[1] = RISCV64_JMP_TABLE_JUMP1; + table[index].addr[0] = jmp_addr; + table[index].addr[1] = origin_addr; + uelf->jmp_cur_entry++; + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + index * sizeof(struct upatch_jmp_table_entry)); +} + +static unsigned long setup_got_table(struct upatch_elf *uelf, + unsigned long jmp_addr, + unsigned long tls_addr) +{ + struct upatch_jmp_table_entry *table = + uelf->core_layout.kbase + uelf->jmp_offs; + unsigned int index = uelf->jmp_cur_entry; + + if (index >= uelf->jmp_max_entry) { + log_error("got table overflow\n"); + return 0; + } + + table[index].inst[0] = jmp_addr; + table[index].inst[1] = tls_addr; + table[index].addr[0] = 0xffffffff; + table[index].addr[1] = 0xffffffff; + uelf->jmp_cur_entry++; + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + index * sizeof(struct upatch_jmp_table_entry)); +} + +unsigned long insert_plt_table(struct upatch_elf *uelf, struct object_file *obj, + unsigned long r_type, unsigned long addr) +{ + unsigned long jmp_addr = 0xffffffff; + unsigned long tls_addr = 0xffffffff; + unsigned long elf_addr = 0; + + if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, + sizeof(jmp_addr))) { + log_error("copy address failed\n"); + goto out; + } + + elf_addr = setup_jmp_table(uelf, jmp_addr, (unsigned long)addr); + + log_debug("0x%lx: jmp_addr=0x%lx, tls_addr=0x%lx\n", elf_addr, + jmp_addr, tls_addr); + +out: + return elf_addr; +} + +unsigned long insert_got_table(struct upatch_elf *uelf, struct object_file *obj, + unsigned long r_type, unsigned long addr) +{ + unsigned long jmp_addr = 0xffffffff; + unsigned long tls_addr = 0xffffffff; + unsigned long elf_addr = 0; + + if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, + sizeof(jmp_addr))) { + log_error("copy address failed\n"); + goto out; + } + + /* + * Addr with this type means the symbol is a dynamic TLS variable. + * Addr points to a GOT entry(16 bytes) having type + * + * typedef struct { + * unsigned long int ti_module; + * unsigned long int ti_offset; + * } tls_index; + * + * We also need copy ti_offset to our jump table. + * + * The corresponding symbol will associate with TLS_GD_HI20 + * relocation type, using this tls_index as argument to call + * `void *__tls_get_addr (tls_index *ti)` to resolve the real address. + */ + if (r_type == R_RISCV_TLS_DTPMOD64 && + upatch_process_mem_read(obj->proc, addr + sizeof(unsigned long), + &tls_addr, sizeof(tls_addr))) { + log_error("copy address failed\n"); + goto out; + } + + elf_addr = setup_got_table(uelf, jmp_addr, tls_addr); + + log_debug("0x%lx: jmp_addr=0x%lx, tls_addr=0x%lx\n", elf_addr, + jmp_addr, tls_addr); + +out: + return elf_addr; +} diff --git a/upatch-manage/upatch-patch.c b/upatch-manage/upatch-patch.c index 4b1b1be9..c126dcdc 100644 --- a/upatch-manage/upatch-patch.c +++ b/upatch-manage/upatch-patch.c @@ -475,6 +475,23 @@ static int complete_info(struct upatch_elf *uelf, struct object_file *obj, upatch_func->addr = upatch_funcs_addr[i].addr; upatch_func->addr.old_addr += uelf->relf->load_bias; + +#ifdef __riscv +#define RISCV_MAX_JUMP_RANGE (1L << 31) + /* + * On RISC-V, to jump to arbitrary address, there must be + * at least 12 bytes to hold 3 instructors. Struct upatch_info + * new_insn field is only 8 bytes. We can only jump into + * +-2G ranges. Here do the check. + */ + long offset = upatch_func->addr.new_addr - upatch_func->addr.old_addr; + if (offset >= RISCV_MAX_JUMP_RANGE || offset < -RISCV_MAX_JUMP_RANGE) { + log_error("new_addr=%lx old_addr=%lx exceed +-2G range\n", + upatch_func->addr.new_addr, upatch_func->addr.old_addr); + goto out; + } +#endif + ret = upatch_process_mem_read(obj->proc, upatch_func->addr.old_addr, &upatch_func->old_insn, get_origin_insn_len()); if (ret) { @@ -483,7 +500,12 @@ static int complete_info(struct upatch_elf *uelf, struct object_file *obj, goto out; } +#ifdef __riscv + upatch_func->new_insn = get_new_insn( + upatch_func->addr.old_addr, upatch_func->addr.new_addr); +#else upatch_func->new_insn = get_new_insn(); +#endif log_debug("\taddr: 0x%lx -> 0x%lx, insn: 0x%lx -> 0x%lx, name: '%s'\n", upatch_func->addr.old_addr, upatch_func->addr.new_addr, upatch_func->old_insn[0], upatch_func->new_insn, diff --git a/upatch-manage/upatch-ptrace.c b/upatch-manage/upatch-ptrace.c index ab21891d..4ebe0380 100644 --- a/upatch-manage/upatch-ptrace.c +++ b/upatch-manage/upatch-ptrace.c @@ -25,6 +25,10 @@ #include #include +#ifdef __riscv +/* user_regs_struct defined here */ +#include +#endif #include #include diff --git a/upatch-manage/upatch-ptrace.h b/upatch-manage/upatch-ptrace.h index 8f36565d..f2503cfd 100644 --- a/upatch-manage/upatch-ptrace.h +++ b/upatch-manage/upatch-ptrace.h @@ -22,6 +22,9 @@ #define __UPATCH_PTRACE__ #include +#ifdef __riscv +#include +#endif #include "upatch-process.h" #include "list.h" @@ -79,6 +82,11 @@ long upatch_execute_remote(struct upatch_ptrace_ctx *, size_t get_origin_insn_len(void); size_t get_upatch_insn_len(void); size_t get_upatch_addr_len(void); + +#ifdef __riscv +unsigned long get_new_insn(unsigned long old_addr, unsigned long new_addr); +#else unsigned long get_new_insn(void); +#endif #endif -- Gitee From ac746002b9da0863994541d2fa9e63b42d38e876 Mon Sep 17 00:00:00 2001 From: Jvle Date: Wed, 23 Apr 2025 17:04:48 +0800 Subject: [PATCH 2/5] upatch-diff: fix '.L' correlation for riscv64 Signed-off-by: Jvle --- upatch-diff/elf-correlate.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/upatch-diff/elf-correlate.c b/upatch-diff/elf-correlate.c index dcf3cfa5..8e2f7659 100644 --- a/upatch-diff/elf-correlate.c +++ b/upatch-diff/elf-correlate.c @@ -83,6 +83,12 @@ void upatch_correlate_symbols(struct upatch_elf *uelf_source, continue; } + /* on RISC-V: .L symbols should not change section */ + if (uelf_source->arch == RISCV64 && !strncmp(sym_orig->name, ".L", 2) && + sym_orig->sec && sym_orig->sec->twin != sym_patched->sec) { + continue; + } + if (is_mapping_symbol(uelf_source, sym_orig)) { continue; } -- Gitee From a554b5bf36b5f58df9a7b93927806d4ee40af181 Mon Sep 17 00:00:00 2001 From: Jvle Date: Wed, 23 Apr 2025 17:05:32 +0800 Subject: [PATCH 3/5] upatch-diff: fix Werrors for riscv64 Signed-off-by: Jvle --- upatch-diff/elf-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upatch-diff/elf-common.c b/upatch-diff/elf-common.c index eb0d0738..23665bb0 100644 --- a/upatch-diff/elf-common.c +++ b/upatch-diff/elf-common.c @@ -135,7 +135,7 @@ static int mangled_strcmp_dot_L(char *str1, char *str2) char *q = str2 + 2; while (*p < '0' || *p > '9') p++; while (*q < '0' || *q > '9') q++; - if ((p - str1 != q - str2) || strncmp(str1, str2, p - str1)) { + if ((p - str1 != q - str2) || strncmp(str1, str2, (size_t)(p - str1))) { return 1; } -- Gitee From 4e88e3c1e2c026ca37d1142bb3cf38e6e0f7fbe1 Mon Sep 17 00:00:00 2001 From: Jvle Date: Wed, 23 Apr 2025 17:07:36 +0800 Subject: [PATCH 4/5] upatch-manage: fix Werrors for riscv64 Signed-off-by: Jvle --- upatch-manage/arch/riscv64/ptrace.c | 14 +++++++++++++- upatch-manage/arch/riscv64/relocation.c | 4 ++++ upatch-manage/arch/riscv64/resolve.c | 8 ++++---- upatch-manage/upatch-patch.c | 4 ++-- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/upatch-manage/arch/riscv64/ptrace.c b/upatch-manage/arch/riscv64/ptrace.c index c5691c94..2ccdc9ef 100644 --- a/upatch-manage/arch/riscv64/ptrace.c +++ b/upatch-manage/arch/riscv64/ptrace.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "upatch-ptrace.h" @@ -100,19 +101,29 @@ long upatch_arch_execute_remote_func(struct upatch_ptrace_ctx *pctx, const void *data) { struct user_regs_struct orig_regs, regs; - unsigned char orig_code[codelen]; + if (!codelen) { + log_error("Invalid codelen\n"); + return -1; + } + unsigned char *orig_code = (unsigned char *)malloc(sizeof(*orig_code) * codelen); + if (orig_code == NULL) { + log_error("Malloc orig_code failed\n"); + return -1; + } long ret; struct upatch_process *proc = pctx->proc; unsigned long libc_base = proc->libc_base; ret = read_gregs(pctx->pid, &orig_regs); if (ret < 0) { + free(orig_code); return -1; } ret = upatch_process_mem_read(proc, libc_base, (unsigned long *)orig_code, codelen); if (ret < 0) { log_error("can't peek original code - %d\n", pctx->pid); + free(orig_code); return -1; } ret = upatch_process_mem_write(proc, (const unsigned long *)code, libc_base, @@ -153,6 +164,7 @@ long upatch_arch_execute_remote_func(struct upatch_ptrace_ctx *pctx, poke_back: upatch_process_mem_write(proc, (unsigned long *)orig_code, libc_base, codelen); + free(orig_code); return ret; } diff --git a/upatch-manage/arch/riscv64/relocation.c b/upatch-manage/arch/riscv64/relocation.c index 6c28bf8c..ba825992 100644 --- a/upatch-manage/arch/riscv64/relocation.c +++ b/upatch-manage/arch/riscv64/relocation.c @@ -51,6 +51,9 @@ static unsigned long find_pcrel_hi_value(GElf_Rela *r, int idx, GElf_Sym *st, un * The patch is a .o file, has only static relocations, all symbols * have been resolved with our jump table act as got/plt. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wsign-conversion" int apply_relocate_add(struct upatch_elf *uelf, unsigned int symindex, unsigned int relsec) { @@ -240,3 +243,4 @@ overflow: (int)GELF_R_TYPE(rel[i].r_info), val); return -ENOEXEC; } +#pragma GCC diagnostic pop diff --git a/upatch-manage/arch/riscv64/resolve.c b/upatch-manage/arch/riscv64/resolve.c index 127fcc00..0b4aa8ba 100644 --- a/upatch-manage/arch/riscv64/resolve.c +++ b/upatch-manage/arch/riscv64/resolve.c @@ -65,9 +65,9 @@ static unsigned long setup_jmp_table(struct upatch_elf *uelf, index * sizeof(struct upatch_jmp_table_entry)); } -static unsigned long setup_got_table(struct upatch_elf *uelf, - unsigned long jmp_addr, - unsigned long tls_addr) +unsigned long setup_got_table(struct upatch_elf *uelf, + unsigned long jmp_addr, + unsigned long tls_addr) { struct upatch_jmp_table_entry *table = uelf->core_layout.kbase + uelf->jmp_offs; @@ -88,7 +88,7 @@ static unsigned long setup_got_table(struct upatch_elf *uelf, } unsigned long insert_plt_table(struct upatch_elf *uelf, struct object_file *obj, - unsigned long r_type, unsigned long addr) + unsigned long r_type __attribute__((unused)), unsigned long addr) { unsigned long jmp_addr = 0xffffffff; unsigned long tls_addr = 0xffffffff; diff --git a/upatch-manage/upatch-patch.c b/upatch-manage/upatch-patch.c index c126dcdc..fd2c5f22 100644 --- a/upatch-manage/upatch-patch.c +++ b/upatch-manage/upatch-patch.c @@ -484,8 +484,8 @@ static int complete_info(struct upatch_elf *uelf, struct object_file *obj, * new_insn field is only 8 bytes. We can only jump into * +-2G ranges. Here do the check. */ - long offset = upatch_func->addr.new_addr - upatch_func->addr.old_addr; - if (offset >= RISCV_MAX_JUMP_RANGE || offset < -RISCV_MAX_JUMP_RANGE) { + long riscv_offset = (long)(upatch_func->addr.new_addr - upatch_func->addr.old_addr); + if (riscv_offset >= RISCV_MAX_JUMP_RANGE || riscv_offset < -RISCV_MAX_JUMP_RANGE) { log_error("new_addr=%lx old_addr=%lx exceed +-2G range\n", upatch_func->addr.new_addr, upatch_func->addr.old_addr); goto out; -- Gitee From 5f31999054b910dd6b930fff5bdc72a43d501b7a Mon Sep 17 00:00:00 2001 From: Jvle Date: Wed, 23 Apr 2025 17:08:09 +0800 Subject: [PATCH 5/5] CMake: fix -Werror=cast-align for riscv64 Signed-off-by: Jvle --- CMakeLists.txt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d8b3051..95a917f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,11 +62,23 @@ list(APPEND PROJECT_C_BUILD_FLAGS -DBUILD_VERSION="${PROJECT_BUILD_VERSION}" -D_FORTIFY_SOURCE=2 -Wtrampolines -Wformat=2 -Wstrict-prototypes -Wdate-time -Wstack-usage=8192 -Wfloat-equal -Wswitch-default - -Wshadow -Wconversion -Wcast-qual -Wcast-align -Wunused -Wundef + -Wshadow -Wconversion -Wcast-qual -Wunused -Wundef -funsigned-char -fstack-protector-all -fpic -fpie -ftrapv -fstack-check -freg-struct-return -fno-canonical-system-headers -fno-common -pipe -fdebug-prefix-map=old=new ) + +# The -Werror=cast-align compiler flag causes issues on riscv64 GCC, +# while the same operations do not error on aarch64. This appears to be +# a compiler-specific problem. Temporarily disable this option as a +# workaround since applying fixes would require intrusive code changes +# across multiple files. +if(NOT ARCH STREQUAL "riscv64") + list(APPEND PROJECT_C_BUILD_FLAGS + -Wcast-align + ) +endif() + list(APPEND PROJECT_RUST_FLAGS --cfg unsound_local_offset -D warnings -- Gitee