From bc35811015ab4d0462a79c378ce35f0a11b6c450 Mon Sep 17 00:00:00 2001 From: Inversewing Date: Wed, 23 Apr 2025 10:48:24 +0800 Subject: [PATCH] backport riscv64 to master --- 0012-backport-riscv64-to-master.patch | 9179 +++++++++++++++++++++++++ syscare.spec | 6 +- 2 files changed, 9184 insertions(+), 1 deletion(-) create mode 100755 0012-backport-riscv64-to-master.patch diff --git a/0012-backport-riscv64-to-master.patch b/0012-backport-riscv64-to-master.patch new file mode 100755 index 0000000..297a0d8 --- /dev/null +++ b/0012-backport-riscv64-to-master.patch @@ -0,0 +1,9179 @@ +diff --git a/README.md b/README.md +index 15d4d4899bd5c36f0838cd58f7f295eceea445be..b34a6c17cd1146e1971fe6b3a9a7b7f2d6456400 100644 +--- a/README.md ++++ b/README.md +@@ -33,6 +33,8 @@ + + * 编译并安装 + ++ PS: 直接编译在应用补丁的时候会显示缺少依赖,建议通过rpm包安装 ++ + ```bash + git clone https://gitee.com/openeuler/syscare.git + cd syscare +@@ -41,6 +43,11 @@ + cmake -DCMAKE_INSTALL_PREFIX=/usr -DKERNEL_VERSION=$(uname -r) .. + make + make install ++ ++ mkdir -p /usr/lib/syscare/patches ++ systemctl daemon-reload ++ systemctl enable syscare ++ systemctl start syscare + ``` + + * 离线编译 +@@ -63,7 +70,11 @@ + ```bash + rpm -ivh syscare-*.rpm + ``` ++或者 + ++``` ++dnf install syscare-*.rpm ++``` + + + ## 使用说明 +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 0eea36246270ade72da2b155700ec72e8656b957..426f5e14df750b37bdb2fb6e76a88c19b7b51375 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -582,7 +582,9 @@ static void replace_section_syms(struct upatch_elf *uelf) + /* text section refer other sections */ + if (is_text_section(relasec->base) && + !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_X86_64_32S || rela->type == R_X86_64_32 || ++ 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."); +@@ -718,8 +720,17 @@ static void include_symbol(struct symbol *sym) + * For section symbols, we always include the section because + * references to them can't otherwise be resolved externally. + */ +- if (sym->sec && (sym->type == STT_SECTION || sym->status != SAME)) ++ if (sym->sec && (sym->type == STT_SECTION || sym->status != SAME)) { + 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) +@@ -899,6 +910,22 @@ 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) + { + struct section *sec, *safesec; +@@ -919,8 +946,14 @@ static void migrate_included_elements(struct upatch_elf *uelf_patched, struct up + list_del(&sec->list); + list_add_tail(&sec->list, &uelf_out->sections); + sec->index = 0; +- if (!is_rela_section(sec) && sec->secsym && !sec->secsym->include) +- sec->secsym = NULL; // break link to non-included section symbol ++ if (!is_rela_section(sec)) { ++ if (sec->secsym && !sec->secsym->include) { ++ // break link to non-included section symbol ++ sec->secsym = NULL; ++ } ++ } else if (uelf_patched->arch == RISCV64) { ++ rv_drop_useless_rela(sec); ++ } + } + + /* migrate included symbols from kelf to out */ +diff --git a/upatch-diff/elf-common.c b/upatch-diff/elf-common.c +index a74da3aa45b0819b1463b04961be32532d936cc5..6f574c76e24bc0b52ec30b680f3f4afbfbae5086 100644 +--- a/upatch-diff/elf-common.c ++++ b/upatch-diff/elf-common.c +@@ -26,100 +26,146 @@ + + #include "elf-common.h" + ++#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) + { + /* +- * ELF string sections aren't mangled, though they look that way. +- */ +- if (strstr(str1, ".str1.")) +- return strcmp(str1, str2); ++ * ELF string sections aren't mangled, though they look that way. ++ */ ++ if (strstr(str1, ".str1.")) { ++ 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; ++ while (*str1 == *str2) { ++ if (!*str2) { ++ return 0; ++ } + + // format like ".[0-9]" +- if (*str1 == '.' && isdigit(str1[1])) { +- if (!isdigit(str2[1])) +- return 1; +- while (isdigit(*++str1)) +- ; +- while (isdigit(*++str2)) +- ; +- } else { +- str1++; +- str2++; +- } +- } +- +- if ((!*str1 && has_digit_tail(str2)) || +- (!*str2 && has_digit_tail(str1))) +- return 0; +- +- return 1; ++ if (*str1 == '.' && isdigit(str1[1])) { ++ if (!isdigit(str2[1])) { ++ return 1; ++ } ++ while (isdigit(*++str1)) { ++ // Empty loop body ++ } ++ while (isdigit(*++str2)) { ++ // Empty loop body ++ } ++ } else { ++ str1++; ++ str2++; ++ } ++ } ++ ++ if ((!*str1 && has_digit_tail(str2)) || ++ (!*str2 && has_digit_tail(str1))) { ++ return 0; ++ } ++ ++ return 1; + } + + bool is_normal_static_local(struct symbol *sym) + { + // only handle local variable +- if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL) +- return false; ++ if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL) { ++ return false; ++ } + + // TODO: .Local ? need a example here +- if (!strncmp(sym->name, ".L", 2)) { ++ if (!strncmp(sym->name, ".L", 2)) { + ERROR("find no-local variable \n"); +- return false; ++ return false; + } + +- if (!strchr(sym->name, '.')) +- return false; ++ if (!strchr(sym->name, '.')) { ++ return false; ++ } + +- /* +- * TODO: Special static local variables should never be correlated and should always +- * be included if they are referenced by an included function. +- */ ++ /* ++ * TODO: Special static local variables should never be correlated and should always ++ * be included if they are referenced by an included function. ++ */ + +- return true; ++ return true; + } + + int offset_of_string(struct list_head *list, char *name) + { +- struct string *string; +- int index = 0; +- +- list_for_each_entry(string, list, list) { +- if (!strcmp(string->name, name)) +- return index; +- index += (int)strlen(string->name) + 1; +- } +- +- ALLOC_LINK(string, list); +- string->name = name; +- return index; ++ struct string *string; ++ int index = 0; ++ ++ list_for_each_entry(string, list, list) { ++ if (!strcmp(string->name, name)) { ++ return index; ++ } ++ index += (int)strlen(string->name) + 1; ++ } ++ ++ ALLOC_LINK(string, list); ++ string->name = name; ++ return index; + } + + // no need for X86 + bool is_gcc6_localentry_bundled_sym(struct upatch_elf *uelf) + { +- switch(uelf->arch) { +- case AARCH64: +- return false; +- case X86_64: +- return false; +- default: +- ERROR("unsupported arch"); +- } +- return false; ++ switch (uelf->arch) { ++ case RISCV64: ++ case AARCH64: ++ return false; ++ case X86_64: ++ return false; ++ default: ++ ERROR("unsupported arch"); ++ } ++ return false; + } + + bool is_mapping_symbol(struct upatch_elf *uelf, struct symbol *sym) + { +- if (uelf->arch != AARCH64) +- return false; +- +- if (sym->name && sym->name[0] == '$' +- && sym->type == STT_NOTYPE +- && sym->bind == STB_LOCAL) +- return true; +- return false; ++ if ((uelf->arch != AARCH64) && (uelf->arch != RISCV64)) { ++ return false; ++ } ++ ++ if (sym->name && sym->name[0] == '$' && ++ sym->type == STT_NOTYPE && ++ sym->bind == STB_LOCAL) { ++ return true; ++ } ++ return false; + } +diff --git a/upatch-diff/elf-common.h b/upatch-diff/elf-common.h +index f3d430867e1801dbb4778860409380dd58bc817b..1598e6a84443dd25b410e6930efde446ca962105 100644 +--- a/upatch-diff/elf-common.h ++++ b/upatch-diff/elf-common.h +@@ -186,14 +186,14 @@ static inline char *section_function_name(struct section *sec) + static inline char *status_str(enum status status) + { + switch(status) { +- case NEW: +- return "NEW"; +- case CHANGED: +- return "CHANGED"; +- case SAME: +- return "SAME"; +- default: +- ERROR("status_str"); ++ case NEW: ++ return "NEW"; ++ case CHANGED: ++ return "CHANGED"; ++ case SAME: ++ return "SAME"; ++ default: ++ ERROR("status_str"); + } + return NULL; + } +@@ -203,12 +203,14 @@ int offset_of_string(struct list_head *, char *); + static inline unsigned int absolute_rela_type(struct upatch_elf *uelf) + { + switch(uelf->arch) { +- case AARCH64: +- return R_AARCH64_ABS64; +- case X86_64: +- return R_X86_64_64; +- default: +- ERROR("unsupported arch"); ++ case AARCH64: ++ return R_AARCH64_ABS64; ++ case X86_64: ++ return R_X86_64_64; ++ case RISCV64: ++ return R_RISCV_64; ++ default: ++ ERROR("unsupported arch"); + } + return 0; + } +diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c +index 5d398250b490aff076b58b17112be8203049c545..38b48d6d70eac5e0d411566a2e35155c6762c9dc 100644 +--- a/upatch-diff/elf-compare.c ++++ b/upatch-diff/elf-compare.c +@@ -33,21 +33,22 @@ static void compare_correlated_symbol(struct symbol *sym, struct symbol *symtwin + { + // compare bind and type info + if (sym->sym.st_info != symtwin->sym.st_info || +- (sym->sec && !symtwin->sec) || +- (symtwin->sec && !sym->sec)) +- DIFF_FATAL("symbol info mismatch: %s", sym->name); ++ (sym->sec && !symtwin->sec) || ++ (symtwin->sec && !sym->sec)) { ++ DIFF_FATAL("symbol info mismatch: %s", sym->name); ++ } + + // check if correlated symbols have correlated sections + if (sym->sec && symtwin->sec && sym->sec->twin != symtwin->sec) +- DIFF_FATAL("symbol changed sections: %s", sym->name); ++ DIFF_FATAL("symbol changed sections: %s", sym->name); + + // data object can't change size + if (sym->type == STT_OBJECT && sym->sym.st_size != symtwin->sym.st_size) +- DIFF_FATAL("object size mismatch: %s", sym->name); ++ DIFF_FATAL("object size mismatch: %s", sym->name); + +- if (sym->sym.st_shndx == SHN_UNDEF || ++ if (sym->sym.st_shndx == SHN_UNDEF || + sym->sym.st_shndx == SHN_ABS) +- sym->status = SAME; ++ sym->status = SAME; + + /* + * For local symbols, we handle them based on their matching sections. +@@ -56,16 +57,17 @@ static void compare_correlated_symbol(struct symbol *sym, struct symbol *symtwin + + void upatch_compare_symbols(struct upatch_elf *uelf) + { +- struct symbol *sym; ++ struct symbol *sym; + +- list_for_each_entry(sym, &uelf->symbols, list) { +- if (sym->twin) +- compare_correlated_symbol(sym, sym->twin); +- else +- sym->status = NEW; ++ list_for_each_entry(sym, &uelf->symbols, list) { ++ if (sym->twin) { ++ compare_correlated_symbol(sym, sym->twin); ++ } else { ++ sym->status = NEW; ++ } + +- log_debug("symbol %s is %s\n", sym->name, status_str(sym->status)); +- } ++ log_debug("symbol %s is %s\n", sym->name, status_str(sym->status)); ++ } + } + + static bool rela_equal(struct rela *rela1, struct rela *rela2) +@@ -78,12 +80,13 @@ static bool rela_equal(struct rela *rela1, struct rela *rela2) + + /* TODO: handle rela for toc section */ + +- if (rela1->string) +- return rela2->string && !strcmp(rela1->string, rela2->string); ++ if (rela1->string) { ++ return rela2->string && !strcmp(rela1->string, rela2->string); ++ } + +- if (rela1->addend != rela2->addend) { ++ if (rela1->addend != rela2->addend) { + log_debug("relocation addend has changed from %ld to %ld", rela1->addend, rela2->addend); +- return false; ++ return false; + } + + return !mangled_strcmp(rela1->sym->name, rela2->sym->name); +@@ -91,30 +94,31 @@ static bool rela_equal(struct rela *rela1, struct rela *rela2) + + static void compare_correlated_rela_section(struct section *relasec, struct section *relasec_twin) + { +- struct rela *rela1, *rela2 = NULL; ++ struct rela *rela1, *rela2 = NULL; + + /* check relocation item one by one, order matters */ +- rela2 = list_entry(relasec_twin->relas.next, struct rela, list); +- list_for_each_entry(rela1, &relasec->relas, list) { +- if (rela_equal(rela1, rela2)) { +- rela2 = list_entry(rela2->list.next, struct rela, list); +- continue; +- } +- relasec->status = CHANGED; +- return; +- } +- +- relasec->status = SAME; ++ rela2 = list_entry(relasec_twin->relas.next, struct rela, list); ++ list_for_each_entry(rela1, &relasec->relas, list) { ++ if (rela_equal(rela1, rela2)) { ++ rela2 = list_entry(rela2->list.next, struct rela, list); ++ continue; ++ } ++ relasec->status = CHANGED; ++ return; ++ } ++ ++ relasec->status = SAME; + } + + static void compare_correlated_nonrela_section(struct section *sec, struct section *sectwin) + { +- if (sec->sh.sh_type != SHT_NOBITS && ++ if (sec->sh.sh_type != SHT_NOBITS && + (sec->data->d_size != sectwin->data->d_size || +- memcmp(sec->data->d_buf, sectwin->data->d_buf, sec->data->d_size))) +- sec->status = CHANGED; +- else +- sec->status = SAME; ++ memcmp(sec->data->d_buf, sectwin->data->d_buf, sec->data->d_size))) { ++ sec->status = CHANGED; ++ } else { ++ sec->status = SAME; ++ } + } + + // we may change status of sec, they are not same +@@ -122,65 +126,69 @@ static int compare_correlated_section(struct section *sec, struct section *sectw + { + /* TODO: addr align of rodata has changed. after strlen(str) >= 30, align 8 exists */ + /* compare section headers */ +- if (sec->sh.sh_type != sectwin->sh.sh_type || +- sec->sh.sh_flags != sectwin->sh.sh_flags || +- sec->sh.sh_entsize != sectwin->sh.sh_entsize || +- (sec->sh.sh_addralign != sectwin->sh.sh_addralign && +- !is_text_section(sec) && !is_string_section(sec))) { +- DIFF_FATAL("%s section header details differ from %s", sec->name, sectwin->name); +- return -1; +- } ++ if (sec->sh.sh_type != sectwin->sh.sh_type || ++ sec->sh.sh_flags != sectwin->sh.sh_flags || ++ sec->sh.sh_entsize != sectwin->sh.sh_entsize || ++ (sec->sh.sh_addralign != sectwin->sh.sh_addralign && ++ !is_text_section(sec) && !is_string_section(sec))) { ++ DIFF_FATAL("%s section header details differ from %s", sec->name, sectwin->name); ++ return -1; ++ } + +- if (is_note_section(sec)) { +- sec->status = SAME; +- goto out; +- } ++ if (is_note_section(sec)) { ++ sec->status = SAME; ++ goto out; ++ } + +- /* As above but for aarch64 */ +- if (!strcmp(sec->name, ".rela__patchable_function_entries") || +- !strcmp(sec->name, "__patchable_function_entries")) { +- sec->status = SAME; +- goto out; +- } ++ /* As above but for aarch64 */ ++ if (!strcmp(sec->name, ".rela__patchable_function_entries") || ++ !strcmp(sec->name, "__patchable_function_entries")) { ++ sec->status = SAME; ++ goto out; ++ } + + /* compare file size and data size(memory size) */ +- if (sec->sh.sh_size != sectwin->sh.sh_size || +- sec->data->d_size != sectwin->data->d_size || ++ if (sec->sh.sh_size != sectwin->sh.sh_size || ++ sec->data->d_size != sectwin->data->d_size || + (sec->rela && !sectwin->rela) || (!sec->rela && sectwin->rela)) { +- sec->status = CHANGED; +- goto out; +- } ++ sec->status = CHANGED; ++ goto out; ++ } + +- if (is_rela_section(sec)) ++ if (is_rela_section(sec)) { + compare_correlated_rela_section(sec, sectwin); +- else ++ } else { + compare_correlated_nonrela_section(sec, sectwin); ++ } + + out: +- if(sec->status == CHANGED) +- log_debug("section %s has changed\n", sec->name); ++ if (sec->status == CHANGED) { ++ log_debug("section %s has changed\n", sec->name); ++ } + + return 0; + } + + bool upatch_handle_redis_line(const char *symname) + { +- if (!strncmp(symname, "_serverPanic", 12) || +- !strncmp(symname, "_serverAssert", 13) || +- !strncmp(symname, "_serverAssertWithInfo", 21) || ++ if (!strncmp(symname, "_serverPanic", 12) || ++ !strncmp(symname, "_serverAssert", 13) || ++ !strncmp(symname, "_serverAssertWithInfo", 21) || + !strncmp(symname, "rdbReportError", 14) || +- !strncmp(symname, "RedisModule__Assert", 19)) +- return true; +- return false; ++ !strncmp(symname, "RedisModule__Assert", 19)) { ++ return true; ++ } ++ return false; + } + + /* TODO: let user support this list or generate by the compiler ? */ + bool check_line_func(const char *symname) + { +- if (!strncmp(basename(g_relf_name), "redis-server", 12)) +- return upatch_handle_redis_line(symname); ++ if (!strncmp(basename(g_relf_name), "redis-server", 12)) { ++ return upatch_handle_redis_line(symname); ++ } + +- return false; ++ return false; + } + + /* Determine if a section has changed only due to a __LINE__ bumber change. +@@ -194,185 +202,202 @@ static bool _line_macro_change_only(struct upatch_elf *uelf, struct section *sec + struct rela *rela; + bool found, found_any = false; + +- if (sec->status != CHANGED || +- is_rela_section(sec) || +- !is_text_section(sec) || +- sec->sh.sh_size != sec->twin->sh.sh_size || +- !sec->rela || +- sec->rela->status != SAME) +- return false; +- +- data1 = sec->twin->data->d_buf; +- data2 = sec->data->d_buf; +- for (offset = 0; offset < sec->sh.sh_size; offset += insn1_len) { +- insn1 = data1 + offset; +- insn2 = data2 + offset; +- +- insn1_len = insn_length(uelf, insn1); +- insn2_len = insn_length(uelf, insn2); +- +- if (!insn1_len || !insn2_len) +- ERROR("decode instruction in section %s at offset 0x%lx failed", +- sec->name, offset); +- +- if (insn1_len != insn2_len) +- return false; +- +- /* if insn are same, continue*/ +- if (!memcmp(insn1, insn2, insn1_len)) +- continue; +- +- log_debug("check list for %s at 0x%lx \n", sec->name, offset); +- +- /* +- * Here we found a differece between two instructions of the +- * same length. Only ignore the change if: +- * +- * 1) the instruction match a known pattern of a '__LINE__' +- * macro immediate value which was embedded in the instruction. +- * +- * 2) the instructions are followed by certain expected relocations. +- * (white-list) +- */ +- if (!insn_is_load_immediate(uelf, insn1) || +- !insn_is_load_immediate(uelf, insn2)) +- return false; +- +- found = false; +- list_for_each_entry(rela, &sec->rela->relas, list) { +- if (rela->offset < offset + insn1_len) +- continue; +- +- if (rela->string) +- continue; +- +- /* TODO: we may need black list ? */ +- if (check_line_func(rela->sym->name)) { +- found = true; +- break; +- } +- +- return false; +- } +- if (!found) +- return false; +- +- found_any = true; +- } +- +- if (!found_any) +- ERROR("no instruction changes detected for changed section %s", +- sec->name); ++ if (sec->status != CHANGED || ++ is_rela_section(sec) || ++ !is_text_section(sec) || ++ sec->sh.sh_size != sec->twin->sh.sh_size || ++ !sec->rela || ++ sec->rela->status != SAME) { ++ return false; ++ } ++ ++ data1 = sec->twin->data->d_buf; ++ data2 = sec->data->d_buf; ++ for (offset = 0; offset < sec->sh.sh_size; offset += insn1_len) { ++ insn1 = data1 + offset; ++ insn2 = data2 + offset; ++ ++ insn1_len = insn_length(uelf, insn1); ++ insn2_len = insn_length(uelf, insn2); ++ ++ if (!insn1_len || !insn2_len) { ++ ERROR("decode instruction in section %s at offset 0x%lx failed", ++ sec->name, offset); ++ } ++ ++ if (insn1_len != insn2_len) { ++ return false; ++ } ++ ++ /* if insn are same, continue */ ++ if (!memcmp(insn1, insn2, insn1_len)) { ++ continue; ++ } ++ ++ log_debug("check list for %s at 0x%lx \n", sec->name, offset); ++ ++ /* ++ * Here we found a differece between two instructions of the ++ * same length. Only ignore the change if: ++ * ++ * 1) the instruction match a known pattern of a '__LINE__' ++ * macro immediate value which was embedded in the instruction. ++ * ++ * 2) the instructions are followed by certain expected relocations. ++ * (white-list) ++ */ ++ if (!insn_is_load_immediate(uelf, insn1) || ++ !insn_is_load_immediate(uelf, insn2)) { ++ return false; ++ } ++ ++ found = false; ++ list_for_each_entry(rela, &sec->rela->relas, list) { ++ if (rela->offset < offset + insn1_len) { ++ continue; ++ } ++ ++ if (rela->string) { ++ continue; ++ } ++ ++ /* TODO: we may need black list ? */ ++ if (check_line_func(rela->sym->name)) { ++ found = true; ++ break; ++ } + ++ return false; ++ } ++ if (!found) { ++ return false; ++ } ++ ++ found_any = true; ++ } ++ ++ if (!found_any) { ++ ERROR("no instruction changes detected for changed section %s", ++ sec->name); ++ } + return true; + } + + static bool _line_macro_change_only_aarch64(struct upatch_elf *uelf, struct section *sec) + { + +- unsigned long start1, start2, size, offset; +- struct rela *rela; +- bool found_any = false, found; +- unsigned int mov_imm_mask = ((1<<16) - 1)<<5; +- unsigned long insn_len = insn_length(uelf, NULL); +- +- if (sec->status != CHANGED || +- is_rela_section(sec) || +- !is_text_section(sec) || +- sec->sh.sh_size != sec->twin->sh.sh_size || +- !sec->rela || +- sec->rela->status != SAME) +- return false; +- +- start1 = (unsigned long)sec->twin->data->d_buf; +- start2 = (unsigned long)sec->data->d_buf; +- size = sec->sh.sh_size; +- for (offset = 0; offset < size; offset += insn_len) { +- if (!memcmp((void *)start1 + offset, (void *)start2 + offset, insn_len)) +- continue; +- +- /* verify it's a mov immediate to w1 */ +- if ((*(unsigned int *)(start1 + offset) & ~mov_imm_mask) != +- (*(unsigned int *)(start2 + offset) & ~mov_imm_mask)) +- return false; +- +- found = false; +- list_for_each_entry(rela, &sec->rela->relas, list) { +- if (rela->offset < offset + insn_len) +- continue; +- if (rela->string) +- continue; +- +- /* TODO: we may need black list ? */ +- if (check_line_func(rela->sym->name)) { +- found = true; +- break; +- } +- return false; +- } +- if (!found) +- return false; +- +- found_any = true; +- } +- +- if (!found_any) +- ERROR("no instruction changes detected for changed section %s", +- sec->name); +- +- return true; ++ unsigned long start1, start2, size, offset; ++ struct rela *rela; ++ bool found_any = false, found; ++ unsigned int mov_imm_mask = ((1 << 16) - 1) << 5; ++ unsigned long insn_len = insn_length(uelf, NULL); ++ ++ if (sec->status != CHANGED || ++ is_rela_section(sec) || ++ !is_text_section(sec) || ++ sec->sh.sh_size != sec->twin->sh.sh_size || ++ !sec->rela || ++ sec->rela->status != SAME) { ++ return false; ++ } ++ ++ start1 = (unsigned long)sec->twin->data->d_buf; ++ start2 = (unsigned long)sec->data->d_buf; ++ size = sec->sh.sh_size; ++ for (offset = 0; offset < size; offset += insn_len) { ++ if (!memcmp((void *)start1 + offset, (void *)start2 + offset, insn_len)) { ++ continue; ++ } ++ ++ /* verify it's a mov immediate to w1 */ ++ if ((*(unsigned int *)(start1 + offset) & ~mov_imm_mask) != ++ (*(unsigned int *)(start2 + offset) & ~mov_imm_mask)) { ++ return false; ++ } ++ ++ found = false; ++ list_for_each_entry(rela, &sec->rela->relas, list) { ++ if (rela->offset < offset + insn_len) { ++ continue; ++ } ++ if (rela->string) { ++ continue; ++ } ++ ++ /* TODO: we may need black list ? */ ++ if (check_line_func(rela->sym->name)) { ++ found = true; ++ break; ++ } ++ return false; ++ } ++ if (!found) { ++ return false; ++ } ++ ++ found_any = true; ++ } ++ ++ if (!found_any) { ++ ERROR("no instruction changes detected for changed section %s", ++ sec->name); ++ } ++ ++ return true; + } + + static bool line_macro_change_only(struct upatch_elf *uelf, struct section *sec) + { +- switch(uelf->arch) { +- case AARCH64: +- return _line_macro_change_only_aarch64(uelf, sec); +- case X86_64: +- return _line_macro_change_only(uelf, sec); +- default: +- ERROR("unsupported arch"); +- } +- return false; ++ switch (uelf->arch) { ++ case AARCH64: ++ return _line_macro_change_only_aarch64(uelf, sec); ++ case X86_64: ++ return _line_macro_change_only(uelf, sec); ++ case RISCV64: ++ /* TODO: not support */ ++ break; ++ default: ++ ERROR("unsupported arch"); ++ } ++ return false; + } + + static inline void update_section_status(struct section *sec, enum status status) + { +- if (sec == NULL) { +- return; +- } +- if (sec->twin != NULL) { +- sec->twin->status = status; +- } +- if (is_rela_section(sec)) { +- if ((sec->base != NULL) && (sec->base->sym != NULL) && status != SAME) { +- sec->base->sym->status = status; +- } +- } else { +- if (sec->sym != NULL) { +- sec->sym->status = status; +- } +- } ++ if (sec == NULL) { ++ return; ++ } ++ if (sec->twin != NULL) { ++ sec->twin->status = status; ++ } ++ if (is_rela_section(sec)) { ++ if ((sec->base != NULL) && (sec->base->sym != NULL) && status != SAME) { ++ sec->base->sym->status = status; ++ } ++ } else { ++ if (sec->sym != NULL) { ++ sec->sym->status = status; ++ } ++ } + } + + void upatch_compare_sections(struct upatch_elf *uelf) + { +- struct section *sec = NULL; +- +- list_for_each_entry(sec, &uelf->sections, list) { +- if (sec->twin == NULL) { +- sec->status = NEW; +- } +- else { +- compare_correlated_section(sec, sec->twin); +- } +- /* exclude WARN-only, might_sleep changes */ +- if (line_macro_change_only(uelf, sec)) { +- log_debug("reverting macro / line number section %s status to SAME\n", sec->name); +- sec->status = SAME; +- } +- /* sync status */ +- update_section_status(sec, sec->status); +- update_section_status(sec->twin, sec->status); +- } ++ struct section *sec = NULL; ++ ++ list_for_each_entry(sec, &uelf->sections, list) { ++ if (sec->twin == NULL) { ++ sec->status = NEW; ++ } else { ++ compare_correlated_section(sec, sec->twin); ++ } ++ /* exclude WARN-only, might_sleep changes */ ++ if (line_macro_change_only(uelf, sec)) { ++ log_debug("reverting macro / line number section %s status to SAME\n", sec->name); ++ sec->status = SAME; ++ } ++ /* sync status */ ++ update_section_status(sec, sec->status); ++ update_section_status(sec->twin, sec->status); ++ } + } +\ No newline at end of file +diff --git a/upatch-diff/elf-correlate.c b/upatch-diff/elf-correlate.c +index cc3b925f3bfe3b9cbf434acc7792b1589f779414..befe8f8f2b3ed07b87e38595a4e3fc8997643645 100644 +--- a/upatch-diff/elf-correlate.c ++++ b/upatch-diff/elf-correlate.c +@@ -39,50 +39,67 @@ static void correlate_symbol(struct symbol *sym_orig, struct symbol *sym_patched + sym_patched->name = sym_orig->name; + sym_patched->name_source = DATA_SOURCE_REF; + } +- if (sym_orig->relf_sym && !sym_patched->relf_sym) +- sym_patched->relf_sym = sym_orig->relf_sym; ++ if (sym_orig->relf_sym && !sym_patched->relf_sym) { ++ sym_patched->relf_sym = sym_orig->relf_sym; ++ } + } + + void upatch_correlate_symbols(struct upatch_elf *uelf_source, struct upatch_elf *uelf_patched) + { +- struct symbol *sym_orig, *sym_patched; ++ struct symbol *sym_orig, *sym_patched; + +- list_for_each_entry(sym_orig, &uelf_source->symbols, list) { +- if (sym_orig->twin) +- continue; ++ list_for_each_entry(sym_orig, &uelf_source->symbols, list) ++ { ++ if (sym_orig->twin) { ++ continue; ++ } + + /* find matched symbol */ +- list_for_each_entry(sym_patched, &uelf_patched->symbols, list) { +- if (mangled_strcmp(sym_orig->name, sym_patched->name) || +- sym_orig->type != sym_patched->type || sym_patched->twin) +- continue; +- +- /* +- * TODO: Special static local variables should never be correlated and should always +- * be included if they are referenced by an included function. +- */ +- /* +- * The .LCx symbols point to string literals in +- * '.rodata..str1.*' sections. They get included +- * in include_standard_elements(). +- * Clang creates similar .Ltmp%d symbols in .rodata.str +- */ +- if (sym_orig->type == STT_NOTYPE && +- (!strncmp(sym_orig->name, ".LC", 3) || !strncmp(sym_orig->name, ".Ltmp", 5))) +- continue; +- +- if (is_mapping_symbol(uelf_source, sym_orig)) +- continue; +- +- /* group section symbols must have correlated sections */ +- if (sym_orig->sec && sym_orig->sec->sh.sh_type == SHT_GROUP && +- sym_orig->sec->twin != sym_patched->sec) +- continue; +- +- correlate_symbol(sym_orig, sym_patched); +- break; +- } +- } ++ list_for_each_entry(sym_patched, &uelf_patched->symbols, list) ++ { ++ if (mangled_strcmp(sym_orig->name, sym_patched->name) || ++ sym_orig->type != sym_patched->type || sym_patched->twin) { ++ continue; ++ } ++ ++ /* ++ * TODO: Special static local variables should never be correlated and should always ++ * be included if they are referenced by an included function. ++ */ ++ /* ++ * The .LCx symbols point to string literals in ++ * '.rodata..str1.*' sections. They get included ++ * in include_standard_elements(). ++ * Clang creates similar .Ltmp%d symbols in .rodata.str ++ */ ++ if (sym_orig->type == STT_NOTYPE && ++ (!strncmp(sym_orig->name, ".LC", 3) || ++ !strncmp(sym_orig->name, ".Ltmp", 5))) { ++ continue; ++ } ++ ++ if (is_mapping_symbol(uelf_source, sym_orig)) { ++ continue; ++ } ++ ++ /* group section symbols must have correlated sections */ ++ if (sym_orig->sec && sym_orig->sec->sh.sh_type == SHT_GROUP && ++ sym_orig->sec->twin != sym_patched->sec) { ++ continue; ++ } ++ ++ /* .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; ++ } ++ ++ correlate_symbol(sym_orig, sym_patched); ++ break; ++ } ++ } + } + + static void __correlate_section(struct section *sec_orig, struct section *sec_patched) +@@ -104,88 +121,95 @@ static void __correlate_section(struct section *sec_orig, struct section *sec_pa + + static void correlate_section(struct section *sec_orig, struct section *sec_patched) + { +- __correlate_section(sec_orig, sec_patched); ++ __correlate_section(sec_orig, sec_patched); + +- if (is_rela_section(sec_orig)) { +- __correlate_section(sec_orig->base, sec_patched->base); ++ if (is_rela_section(sec_orig)) { ++ __correlate_section(sec_orig->base, sec_patched->base); + + /* handle symbol for base section now */ +- sec_orig = sec_orig->base; +- sec_patched = sec_patched->base; +- } else if (sec_orig->rela && sec_patched->rela) { +- __correlate_section(sec_orig->rela, sec_patched->rela); +- } +- +- if (sec_orig->secsym && sec_patched->secsym) { +- correlate_symbol(sec_orig->secsym, sec_patched->secsym); +- } +- +- if (sec_orig->sym) { +- correlate_symbol(sec_orig->sym, sec_patched->sym); +- } ++ sec_orig = sec_orig->base; ++ sec_patched = sec_patched->base; ++ } else if (sec_orig->rela && sec_patched->rela) { ++ __correlate_section(sec_orig->rela, sec_patched->rela); ++ } ++ ++ if (sec_orig->secsym && sec_patched->secsym) { ++ correlate_symbol(sec_orig->secsym, sec_patched->secsym); ++ } ++ ++ if (sec_orig->sym) { ++ correlate_symbol(sec_orig->sym, sec_patched->sym); ++ } + } + + void upatch_correlate_sections(struct upatch_elf *uelf_source, struct upatch_elf *uelf_patched) + { +- struct section *sec_orig, *sec_patched; ++ struct section *sec_orig, *sec_patched; + +- list_for_each_entry(sec_orig, &uelf_source->sections, list) { ++ list_for_each_entry(sec_orig, &uelf_source->sections, list) { + /* already found */ +- if (sec_orig->twin) +- continue; +- +- list_for_each_entry(sec_patched, &uelf_patched->sections, list) { +- if (mangled_strcmp(sec_orig->name, sec_patched->name) || +- sec_patched->twin) +- continue; +- +- /* +- * TODO: Special static local variables should never be correlated and should always +- * be included if they are referenced by an included function. +- */ +- /* +- * Group sections must match exactly to be correlated. +- */ +- if (sec_orig->sh.sh_type == SHT_GROUP) { +- if (sec_orig->data->d_size != sec_patched->data->d_size) +- continue; +- if (memcmp(sec_orig->data->d_buf, sec_patched->data->d_buf, +- sec_orig->data->d_size)) +- continue; +- } +- +- correlate_section(sec_orig, sec_patched); +- break; +- } +- } ++ if (sec_orig->twin) { ++ continue; ++ } ++ ++ list_for_each_entry(sec_patched, &uelf_patched->sections, list) { ++ if (mangled_strcmp(sec_orig->name, sec_patched->name) || ++ sec_patched->twin) { ++ continue; ++ } ++ ++ /* ++ * TODO: Special static local variables should never be correlated and should always ++ * be included if they are referenced by an included function. ++ */ ++ /* ++ * Group sections must match exactly to be correlated. ++ */ ++ if (sec_orig->sh.sh_type == SHT_GROUP) { ++ if (sec_orig->data->d_size != sec_patched->data->d_size) { ++ continue; ++ } ++ if (memcmp(sec_orig->data->d_buf, sec_patched->data->d_buf, ++ sec_orig->data->d_size)) { ++ continue; ++ } ++ } ++ ++ correlate_section(sec_orig, sec_patched); ++ break; ++ } ++ } + } + + /* TODO: need handle .toc section */ + static struct symbol *find_uncorrelated_rela(struct section *relasec, struct symbol *sym) + { +- struct rela *rela; ++ struct rela *rela; + +- /* find the patched object's corresponding variable */ +- list_for_each_entry(rela, &relasec->relas, list) { +- struct symbol *patched_sym = rela->sym; +- if (patched_sym->twin) +- continue; ++ /* find the patched object's corresponding variable */ ++ list_for_each_entry(rela, &relasec->relas, list) { ++ struct symbol *patched_sym = rela->sym; ++ if (patched_sym->twin) { ++ continue; ++ } + +- if (sym->type != patched_sym->type || +- (sym->type == STT_OBJECT && +- sym->sym.st_size != patched_sym->sym.st_size)) +- continue; ++ if (sym->type != patched_sym->type || ++ (sym->type == STT_OBJECT && ++ sym->sym.st_size != patched_sym->sym.st_size)) { ++ continue; ++ } + +- if (mangled_strcmp(patched_sym->name, sym->name)) +- continue; ++ if (mangled_strcmp(patched_sym->name, sym->name)) { ++ continue; ++ } + + log_debug("find uncorrelated rela symbol successful %s [%s] \n", +- patched_sym->name, section_function_name(relasec)); ++ patched_sym->name, section_function_name(relasec)); + +- return patched_sym; +- } ++ return patched_sym; ++ } + +- return NULL; ++ return NULL; + } + + /* +@@ -197,23 +221,25 @@ static struct symbol *find_static_twin(struct section *relasec, struct symbol *s + { + /* TODO: handle .part symbol is neccessry */ + +- if (!relasec->twin) +- return NULL; ++ if (!relasec->twin) { ++ return NULL; ++ } + +- return find_uncorrelated_rela(relasec->twin, sym); ++ return find_uncorrelated_rela(relasec->twin, sym); + } + + static struct rela *find_static_twin_ref(struct section *relasec, struct symbol *sym) + { +- struct rela *rela; ++ struct rela *rela; + +- list_for_each_entry(rela, &relasec->relas, list) { +- if (rela->sym == sym->twin) +- return rela; +- } ++ list_for_each_entry(rela, &relasec->relas, list) { ++ if (rela->sym == sym->twin) { ++ return rela; ++ } ++ } + +- /* TODO: handle child func here */ +- return NULL; ++ /* TODO: handle child func here */ ++ return NULL; + } + + /* Check two things: +@@ -224,56 +250,65 @@ static struct rela *find_static_twin_ref(struct section *relasec, struct symbol + */ + static void check_static_variable_correlate(struct upatch_elf *uelf_source, struct upatch_elf *uelf_patched) + { +- struct section *relasec; +- struct rela *rela; ++ struct section *relasec; ++ struct rela *rela; + struct symbol *sym; + +- list_for_each_entry(relasec, &uelf_source->sections, list) { +- if (!is_rela_section(relasec) || +- is_debug_section(relasec) || +- is_note_section(relasec)) +- continue; ++ list_for_each_entry(relasec, &uelf_source->sections, list) { ++ if (!is_rela_section(relasec) || ++ is_debug_section(relasec) || ++ is_note_section(relasec)) { ++ continue; ++ } + +- list_for_each_entry(rela, &relasec->relas, list) { ++ list_for_each_entry(rela, &relasec->relas, list) { + sym = rela->sym; +- if (!is_normal_static_local(sym)) +- continue; +- +- if (!sym->twin || !relasec->twin) +- DIFF_FATAL("reference to static local variable %s in %s was removed", +- sym->name, section_function_name(relasec)); +- +- if(!find_static_twin_ref(relasec->twin, sym)) +- DIFF_FATAL("static local %s has been correlated with %s, but patched %s is missing a reference to it", +- sym->name, sym->twin->name, section_function_name(relasec->twin)); ++ if (!is_normal_static_local(sym)) { ++ continue; ++ } ++ ++ if (!sym->twin || !relasec->twin) { ++ DIFF_FATAL("reference to static local variable %s in %s was removed", ++ sym->name, section_function_name(relasec)); ++ } ++ ++ if (!find_static_twin_ref(relasec->twin, sym)) { ++ DIFF_FATAL("static local %s has been correlated with %s, but patched %s is missing a reference to it", ++ sym->name, sym->twin->name, section_function_name(relasec->twin)); ++ } + } + } + +- /* +- * Now go through the patched object and look for any uncorrelated +- * static locals to see if we need to print any warnings about new +- * variables. +- */ +- +- list_for_each_entry(relasec, &uelf_patched->sections, list) { +- +- if (!is_rela_section(relasec) || +- is_debug_section(relasec) || +- is_note_section(relasec)) +- continue; ++ /* ++ * Now go through the patched object and look for any uncorrelated ++ * static locals to see if we need to print any warnings about new ++ * variables. ++ */ ++ ++ list_for_each_entry(relasec, &uelf_patched->sections, list) ++ { ++ ++ if (!is_rela_section(relasec) || ++ is_debug_section(relasec) || ++ is_note_section(relasec)) { ++ continue; ++ } + +- list_for_each_entry(rela, &relasec->relas, list) { +- sym = rela->sym; +- if (!is_normal_static_local(sym)) +- continue; ++ list_for_each_entry(rela, &relasec->relas, list) ++ { ++ sym = rela->sym; ++ if (!is_normal_static_local(sym)) { ++ continue; ++ } + +- if (sym->twin) +- continue; ++ if (sym->twin) { ++ continue; ++ } + +- log_normal("unable to correlate static local variable %s used by %s, assuming variable is new \n", +- sym->name, section_function_name(relasec)); +- } +- } ++ log_normal("unable to correlate static local variable %s used by %s, assuming variable is new \n", ++ sym->name, section_function_name(relasec)); ++ } ++ } + } + + static void uncorrelate_symbol(struct symbol *sym) +@@ -314,86 +349,97 @@ static void uncorrelate_section(struct section *sec) + */ + void upatch_correlate_static_local_variables(struct upatch_elf *uelf_source, struct upatch_elf *uelf_patched) + { +- struct symbol *sym, *patched_sym; +- struct section *relasec; +- struct rela *rela; +- int bundled, patched_bundled; ++ struct symbol *sym, *patched_sym; ++ struct section *relasec; ++ struct rela *rela; ++ int bundled, patched_bundled; + +- /* +- * undo the correlations for all static locals. Two static locals can have the same numbered suffix in the orig ++ /* ++ * undo the correlations for all static locals. Two static locals can have the same numbered suffix in the orig + * and patchedobjects by coincidence. +- */ ++ */ + list_for_each_entry(sym, &uelf_source->symbols, list) { +- if (!is_normal_static_local(sym)) +- continue; ++ if (!is_normal_static_local(sym)) { ++ continue; ++ } + + log_debug("find normal symbol %s \n", sym->name); +- if (sym->twin) +- uncorrelate_symbol(sym); ++ if (sym->twin) { ++ uncorrelate_symbol(sym); ++ } + +- bundled = (sym == sym->sec->sym) ? 1 : 0; +- if (bundled && sym->sec->twin) { ++ bundled = (sym == sym->sec->sym) ? 1 : 0; ++ if (bundled && sym->sec->twin) { + log_debug("find bundled static symbol %s \n", sym->name); + +- uncorrelate_section(sym->sec); ++ uncorrelate_section(sym->sec); + +- if (sym->sec->secsym) +- uncorrelate_symbol(sym->sec->secsym); ++ if (sym->sec->secsym) { ++ uncorrelate_symbol(sym->sec->secsym); ++ } + +- if (sym->sec->rela) +- uncorrelate_section(sym->sec->rela); // uncorrelate relocation section which is not equal to reference +- } +- } ++ if (sym->sec->rela) { ++ uncorrelate_section(sym->sec->rela); // uncorrelate relocation section which is not equal to reference ++ } ++ } ++ } + + /* +- * Do the correlations: for each section reference to a static local, +- * look for a corresponding reference in the section's twin. +- */ +- list_for_each_entry(relasec, &uelf_source->sections, list) { ++ * Do the correlations: for each section reference to a static local, ++ * look for a corresponding reference in the section's twin. ++ */ ++ list_for_each_entry(relasec, &uelf_source->sections, list) { + + /* handle .rela.toc sectoins */ +- if (!is_rela_section(relasec) || +- is_debug_section(relasec) || +- is_note_section(relasec)) +- continue; ++ if (!is_rela_section(relasec) || ++ is_debug_section(relasec) || ++ is_note_section(relasec)) { ++ continue; ++ } + + /* check all relocation symbols */ +- list_for_each_entry(rela, &relasec->relas, list) { ++ list_for_each_entry(rela, &relasec->relas, list) { + sym = rela->sym; + +- if (!is_normal_static_local(sym)) +- continue; ++ if (!is_normal_static_local(sym)) { ++ continue; ++ } + +- if (sym->twin) +- continue; ++ if (sym->twin) { ++ continue; ++ } + +- bundled = (sym == sym->sec->sym) ? 1 : 0; +- if (bundled && sym->sec == relasec->base) { +- /* +- * TODO: A rare case where a static local data structure references itself. ++ bundled = (sym == sym->sec->sym) ? 1 : 0; ++ if (bundled && sym->sec == relasec->base) { ++ /* ++ * TODO: A rare case where a static local data structure references itself. + * There's no reliable way to correlate this. Hopefully + * to the symbol somewhere that can be used. +- */ +- log_debug("can't correlate static local %s's reference to itself\n", sym->name); +- continue; +- } +- +- patched_sym = find_static_twin(relasec, sym); +- if (!patched_sym) +- DIFF_FATAL("reference to static local variable %s in %s was removed", +- sym->name, section_function_name(relasec)); +- +- patched_bundled = (patched_sym == patched_sym->sec->sym) ? 1 : 0; +- if (bundled != patched_bundled) +- ERROR("bundle mismatch for symbol %s", sym->name); +- if (!bundled && sym->sec->twin != patched_sym->sec) +- ERROR("sections %s and %s aren't correlated for symbol %s", +- sym->sec->name, patched_sym->sec->name, sym->name); +- +- correlate_symbol(sym, patched_sym); +- +- if (bundled) +- correlate_section(sym->sec, patched_sym->sec); ++ */ ++ log_debug("can't correlate static local %s's reference to itself\n", sym->name); ++ continue; ++ } ++ ++ patched_sym = find_static_twin(relasec, sym); ++ if (!patched_sym) { ++ DIFF_FATAL("reference to static local variable %s in %s was removed", ++ sym->name, section_function_name(relasec)); ++ } ++ ++ patched_bundled = (patched_sym == patched_sym->sec->sym) ? 1 : 0; ++ if (bundled != patched_bundled) { ++ ERROR("bundle mismatch for symbol %s", sym->name); ++ } ++ if (!bundled && sym->sec->twin != patched_sym->sec) { ++ ERROR("sections %s and %s aren't correlated for symbol %s", ++ sym->sec->name, patched_sym->sec->name, sym->name); ++ } ++ ++ correlate_symbol(sym, patched_sym); ++ ++ if (bundled) { ++ correlate_section(sym->sec, patched_sym->sec); ++ } + } + } + +diff --git a/upatch-diff/elf-debug.c b/upatch-diff/elf-debug.c +index 0490f8628c9fa0f20c3d6379c46e361ac9a16b42..3d7d566f8dc321d32698e241af9eaad3509b4827 100644 +--- a/upatch-diff/elf-debug.c ++++ b/upatch-diff/elf-debug.c +@@ -61,10 +61,10 @@ void upatch_dump_kelf(struct upatch_elf *uelf) + log_debug("rela section expansion\n"); + list_for_each_entry(rela, &sec->relas, list) { + log_debug("sym %d, offset %ld, type %d, %s %s %ld \n", +- rela->sym->index, rela->offset, +- rela->type, rela->sym->name, +- (rela->addend < 0) ? "-" : "+", +- labs(rela->addend)); ++ rela->sym->index, rela->offset, ++ rela->type, rela->sym->name, ++ (rela->addend < 0) ? "-" : "+", ++ labs(rela->addend)); + } + } else { + if (sec->sym) +@@ -81,8 +81,8 @@ next: + log_debug("\n=== Symbols ===\n"); + list_for_each_entry(sym, &uelf->symbols, list) { + log_debug("sym %02d, type %d, bind %d, ndx %02d, name %s (%s)", +- sym->index, sym->type, sym->bind, sym->sym.st_shndx, +- sym->name, status_str(sym->status)); ++ sym->index, sym->type, sym->bind, sym->sym.st_shndx, ++ sym->name, status_str(sym->status)); + if (sym->sec && (sym->type == STT_FUNC || sym->type == STT_OBJECT)) + log_debug(" -> %s", sym->sec->name); + log_debug("\n"); +@@ -115,7 +115,7 @@ void upatch_rebuild_eh_frame(struct section *sec) + return; + + list_for_each_entry(rela, &sec->rela->relas, list) +- count ++; ++ count++; + + /* currently, only delete is possible */ + if (sec->rela->sh.sh_entsize != 0 && +diff --git a/upatch-diff/elf-insn.c b/upatch-diff/elf-insn.c +index 11380d07d2a1fb96752e3e9b00274a871a66a8bd..737cfda822b1c67aa83a3f15e2b71f1d4c079be8 100644 +--- a/upatch-diff/elf-insn.c ++++ b/upatch-diff/elf-insn.c +@@ -60,29 +60,30 @@ long rela_target_offset(struct upatch_elf *uelf, struct section *relasec, struct + struct section *sec = relasec->base; + + switch(uelf->arch) { +- case AARCH64: +- add_off = 0; +- break; +- case X86_64: +- if (!is_text_section(sec) || +- rela->type == R_X86_64_64 || +- rela->type == R_X86_64_32 || +- rela->type == R_X86_64_32S) ++ case RISCV64: ++ case AARCH64: + add_off = 0; +- else if (rela->type == R_X86_64_PC32 || +- rela->type == R_X86_64_PLT32) { +- struct insn insn; +- rela_insn(sec, rela, &insn); +- add_off = (long)insn.next_byte - +- (long)sec->data->d_buf - +- (long)rela->offset; +- } else { +- ERROR("unable to handle rela type %d \n", rela->type); +- } +- break; +- default: +- ERROR("unsupported arch \n"); +- break; ++ break; ++ case X86_64: ++ if (!is_text_section(sec) || ++ rela->type == R_X86_64_64 || ++ rela->type == R_X86_64_32 || ++ rela->type == R_X86_64_32S) { ++ add_off = 0; ++ } else if (rela->type == R_X86_64_PC32 || ++ rela->type == R_X86_64_PLT32) { ++ struct insn insn; ++ rela_insn(sec, rela, &insn); ++ add_off = (long)insn.next_byte - ++ (long)sec->data->d_buf - ++ (long)rela->offset; ++ } else { ++ ERROR("unable to handle rela type %d \n", rela->type); ++ } ++ break; ++ default: ++ ERROR("unsupported arch \n"); ++ break; + } + + return rela->addend + add_off; +@@ -93,14 +94,20 @@ unsigned int insn_length(struct upatch_elf *uelf, void *addr) + struct insn decoded_insn; + + switch(uelf->arch) { +- case AARCH64: +- return ARM64_INSTR_LEN; +- case X86_64: +- insn_init(&decoded_insn, addr, 1); +- insn_get_length(&decoded_insn); +- return decoded_insn.length; +- default: +- ERROR("unsupported arch"); ++ case AARCH64: ++ return ARM64_INSTR_LEN; ++ case X86_64: ++ 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"); + } + + return 0; +@@ -112,22 +119,25 @@ bool insn_is_load_immediate(struct upatch_elf *uelf, void *addr) + unsigned char *insn = addr; + + switch(uelf->arch) { +- case X86_64: +- /* arg2: mov $imm, %esi */ +- if (insn[0] == 0xbe) +- return true; +- +- /* arg3: mov $imm, %edx */ +- if (insn[0] == 0xba) +- return true; +- +- /* 0x41 is the prefix extend - REX.B */ +- if (insn[0] == 0x41 && insn[1] == 0xb8) +- return true; +- +- break; +- default: +- ERROR("unsupported arch"); ++ case X86_64: ++ /* arg2: mov $imm, %esi */ ++ if (insn[0] == 0xbe) { ++ return true; ++ } ++ ++ /* arg3: mov $imm, %edx */ ++ if (insn[0] == 0xba) { ++ return true; ++ } ++ ++ /* 0x41 is the prefix extend - REX.B */ ++ if (insn[0] == 0x41 && insn[1] == 0xb8) { ++ return true; ++ } ++ ++ break; ++ default: ++ ERROR("unsupported arch"); + } + return false; + } +\ No newline at end of file +diff --git a/upatch-diff/elf-insn.h b/upatch-diff/elf-insn.h +index f64ebe190717ebb8c41ea41da9d079ba3794ca6e..96d2225e12d119f7c267340f69f26ab7d7a98e40 100644 +--- a/upatch-diff/elf-insn.h ++++ b/upatch-diff/elf-insn.h +@@ -29,8 +29,13 @@ + #include "asm/insn.h" + #include "upatch-elf.h" + ++// 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/insn/insn.c b/upatch-diff/insn/insn.c +index 7880eedf0868537c1d4ed433b7c493e2663e3ee6..fc6146f8b392ebc871549df6fbfbfa575aad3b88 100644 +--- a/upatch-diff/insn/insn.c ++++ b/upatch-diff/insn/insn.c +@@ -386,22 +386,22 @@ err_out: + static int __get_moffset(struct insn *insn) + { + switch (insn->addr_bytes) { +- case 2: +- insn->moffset1.value = get_next(short, insn); +- insn->moffset1.nbytes = 2; +- break; +- case 4: +- insn->moffset1.value = get_next(int, insn); +- insn->moffset1.nbytes = 4; +- break; +- case 8: +- insn->moffset1.value = get_next(int, insn); +- insn->moffset1.nbytes = 4; +- insn->moffset2.value = get_next(int, insn); +- insn->moffset2.nbytes = 4; +- break; +- default: /* opnd_bytes must be modified manually */ +- goto err_out; ++ case 2: ++ insn->moffset1.value = get_next(short, insn); ++ insn->moffset1.nbytes = 2; ++ break; ++ case 4: ++ insn->moffset1.value = get_next(int, insn); ++ insn->moffset1.nbytes = 4; ++ break; ++ case 8: ++ insn->moffset1.value = get_next(int, insn); ++ insn->moffset1.nbytes = 4; ++ insn->moffset2.value = get_next(int, insn); ++ insn->moffset2.nbytes = 4; ++ break; ++ default: /* opnd_bytes must be modified manually */ ++ goto err_out; + } + insn->moffset1.got = insn->moffset2.got = 1; + +@@ -415,17 +415,18 @@ err_out: + static int __get_immv32(struct insn *insn) + { + switch (insn->opnd_bytes) { +- case 2: +- insn->immediate.value = get_next(short, insn); +- insn->immediate.nbytes = 2; +- break; +- case 4: +- case 8: +- insn->immediate.value = get_next(int, insn); +- insn->immediate.nbytes = 4; +- break; +- default: /* opnd_bytes must be modified manually */ +- goto err_out; ++ case 2: ++ insn->immediate.value = get_next(short, insn); ++ insn->immediate.nbytes = 2; ++ break; ++ case 4: ++ case 8: ++ insn->immediate.value = get_next(int, insn); ++ insn->immediate.nbytes = 4; ++ break; ++ default: ++ /* opnd_bytes must be modified manually */ ++ goto err_out; + } + + return 1; +@@ -438,22 +439,23 @@ err_out: + static int __get_immv(struct insn *insn) + { + switch (insn->opnd_bytes) { +- case 2: +- insn->immediate1.value = get_next(short, insn); +- insn->immediate1.nbytes = 2; +- break; +- case 4: +- insn->immediate1.value = get_next(int, insn); +- insn->immediate1.nbytes = 4; +- break; +- case 8: +- insn->immediate1.value = get_next(int, insn); +- insn->immediate1.nbytes = 4; +- insn->immediate2.value = get_next(int, insn); +- insn->immediate2.nbytes = 4; +- break; +- default: /* opnd_bytes must be modified manually */ +- goto err_out; ++ case 2: ++ insn->immediate1.value = get_next(short, insn); ++ insn->immediate1.nbytes = 2; ++ break; ++ case 4: ++ insn->immediate1.value = get_next(int, insn); ++ insn->immediate1.nbytes = 4; ++ break; ++ case 8: ++ insn->immediate1.value = get_next(int, insn); ++ insn->immediate1.nbytes = 4; ++ insn->immediate2.value = get_next(int, insn); ++ insn->immediate2.nbytes = 4; ++ break; ++ default: ++ /* opnd_bytes must be modified manually */ ++ goto err_out; + } + insn->immediate1.got = insn->immediate2.got = 1; + +@@ -466,19 +468,20 @@ err_out: + static int __get_immptr(struct insn *insn) + { + switch (insn->opnd_bytes) { +- case 2: +- insn->immediate1.value = get_next(short, insn); +- insn->immediate1.nbytes = 2; +- break; +- case 4: +- insn->immediate1.value = get_next(int, insn); +- insn->immediate1.nbytes = 4; +- break; +- case 8: +- /* ptr16:64 is not exist (no segment) */ +- return 0; +- default: /* opnd_bytes must be modified manually */ +- goto err_out; ++ case 2: ++ insn->immediate1.value = get_next(short, insn); ++ insn->immediate1.nbytes = 2; ++ break; ++ case 4: ++ insn->immediate1.value = get_next(int, insn); ++ insn->immediate1.nbytes = 4; ++ break; ++ case 8: ++ /* ptr16:64 is not exist (no segment) */ ++ return 0; ++ default: ++ /* opnd_bytes must be modified manually */ ++ goto err_out; + } + insn->immediate2.value = get_next(unsigned short, insn); + insn->immediate2.nbytes = 2; +@@ -516,39 +519,42 @@ void insn_get_immediate(struct insn *insn) + goto done; + + switch (inat_immediate_size(insn->attr)) { +- case INAT_IMM_BYTE: +- insn->immediate.value = get_next(char, insn); +- insn->immediate.nbytes = 1; +- break; +- case INAT_IMM_WORD: +- insn->immediate.value = get_next(short, insn); +- insn->immediate.nbytes = 2; +- break; +- case INAT_IMM_DWORD: +- insn->immediate.value = get_next(int, insn); +- insn->immediate.nbytes = 4; +- break; +- case INAT_IMM_QWORD: +- insn->immediate1.value = get_next(int, insn); +- insn->immediate1.nbytes = 4; +- insn->immediate2.value = get_next(int, insn); +- insn->immediate2.nbytes = 4; +- break; +- case INAT_IMM_PTR: +- if (!__get_immptr(insn)) +- goto err_out; +- break; +- case INAT_IMM_VWORD32: +- if (!__get_immv32(insn)) +- goto err_out; +- break; +- case INAT_IMM_VWORD: +- if (!__get_immv(insn)) +- goto err_out; +- break; +- default: +- /* Here, insn must have an immediate, but failed */ +- goto err_out; ++ case INAT_IMM_BYTE: ++ insn->immediate.value = get_next(char, insn); ++ insn->immediate.nbytes = 1; ++ break; ++ case INAT_IMM_WORD: ++ insn->immediate.value = get_next(short, insn); ++ insn->immediate.nbytes = 2; ++ break; ++ case INAT_IMM_DWORD: ++ insn->immediate.value = get_next(int, insn); ++ insn->immediate.nbytes = 4; ++ break; ++ case INAT_IMM_QWORD: ++ insn->immediate1.value = get_next(int, insn); ++ insn->immediate1.nbytes = 4; ++ insn->immediate2.value = get_next(int, insn); ++ insn->immediate2.nbytes = 4; ++ break; ++ case INAT_IMM_PTR: ++ if (!__get_immptr(insn)) { ++ goto err_out; ++ } ++ break; ++ case INAT_IMM_VWORD32: ++ if (!__get_immv32(insn)) { ++ goto err_out; ++ } ++ break; ++ case INAT_IMM_VWORD: ++ if (!__get_immv(insn)) { ++ goto err_out; ++ } ++ break; ++ default: ++ /* Here, insn must have an immediate, but failed */ ++ goto err_out; + } + if (inat_has_second_immediate(insn->attr)) { + insn->immediate2.value = get_next(char, insn); +diff --git a/upatch-diff/log.h b/upatch-diff/log.h +index 34b58bfa70b415d52ebfed928467c565ac2218c9..038181bb5ededf035a37d2d5a742e5f32bc91f8d 100644 +--- a/upatch-diff/log.h ++++ b/upatch-diff/log.h +@@ -41,10 +41,10 @@ enum exit_status{ + + /* Since upatch-build is an one-shot program, we do not care about failure handler */ + #define ERROR(format, ...) \ +- error(EXIT_STATUS_ERROR, 0, "ERROR: %s: %s: %d: " format, g_logprefix, __FUNCTION__, __LINE__, ##__VA_ARGS__) ++ error(EXIT_STATUS_ERROR, 0, "ERROR: %s: %s: %d: " format, g_logprefix, __FUNCTION__, __LINE__, ##__VA_ARGS__) + + #define DIFF_FATAL(format, ...) \ +- error(EXIT_STATUS_DIFF_FATAL, 0, "ERROR: %s: %s: %d: " format, g_logprefix, __FUNCTION__, __LINE__, ##__VA_ARGS__) ++ error(EXIT_STATUS_DIFF_FATAL, 0, "ERROR: %s: %s: %d: " format, g_logprefix, __FUNCTION__, __LINE__, ##__VA_ARGS__) + + /* it is time cost */ + #define log_debug(format, ...) log(DEBUG, format, ##__VA_ARGS__) +@@ -53,12 +53,12 @@ enum exit_status{ + + #define log(level, format, ...) \ + ({ \ +- if (g_loglevel <= (level)) \ +- printf(format, ##__VA_ARGS__); \ ++ if (g_loglevel <= (level)) \ ++ printf(format, ##__VA_ARGS__); \ + }) + + #define REQUIRE(COND, message) \ +- do \ ++ do \ + if (!(COND)) \ + ERROR(message); \ + while (0) +diff --git a/upatch-diff/running-elf.c b/upatch-diff/running-elf.c +index c99b3958c1acd46e9ba1b63bc8c80c3fa290a652..9a197c2c446d4917378f640bf445d2fd52738376 100644 +--- a/upatch-diff/running-elf.c ++++ b/upatch-diff/running-elf.c +@@ -61,38 +61,44 @@ void relf_init(char *elf_name, struct running_elf *relf) + GElf_Sym sym; + + relf->fd = open(elf_name, O_RDONLY); +- if (relf->fd == -1) ++ if (relf->fd == -1) { + ERROR("open with errno = %d", errno); ++ } + + relf->elf = elf_begin(relf->fd, ELF_C_READ, NULL); +- if (!relf->elf) ++ if (!relf->elf) { + ERROR("elf_begin with error %s", elf_errmsg(0)); ++ } + + relf->is_exec = is_exec(relf->elf); + + while ((scn = elf_nextscn(relf->elf, scn)) != NULL) { +- if (!gelf_getshdr(scn, &shdr)) ++ if (!gelf_getshdr(scn, &shdr)) { + ERROR("gelf_getshdr with error %s", elf_errmsg(0)); ++ } + +- if (shdr.sh_type == SHT_SYMTAB) ++ if (shdr.sh_type == SHT_SYMTAB) { + break; ++ } + } + + data = elf_getdata(scn, NULL); +- if (!data) ++ if (!data) { + ERROR("elf_getdata with error %s", elf_errmsg(0)); +- ++ } + relf->obj_nr = (int)(shdr.sh_size / shdr.sh_entsize); + relf->obj_syms = calloc((size_t)relf->obj_nr, sizeof(struct debug_symbol)); +- if (!relf->obj_syms) ++ if (!relf->obj_syms) { + ERROR("calloc with errno = %d", errno); +- ++ } + for (int i = 0; i < relf->obj_nr; i ++) { +- if (!gelf_getsym(data, i, &sym)) ++ if (!gelf_getsym(data, i, &sym)) { + ERROR("gelf_getsym with error %s", elf_errmsg(0)); ++ } + relf->obj_syms[i].name = elf_strptr(relf->elf, shdr.sh_link, sym.st_name); +- if (!relf->obj_syms[i].name) ++ if (!relf->obj_syms[i].name) { + ERROR("elf_strptr with error %s", elf_errmsg(0)); ++ } + relf->obj_syms[i].type = GELF_ST_TYPE(sym.st_info); + relf->obj_syms[i].bind = GELF_ST_BIND(sym.st_info); + relf->obj_syms[i].shndx = sym.st_shndx; +diff --git a/upatch-diff/upatch-elf.c b/upatch-diff/upatch-elf.c +index 171e88ebbb8274bbe7c85c2f8929b2a5221878f1..fc27d099faab620e591f24183d6e3749facfa689 100644 +--- a/upatch-diff/upatch-elf.c ++++ b/upatch-diff/upatch-elf.c +@@ -45,31 +45,38 @@ static void create_section_list(struct upatch_elf *uelf) + struct section *sec; + Elf_Scn *scn = NULL; + +- if (elf_getshdrnum(uelf->elf, §ions_nr)) ++ if (elf_getshdrnum(uelf->elf, §ions_nr)) { + ERROR("elf_getshdrnum with error %s", elf_errmsg(0)); ++ } + +- sections_nr --; ++ sections_nr--; + +- if (elf_getshdrstrndx(uelf->elf, &shstrndx)) ++ if (elf_getshdrstrndx(uelf->elf, &shstrndx)) { + ERROR("elf_getshdrstrndx with error %s", elf_errmsg(0)); ++ } + + log_debug("=== section list (%zu) === \n", sections_nr); +- while (sections_nr --) { ++ while (sections_nr--) { + ALLOC_LINK(sec, &uelf->sections); + + scn = elf_nextscn(uelf->elf, scn); +- if (!scn) ++ if (!scn) { + ERROR("elf_nextscn with error %s", elf_errmsg(0)); ++ } + +- if (!gelf_getshdr(scn, &sec->sh)) ++ if (!gelf_getshdr(scn, &sec->sh)) { + ERROR("gelf_getshdr with error %s", elf_errmsg(0)); ++ } + + sec->name = elf_strptr(uelf->elf, shstrndx, sec->sh.sh_name); +- if (!sec->name) ++ if (!sec->name) { + ERROR("elf_strptr with error %s", elf_errmsg(0)); ++ } ++ + sec->data = elf_getdata(scn, NULL); +- if (!sec->data) ++ if (!sec->data) { + ERROR("elf_getdata with error %s", elf_errmsg(0)); ++ } + + sec->name_source = DATA_SOURCE_ELF; + sec->data_source = DATA_SOURCE_ELF; +@@ -77,15 +84,17 @@ static void create_section_list(struct upatch_elf *uelf) + + sec->index = (unsigned int)elf_ndxscn(scn); + /* found extended section header */ +- if (sec->sh.sh_type == SHT_SYMTAB_SHNDX) ++ if (sec->sh.sh_type == SHT_SYMTAB_SHNDX) { + uelf->symtab_shndx = sec->data; /* correct ? */ ++ } + + log_debug("ndx %02d, data %p, size %zu, name %s\n", +- sec->index, sec->data->d_buf, sec->data->d_size, sec->name); ++ sec->index, sec->data->d_buf, sec->data->d_size, sec->name); + } + +- if (elf_nextscn(uelf->elf, scn)) ++ if (elf_nextscn(uelf->elf, scn)) { + ERROR("elf_nextscn with error %s", elf_errmsg(0)); ++ } + } + + static void create_symbol_list(struct upatch_elf *uelf) +@@ -98,26 +107,28 @@ static void create_symbol_list(struct upatch_elf *uelf) + + /* consider type first */ + symtab = find_section_by_name(&uelf->sections, ".symtab"); +- if (!symtab) ++ if (!symtab) { + ERROR("can't find symbol table"); +- ++ } + symbols_nr = (unsigned int)(symtab->sh.sh_size / symtab->sh.sh_entsize); + + log_debug("\n=== symbol list (%d entries) ===\n", symbols_nr); +- while (symbols_nr --) { ++ while (symbols_nr--) { + ALLOC_LINK(sym, &uelf->symbols); + INIT_LIST_HEAD(&sym->children); + + sym->index = index; +- if (!gelf_getsym(symtab->data, (int)index, &sym->sym)) ++ if (!gelf_getsym(symtab->data, (int)index, &sym->sym)) { + ERROR("gelf_getsym with error %s", elf_errmsg(0)); ++ } + +- index ++; ++ index++; + + sym->name = elf_strptr(uelf->elf, symtab->sh.sh_link, sym->sym.st_name); +- if (!sym->name) ++ if (!sym->name) { + ERROR("elf_strptr with error %s", elf_errmsg(0)); +- ++ } ++ + sym->type = GELF_ST_TYPE(sym->sym.st_info); + sym->bind = GELF_ST_BIND(sym->sym.st_info); + +@@ -125,15 +136,17 @@ static void create_symbol_list(struct upatch_elf *uelf) + /* releated section located in extended header */ + if (shndx == SHN_XINDEX && + !gelf_getsymshndx(symtab->data, uelf->symtab_shndx, +- (int)sym->index, &sym->sym, &shndx)) ++ (int)sym->index, &sym->sym, &shndx)) { + ERROR("gelf_getsymshndx with error %s", elf_errmsg(0)); ++ } + + if ((sym->sym.st_shndx > SHN_UNDEF && sym->sym.st_shndx < SHN_LORESERVE) || + sym->sym.st_shndx == SHN_XINDEX) { + + sym->sec = find_section_by_index(&uelf->sections, shndx); +- if (!sym->sec) ++ if (!sym->sec) { + ERROR("no releated section found for symbol %s \n", sym->name); ++ } + + /* this symbol is releated with a section */ + if (sym->type == STT_SECTION) { +@@ -145,10 +158,12 @@ static void create_symbol_list(struct upatch_elf *uelf) + } + } + log_debug("sym %02d, type %d, bind %d, ndx %02d, name %s", +- sym->index, sym->type, sym->bind, sym->sym.st_shndx, +- sym->name); +- if (sym->sec) ++ sym->index, sym->type, sym->bind, sym->sym.st_shndx, ++ sym->name); ++ if (sym->sec) { + log_debug(" -> %s", sym->sec->name); ++ } ++ + log_debug("\n"); + } + } +@@ -164,14 +179,15 @@ static void create_rela_list(struct upatch_elf *uelf, struct section *relasec) + + /* for relocation sections, sh_info is the index which these informations apply */ + relasec->base = find_section_by_index(&uelf->sections, relasec->sh.sh_info); +- if (!relasec->base) ++ if (!relasec->base) { + ERROR("no base section found for relocation section %s", relasec->name); ++ } + + relasec->base->rela = relasec; + rela_nr = relasec->sh.sh_size / relasec->sh.sh_entsize; + + log_debug("\n=== rela list for %s (%ld entries) === \n", +- relasec->base->name, rela_nr); ++ relasec->base->name, rela_nr); + + if (is_debug_section(relasec)) { + log_debug("skipping rela listing for .debug_* section \n"); +@@ -183,12 +199,13 @@ static void create_rela_list(struct upatch_elf *uelf, struct section *relasec) + skip = 1; + } + +- while (rela_nr --) { ++ while (rela_nr--) { + ALLOC_LINK(rela, &relasec->relas); + + /* use index because we need to keep the order of rela */ +- if (!gelf_getrela(relasec->data, index, &rela->rela)) ++ if (!gelf_getrela(relasec->data, index, &rela->rela)) { + ERROR("gelf_getrela with error %s", elf_errmsg(0)); ++ } + index++; + + rela->type = GELF_R_TYPE(rela->rela.r_info); +@@ -196,26 +213,32 @@ static void create_rela_list(struct upatch_elf *uelf, struct section *relasec) + rela->offset = (unsigned int)rela->rela.r_offset; + symndx = (unsigned int)GELF_R_SYM(rela->rela.r_info); + rela->sym = find_symbol_by_index(&uelf->symbols, symndx); +- if (!rela->sym) ++ if (!rela->sym) { + ERROR("no rela entry symbol found \n"); ++ } + + if (rela->sym->sec && is_string_section(rela->sym->sec)) { + rela->string = rela->sym->sec->data->d_buf + +- rela->sym->sym.st_value + +- rela_target_offset(uelf, relasec, rela); +- if (!rela->string) ++ rela->sym->sym.st_value + ++ rela_target_offset(uelf, relasec, rela); ++ if (!rela->string) { + ERROR("could not lookup rela string for %s+%ld", +- rela->sym->name, rela->addend); ++ rela->sym->name, rela->addend); ++ } + } + +- if (skip) ++ if (skip) { + continue; ++ } + + log_debug("offset %ld, type %d, %s %s %ld", rela->offset, +- rela->type, rela->sym->name, +- (rela->addend < 0) ? "-" : "+", labs(rela->addend)); +- if (rela->string) // rela->string is not utf8 ++ rela->type, rela->sym->name, ++ (rela->addend < 0) ? "-" : "+", labs(rela->addend)); ++ ++ // rela->string is not utf8 ++ if (rela->string) { + log_debug(" string = %s", rela->string); ++ } + log_debug("\n"); + } + } +@@ -304,12 +327,14 @@ void upatch_elf_open(struct upatch_elf *uelf, const char *name) + int fd = 1; + + fd = open(name, O_RDONLY); +- if (fd == -1) ++ if (fd == -1) { + ERROR("open %s failed with errno %d \n", name, errno); ++ } + + elf = elf_begin(fd, ELF_C_RDWR, NULL); +- if (!elf) ++ if (!elf) { + ERROR("open elf %s failed with error %s \n", name, elf_errmsg(0)); ++ } + + memset(uelf, 0, sizeof(*uelf)); + INIT_LIST_HEAD(&uelf->sections); +@@ -319,27 +344,39 @@ void upatch_elf_open(struct upatch_elf *uelf, const char *name) + uelf->elf = elf; + uelf->fd = fd; + +- if (!gelf_getehdr(uelf->elf, &ehdr)) ++ if (!gelf_getehdr(uelf->elf, &ehdr)) { + ERROR("get file %s elf header failed with error %s \n", +- name, elf_errmsg(0)); ++ name, elf_errmsg(0)); ++ } + + /* TODO: check ELF type here, we only handle object file */ +- if (ehdr.e_type != ET_REL) ++ if (ehdr.e_type != ET_REL) { + ERROR("only handles relocatable files \n"); ++ } + + /* + * Main problem here is stack check, for kernel, only x86 is support + * Not sure how to handle userspace, but let us handle x86 first here + */ + switch (ehdr.e_machine) { +- case EM_AARCH64: +- uelf->arch = AARCH64; +- break; +- case EM_X86_64: +- uelf->arch = X86_64; +- break; +- default: +- ERROR("unsupported architecture here"); ++ case EM_AARCH64: ++ uelf->arch = AARCH64; ++ break; ++ 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 here"); + } + + create_section_list(uelf); +diff --git a/upatch-diff/upatch-elf.h b/upatch-diff/upatch-elf.h +index 6c62c931e2e0a586a41cffb07330d1011b6ece9a..7bf0ffc0d1bfd2e41204571015e08eb15c1efd86 100644 +--- a/upatch-diff/upatch-elf.h ++++ b/upatch-diff/upatch-elf.h +@@ -126,6 +126,7 @@ struct symbol { + enum architecture { + X86_64 = 0x1 << 0, + AARCH64 = 0x1 << 1, ++ RISCV64 = 0x1 << 2 + }; + + struct upatch_elf { +diff --git a/upatch-manage/arch/aarch64/insn.c b/upatch-manage/arch/aarch64/insn.c +index bb61f777f3b1008e0e66463c8a03b8320bec455e..153053a15a8a20693e5e0b2adf32693b6ff201ec 100644 +--- a/upatch-manage/arch/aarch64/insn.c ++++ b/upatch-manage/arch/aarch64/insn.c +@@ -17,49 +17,49 @@ static int aarch64_get_imm_shift_mask(enum aarch64_insn_imm_type type, + int shift; + + switch (type) { +- case AARCH64_INSN_IMM_26: +- mask = BIT(26) - 1; +- shift = 0; +- break; +- case AARCH64_INSN_IMM_19: +- mask = BIT(19) - 1; +- shift = 5; +- break; +- case AARCH64_INSN_IMM_16: +- mask = BIT(16) - 1; +- shift = 5; +- break; +- case AARCH64_INSN_IMM_14: +- mask = BIT(14) - 1; +- shift = 5; +- break; +- case AARCH64_INSN_IMM_12: +- mask = BIT(12) - 1; +- shift = 10; +- break; +- case AARCH64_INSN_IMM_9: +- mask = BIT(9) - 1; +- shift = 12; +- break; +- case AARCH64_INSN_IMM_7: +- mask = BIT(7) - 1; +- shift = 15; +- break; +- case AARCH64_INSN_IMM_6: +- case AARCH64_INSN_IMM_S: +- mask = BIT(6) - 1; +- shift = 10; +- break; +- case AARCH64_INSN_IMM_R: +- mask = BIT(6) - 1; +- shift = 16; +- break; +- case AARCH64_INSN_IMM_N: +- mask = 1; +- shift = 22; +- break; +- default: +- return -EINVAL; ++ case AARCH64_INSN_IMM_26: ++ mask = BIT(26) - 1; ++ shift = 0; ++ break; ++ case AARCH64_INSN_IMM_19: ++ mask = BIT(19) - 1; ++ shift = 5; ++ break; ++ case AARCH64_INSN_IMM_16: ++ mask = BIT(16) - 1; ++ shift = 5; ++ break; ++ case AARCH64_INSN_IMM_14: ++ mask = BIT(14) - 1; ++ shift = 5; ++ break; ++ case AARCH64_INSN_IMM_12: ++ mask = BIT(12) - 1; ++ shift = 10; ++ break; ++ case AARCH64_INSN_IMM_9: ++ mask = BIT(9) - 1; ++ shift = 12; ++ break; ++ case AARCH64_INSN_IMM_7: ++ mask = BIT(7) - 1; ++ shift = 15; ++ break; ++ case AARCH64_INSN_IMM_6: ++ case AARCH64_INSN_IMM_S: ++ mask = BIT(6) - 1; ++ shift = 10; ++ break; ++ case AARCH64_INSN_IMM_R: ++ mask = BIT(6) - 1; ++ shift = 16; ++ break; ++ case AARCH64_INSN_IMM_N: ++ mask = 1; ++ shift = 22; ++ break; ++ default: ++ return -EINVAL; + } + + *maskp = mask; +@@ -78,21 +78,21 @@ u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, u32 insn, + return AARCH64_BREAK_FAULT; + + switch (type) { +- case AARCH64_INSN_IMM_ADR: +- shift = 0; +- immlo = (imm & ADR_IMM_LOMASK) << ADR_IMM_LOSHIFT; +- imm >>= ADR_IMM_HILOSPLIT; +- immhi = (imm & ADR_IMM_HIMASK) << ADR_IMM_HISHIFT; +- imm = immlo | immhi; +- mask = ((ADR_IMM_LOMASK << ADR_IMM_LOSHIFT) | +- (ADR_IMM_HIMASK << ADR_IMM_HISHIFT)); +- break; +- default: +- if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) { +- log_error("upatch: unknown immediate encoding %d\n", +- type); +- return AARCH64_BREAK_FAULT; +- } ++ case AARCH64_INSN_IMM_ADR: ++ shift = 0; ++ immlo = (imm & ADR_IMM_LOMASK) << ADR_IMM_LOSHIFT; ++ imm >>= ADR_IMM_HILOSPLIT; ++ immhi = (imm & ADR_IMM_HIMASK) << ADR_IMM_HISHIFT; ++ imm = immlo | immhi; ++ mask = ((ADR_IMM_LOMASK << ADR_IMM_LOSHIFT) | ++ (ADR_IMM_HIMASK << ADR_IMM_HISHIFT)); ++ break; ++ default: ++ if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) { ++ log_error("upatch: unknown immediate encoding %d\n", ++ type); ++ return AARCH64_BREAK_FAULT; ++ } + } + + /* Update the immediate field. */ +diff --git a/upatch-manage/arch/aarch64/relocation.c b/upatch-manage/arch/aarch64/relocation.c +index 395113556f5411ebcbd8c44ba9d3ae35f517373c..370cdedbad1348c39131ce769992659b9c643bfc 100644 +--- a/upatch-manage/arch/aarch64/relocation.c ++++ b/upatch-manage/arch/aarch64/relocation.c +@@ -24,300 +24,310 @@ + #include "upatch-relocation.h" + #include "upatch-resolve.h" + +-#define TCB_SIZE 2 * sizeof(void *) ++#define TCB_SIZE (2 * sizeof(void*)) ++ + + enum aarch64_reloc_op { +- RELOC_OP_NONE, +- RELOC_OP_ABS, +- RELOC_OP_PREL, +- RELOC_OP_PAGE, ++ RELOC_OP_NONE, ++ RELOC_OP_ABS, ++ RELOC_OP_PREL, ++ RELOC_OP_PAGE, + }; + +-static inline s64 calc_reloc(enum aarch64_reloc_op op, void *place, u64 val) ++static inline s64 calc_reloc(enum aarch64_reloc_op op, void* place, u64 val) + { +- s64 sval; +- switch (op) { +- case RELOC_OP_ABS: +- // S + A +- sval = (s64)val; +- break; +- case RELOC_OP_PREL: +- // S + A - P +- sval = (s64)(val - (u64)place); +- break; +- case RELOC_OP_PAGE: +- // Page(S + A) - Page(P) +- sval = (s64)((val & ~(u64)0xfff) - ((u64)place & ~(u64)0xfff)); +- break; +- default: +- log_error("upatch: unknown relocation operation %d\n", op); +- break; +- } ++ s64 sval; ++ switch (op) { ++ case RELOC_OP_ABS: ++ // S + A ++ sval = (s64)val; ++ break; ++ case RELOC_OP_PREL: ++ // S + A - P ++ sval = (s64)(val - (u64)place); ++ break; ++ case RELOC_OP_PAGE: ++ // Page(S + A) - Page(P) ++ sval = (s64)((val & ~(u64)0xfff) - ((u64)place & ~(u64)0xfff)); ++ break; ++ default: ++ log_error("upatch: unknown relocation operation %d\n", op); ++ break; ++ } + +- log_debug("upatch: reloc, S+A=0x%lx, P=0x%lx, X=0x%lx\n", val, +- (u64)place, sval); +- return sval; ++ log_debug("upatch: reloc, S+A=0x%lx, P=0x%lx, X=0x%lx\n", val, (u64)place, ++ sval); ++ return sval; + } + +-int apply_relocate_add(struct upatch_elf *uelf, unsigned int symindex, +- unsigned int relsec) ++int apply_relocate_add(struct upatch_elf* uelf, ++ unsigned int symindex, ++ unsigned int relsec) + { +- unsigned int i; +- GElf_Sym *sym; +- char const *sym_name; +- void *loc; +- void *uloc; +- u64 val; +- s64 result; +- GElf_Shdr *shdrs = (void *)uelf->info.shdrs; +- GElf_Rela *rel = (void *)shdrs[relsec].sh_addr; ++ unsigned int i; ++ GElf_Sym* sym; ++ char const* sym_name; ++ void* loc; ++ void* uloc; ++ u64 val; ++ s64 result; ++ GElf_Shdr* shdrs = (void*)uelf->info.shdrs; ++ GElf_Rela* rel = (void*)shdrs[relsec].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; ++ 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 = (void *)shdrs[shdrs[relsec].sh_info].sh_addralign + +- rel[i].r_offset; ++ // /* uloc corresponds P in user space */ ++ uloc = ++ (void*)shdrs[shdrs[relsec].sh_info].sh_addralign + rel[i].r_offset; + +- /* sym is the ELF symbol we're referring to */ +- sym = (GElf_Sym *)shdrs[symindex].sh_addr + +- GELF_R_SYM(rel[i].r_info); +- if (GELF_ST_TYPE(sym[i].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; ++ /* sym is the ELF symbol we're referring to */ ++ sym = (GElf_Sym*)shdrs[symindex].sh_addr + GELF_R_SYM(rel[i].r_info); ++ if (GELF_ST_TYPE(sym[i].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 = (unsigned long)sym->st_value + (unsigned long)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, +- shdrs[shdrs[relsec].sh_info].sh_addralign, +- rel[i].r_offset, sym->st_value, rel[i].r_addend); ++ /* val corresponds to (S + A) */ ++ val = (unsigned long)sym->st_value + (unsigned long)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, ++ shdrs[shdrs[relsec].sh_info].sh_addralign, 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_AARCH64_NONE: +- break; +- /* Data relocations. */ +- case R_AARCH64_ABS64: +- result = calc_reloc(RELOC_OP_ABS, uloc, val); +- *(s64 *)loc = result; +- break; +- case R_AARCH64_ABS32: +- result = calc_reloc(RELOC_OP_ABS, uloc, val); +- if (result < -(s64)BIT(31) || result >= (s64)BIT(32)) +- goto overflow; +- *(s32 *)loc = (s32)result; +- break; +- case R_AARCH64_ABS16: +- result = calc_reloc(RELOC_OP_ABS, uloc, val); +- if (result < -(s64)BIT(15) || result >= (s64)BIT(16)) +- goto overflow; +- *(s16 *)loc = (s16)result; +- break; +- case R_AARCH64_PREL64: +- result = calc_reloc(RELOC_OP_PREL, uloc, val); +- *(s64 *)loc = result; +- break; +- case R_AARCH64_PREL32: +- result = calc_reloc(RELOC_OP_PREL, uloc, val); +- if (result < -(s64)BIT(31) || result >= (s64)BIT(32)) +- goto overflow; +- *(s32 *)loc = (s32)result; +- break; +- case R_AARCH64_PREL16: +- result = calc_reloc(RELOC_OP_PREL, uloc, val); +- if (result < -(s64)BIT(15) || result >= (s64)BIT(16)) +- goto overflow; +- *(s16 *)loc = (s16)result; +- break; +- /* Immediate instruction relocations. */ +- case R_AARCH64_LD_PREL_LO19: +- result = calc_reloc(RELOC_OP_PREL, uloc, val); +- if (result < -(s64)BIT(20) || result >= (s64)BIT(20)) +- goto overflow; +- result = extract_insn_imm(result, 19, 2); +- result = insert_insn_imm(AARCH64_INSN_IMM_19, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_ADR_PREL_LO21: +- result = calc_reloc(RELOC_OP_PREL, uloc, val); +- if (result < -(s64)BIT(20) || result >= (s64)BIT(20)) +- goto overflow; +- result = extract_insn_imm(result, 21, 0); +- result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_ADR_PREL_PG_HI21: +- result = calc_reloc(RELOC_OP_PAGE, uloc, val); +- if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) +- goto overflow; +- result = extract_insn_imm(result, 21, 12); +- result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_ADR_PREL_PG_HI21_NC: +- result = calc_reloc(RELOC_OP_PAGE, uloc, val); +- result = extract_insn_imm(result, 21, 12); +- result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_ADD_ABS_LO12_NC: +- case R_AARCH64_LDST8_ABS_LO12_NC: +- result = calc_reloc(RELOC_OP_ABS, uloc, val); +- result = extract_insn_imm(result, 12, 0); +- result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_LDST16_ABS_LO12_NC: +- result = calc_reloc(RELOC_OP_ABS, uloc, val); +- result = extract_insn_imm(result, 11, 1); +- result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_LDST32_ABS_LO12_NC: +- result = calc_reloc(RELOC_OP_ABS, uloc, val); +- result = extract_insn_imm(result, 10, 2); +- result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_LDST64_ABS_LO12_NC: +- result = calc_reloc(RELOC_OP_ABS, uloc, val); +- result = extract_insn_imm(result, 9, 3); +- result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_LDST128_ABS_LO12_NC: +- result = calc_reloc(RELOC_OP_ABS, uloc, val); +- result = extract_insn_imm(result, 8, 4); +- result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_TSTBR14: +- result = calc_reloc(RELOC_OP_PREL, uloc, val); +- if (result < -(s64)BIT(15) || result >= (s64)BIT(15)) +- goto overflow; +- result = extract_insn_imm(result, 14, 2); +- result = insert_insn_imm(AARCH64_INSN_IMM_14, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_CONDBR19: +- result = calc_reloc(RELOC_OP_PREL, uloc, val); +- result = extract_insn_imm(result, 19, 2); +- result = insert_insn_imm(AARCH64_INSN_IMM_19, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_JUMP26: +- case R_AARCH64_CALL26: +- result = calc_reloc(RELOC_OP_PREL, uloc, val); +- if (result < -(s64)BIT(27) || result >= (s64)BIT(27)) { +- log_debug( +- "R_AARCH64_CALL26 overflow: result = 0x%lx, uloc = " +- "0x%lx, val = 0x%lx\n", +- result, (unsigned long)uloc, val); +- val = search_insert_plt_table(uelf, val, +- (u64)&val); +- log_debug( +- "R_AARCH64_CALL26 overflow: plt.addr = 0x%lx\n", +- val); +- if (!val) +- goto overflow; +- result = calc_reloc(RELOC_OP_PREL, uloc, val); +- } +- result = extract_insn_imm(result, 26, 2); +- result = insert_insn_imm(AARCH64_INSN_IMM_26, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_ADR_GOT_PAGE: +- result = calc_reloc(RELOC_OP_PAGE, uloc, val); +- if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) +- goto overflow; +- result = extract_insn_imm(result, 21, 12); +- result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_LD64_GOT_LO12_NC: +- result = calc_reloc(RELOC_OP_ABS, uloc, val); +- // don't check result & 7 == 0. +- // sometimes, result & 7 != 0, it works fine. +- result = extract_insn_imm(result, 9, 3); +- result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_TLSLE_ADD_TPREL_HI12: +- result = (long)(ALIGN(TCB_SIZE, uelf->relf->tls_align) + val); +- if (result < 0 || result >= (s64)BIT(24)) +- goto overflow; +- result = extract_insn_imm(result, 12, 12); +- result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: +- result = (long)(ALIGN(TCB_SIZE, uelf->relf->tls_align) + val); +- result = extract_insn_imm(result, 12, 0); +- result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_TLSDESC_ADR_PAGE21: +- result = calc_reloc(RELOC_OP_PAGE, uloc, val); +- if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) +- goto overflow; +- result = extract_insn_imm(result, 21, 12); +- result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_TLSDESC_LD64_LO12: +- result = calc_reloc(RELOC_OP_ABS, uloc, val); +- // don't check result & 7 == 0. +- result = extract_insn_imm(result, 9, 3); +- result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_TLSDESC_ADD_LO12: +- result = calc_reloc(RELOC_OP_ABS, uloc, val); +- result = extract_insn_imm(result, 12, 0); +- result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, +- (unsigned long)result); +- *(__le32 *)loc = cpu_to_le32((__le32)result); +- break; +- case R_AARCH64_TLSDESC_CALL: +- // this is a blr instruction, don't need to modify +- break; ++ /* Perform the static relocation. */ ++ switch (GELF_R_TYPE(rel[i].r_info)) { ++ case R_AARCH64_NONE: ++ break; ++ /* Data relocations. */ ++ case R_AARCH64_ABS64: ++ result = calc_reloc(RELOC_OP_ABS, uloc, val); ++ *(s64*)loc = result; ++ break; ++ case R_AARCH64_ABS32: ++ result = calc_reloc(RELOC_OP_ABS, uloc, val); ++ if (result < -(s64)BIT(31) || result >= (s64)BIT(32)) { ++ goto overflow; ++ } ++ *(s32*)loc = (s32)result; ++ break; ++ case R_AARCH64_ABS16: ++ result = calc_reloc(RELOC_OP_ABS, uloc, val); ++ if (result < -(s64)BIT(15) || result >= (s64)BIT(16)) { ++ goto overflow; ++ } ++ *(s16*)loc = (s16)result; ++ break; ++ case R_AARCH64_PREL64: ++ result = calc_reloc(RELOC_OP_PREL, uloc, val); ++ *(s64*)loc = result; ++ break; ++ case R_AARCH64_PREL32: ++ result = calc_reloc(RELOC_OP_PREL, uloc, val); ++ if (result < -(s64)BIT(31) || result >= (s64)BIT(32)) { ++ goto overflow; ++ } ++ *(s32*)loc = (s32)result; ++ break; ++ case R_AARCH64_PREL16: ++ result = calc_reloc(RELOC_OP_PREL, uloc, val); ++ if (result < -(s64)BIT(15) || result >= (s64)BIT(16)) { ++ goto overflow; ++ } ++ *(s16*)loc = (s16)result; ++ break; ++ /* Immediate instruction relocations. */ ++ case R_AARCH64_LD_PREL_LO19: ++ result = calc_reloc(RELOC_OP_PREL, uloc, val); ++ if (result < -(s64)BIT(20) || result >= (s64)BIT(20)) { ++ goto overflow; ++ } ++ result = extract_insn_imm(result, 19, 2); ++ result = insert_insn_imm(AARCH64_INSN_IMM_19, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_ADR_PREL_LO21: ++ result = calc_reloc(RELOC_OP_PREL, uloc, val); ++ if (result < -(s64)BIT(20) || result >= (s64)BIT(20)) { ++ goto overflow; ++ } ++ result = extract_insn_imm(result, 21, 0); ++ result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_ADR_PREL_PG_HI21: ++ result = calc_reloc(RELOC_OP_PAGE, uloc, val); ++ if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) { ++ goto overflow; ++ } ++ result = extract_insn_imm(result, 21, 12); ++ result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_ADR_PREL_PG_HI21_NC: ++ result = calc_reloc(RELOC_OP_PAGE, uloc, val); ++ result = extract_insn_imm(result, 21, 12); ++ result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_ADD_ABS_LO12_NC: ++ case R_AARCH64_LDST8_ABS_LO12_NC: ++ result = calc_reloc(RELOC_OP_ABS, uloc, val); ++ result = extract_insn_imm(result, 12, 0); ++ result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_LDST16_ABS_LO12_NC: ++ result = calc_reloc(RELOC_OP_ABS, uloc, val); ++ result = extract_insn_imm(result, 11, 1); ++ result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_LDST32_ABS_LO12_NC: ++ result = calc_reloc(RELOC_OP_ABS, uloc, val); ++ result = extract_insn_imm(result, 10, 2); ++ result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_LDST64_ABS_LO12_NC: ++ result = calc_reloc(RELOC_OP_ABS, uloc, val); ++ result = extract_insn_imm(result, 9, 3); ++ result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_LDST128_ABS_LO12_NC: ++ result = calc_reloc(RELOC_OP_ABS, uloc, val); ++ result = extract_insn_imm(result, 8, 4); ++ result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_TSTBR14: ++ result = calc_reloc(RELOC_OP_PREL, uloc, val); ++ if (result < -(s64)BIT(15) || result >= (s64)BIT(15)) { ++ goto overflow; ++ } ++ result = extract_insn_imm(result, 14, 2); ++ result = insert_insn_imm(AARCH64_INSN_IMM_14, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_CONDBR19: ++ result = calc_reloc(RELOC_OP_PREL, uloc, val); ++ result = extract_insn_imm(result, 19, 2); ++ result = insert_insn_imm(AARCH64_INSN_IMM_19, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_JUMP26: ++ case R_AARCH64_CALL26: ++ result = calc_reloc(RELOC_OP_PREL, uloc, val); ++ if (result < -(s64)BIT(27) || result >= (s64)BIT(27)) { ++ log_debug( ++ "R_AARCH64_CALL26 overflow: result = 0x%lx, uloc = " ++ "0x%lx, val = 0x%lx\n", ++ result, (unsigned long)uloc, val); ++ val = search_insert_plt_table(uelf, val, (u64)&val); ++ log_debug("R_AARCH64_CALL26 overflow: plt.addr = 0x%lx\n", ++ val); ++ if (!val) { ++ goto overflow; ++ } ++ result = calc_reloc(RELOC_OP_PREL, uloc, val); ++ } ++ result = extract_insn_imm(result, 26, 2); ++ result = insert_insn_imm(AARCH64_INSN_IMM_26, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_ADR_GOT_PAGE: ++ result = calc_reloc(RELOC_OP_PAGE, uloc, val); ++ if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) { ++ goto overflow; ++ } ++ result = extract_insn_imm(result, 21, 12); ++ result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_LD64_GOT_LO12_NC: ++ result = calc_reloc(RELOC_OP_ABS, uloc, val); ++ // don't check result & 7 == 0. ++ // sometimes, result & 7 != 0, it works fine. ++ result = extract_insn_imm(result, 9, 3); ++ result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_TLSLE_ADD_TPREL_HI12: ++ result = (long)(ALIGN(TCB_SIZE, uelf->relf->tls_align) + val); ++ if (result < 0 || result >= (s64)BIT(24)) { ++ goto overflow; ++ } ++ result = extract_insn_imm(result, 12, 12); ++ result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: ++ result = (long)(ALIGN(TCB_SIZE, uelf->relf->tls_align) + val); ++ result = extract_insn_imm(result, 12, 0); ++ result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_TLSDESC_ADR_PAGE21: ++ result = calc_reloc(RELOC_OP_PAGE, uloc, val); ++ if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) { ++ goto overflow; ++ } ++ result = extract_insn_imm(result, 21, 12); ++ result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_TLSDESC_LD64_LO12: ++ result = calc_reloc(RELOC_OP_ABS, uloc, val); ++ // don't check result & 7 == 0. ++ result = extract_insn_imm(result, 9, 3); ++ result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_TLSDESC_ADD_LO12: ++ result = calc_reloc(RELOC_OP_ABS, uloc, val); ++ result = extract_insn_imm(result, 12, 0); ++ result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, ++ (unsigned long)result); ++ *(__le32*)loc = cpu_to_le32((__le32)result); ++ break; ++ case R_AARCH64_TLSDESC_CALL: ++ // this is a blr instruction, don't need to modify ++ break; + +- default: +- log_error("upatch: unsupported RELA relocation: %lu\n", +- GELF_R_TYPE(rel[i].r_info)); +- return -ENOEXEC; +- } +- } +- return 0; ++ 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 reloc %lx\n", +- (int)GELF_R_TYPE(rel[i].r_info), val, result); +- return -ENOEXEC; ++ log_error("upatch: overflow in relocation type %d val %lx reloc %lx\n", ++ (int)GELF_R_TYPE(rel[i].r_info), val, result); ++ return -ENOEXEC; + } +diff --git a/upatch-manage/arch/riscv64/insn.h b/upatch-manage/arch/riscv64/insn.h +new file mode 100644 +index 0000000000000000000000000000000000000000..8ab4daf7ca4091d3f259663df98e4f53ba789834 +--- /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 0000000000000000000000000000000000000000..91926fa22c2788503711dac2a38094d18e6ea349 +--- /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 0000000000000000000000000000000000000000..c5691c940ade26bb6ddac8b09275646b3b675453 +--- /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 0000000000000000000000000000000000000000..6c28bf8c758b9749e09b0e65e534b9fb32b55253 +--- /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 0000000000000000000000000000000000000000..127fcc0070b916a0d921a72fb0479aad5df9c59c +--- /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/arch/x86_64/relocation.c b/upatch-manage/arch/x86_64/relocation.c +index 657f014d5009dc962826b2cc065ee2020cd12bf3..0fd09f2d85e21ce1f07d434deb3b98772de184fc 100644 +--- a/upatch-manage/arch/x86_64/relocation.c ++++ b/upatch-manage/arch/x86_64/relocation.c +@@ -25,116 +25,125 @@ + #include "upatch-relocation.h" + + int apply_relocate_add(struct upatch_elf *uelf, unsigned int symindex, +- unsigned int relsec) ++ unsigned int relsec) + { +- unsigned int i; +- GElf_Sym *sym; +- void *loc, *real_loc; +- u64 val; +- const char *sym_name; +- GElf_Xword tls_size; +- GElf_Shdr *shdrs = (void *)uelf->info.shdrs; +- GElf_Rela *rel = (void *)shdrs[relsec].sh_addr; ++ unsigned int i; ++ GElf_Sym *sym; ++ void *loc, *real_loc; ++ u64 val; ++ const char *sym_name; ++ GElf_Xword tls_size; ++ GElf_Shdr *shdrs = (void *)uelf->info.shdrs; ++ GElf_Rela *rel = (void *)shdrs[relsec].sh_addr; + +- log_debug("Applying relocate section %u to %u\n", relsec, +- shdrs[relsec].sh_info); ++ log_debug("Applying relocate section %u to %u\n", relsec, shdrs[relsec].sh_info); + +- for (i = 0; i < shdrs[relsec].sh_size / sizeof(*rel); i++) { +- /* This is where to make the change, calculate it from kernel address */ +- loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addr + +- rel[i].r_offset; ++ for (i = 0; i < shdrs[relsec].sh_size / sizeof(*rel); i++) { ++ /* This is where to make the change, calculate it from kernel address */ ++ loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addr + ++ rel[i].r_offset; + +- real_loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addralign + +- rel[i].r_offset; ++ real_loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addralign + ++ rel[i].r_offset; + +- /* This is the symbol it is referring to. Note that all +- undefined symbols have been resolved. */ +- sym = (GElf_Sym *)shdrs[symindex].sh_addr + +- GELF_R_SYM(rel[i].r_info); ++ /* This is the symbol it is referring to. Note that all ++ undefined symbols have been resolved. */ ++ sym = (GElf_Sym *)shdrs[symindex].sh_addr + ++ GELF_R_SYM(rel[i].r_info); + +- if (GELF_ST_TYPE(sym[i].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; ++ if (GELF_ST_TYPE(sym[i].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; ++ } + +- log_debug("type %d st_value %lx r_addend %lx loc %lx\n", +- (int)GELF_R_TYPE(rel[i].r_info), sym->st_value, +- rel[i].r_addend, (u64)loc); ++ log_debug("type %d st_value %lx r_addend %lx loc %lx\n", ++ (int)GELF_R_TYPE(rel[i].r_info), sym->st_value, ++ rel[i].r_addend, (u64)loc); + +- val = sym->st_value + (unsigned long)rel[i].r_addend; +- switch (GELF_R_TYPE(rel[i].r_info)) { +- case R_X86_64_NONE: +- break; +- case R_X86_64_64: +- if (*(u64 *)loc != 0) +- goto invalid_relocation; +- memcpy(loc, &val, 8); +- break; +- case R_X86_64_32: +- if (*(u32 *)loc != 0) +- goto invalid_relocation; +- memcpy(loc, &val, 4); +- if (val != *(u32 *)loc && +- (GELF_ST_TYPE(sym->st_info) != STT_SECTION)) +- goto overflow; +- break; +- case R_X86_64_32S: +- if (*(s32 *)loc != 0) +- goto invalid_relocation; +- memcpy(loc, &val, 4); +- if ((s64)val != *(s32 *)loc && +- (GELF_ST_TYPE(sym->st_info) != STT_SECTION)) +- goto overflow; +- break; +- case R_X86_64_TLSGD: +- case R_X86_64_GOTTPOFF: +- case R_X86_64_GOTPCRELX: +- case R_X86_64_REX_GOTPCRELX: +- if (sym->st_value == 0) +- goto overflow; +- /* G + GOT + A*/ +- val = sym->st_value + (unsigned long)rel[i].r_addend; +- /* fallthrough*/ +- case R_X86_64_PC32: +- case R_X86_64_PLT32: +- if (*(u32 *)loc != 0) +- goto invalid_relocation; +- val -= (u64)real_loc; +- memcpy(loc, &val, 4); +- break; +- case R_X86_64_PC64: +- if (*(u64 *)loc != 0) +- goto invalid_relocation; +- val -= (u64)real_loc; +- memcpy(loc, &val, 8); +- break; +- case R_X86_64_TPOFF32: +- tls_size = ALIGN(uelf->relf->tls_size, +- uelf->relf->tls_align); +- // %fs + val - tls_size +- if (val >= tls_size) +- goto overflow; +- val -= (u64)tls_size; +- memcpy(loc, &val, 4); +- break; +- default: +- log_error("Unknown rela relocation: %lu\n", +- GELF_R_TYPE(rel[i].r_info)); +- return -ENOEXEC; +- } +- } ++ val = sym->st_value + (unsigned long)rel[i].r_addend; ++ switch (GELF_R_TYPE(rel[i].r_info)) { ++ case R_X86_64_NONE: ++ break; ++ case R_X86_64_64: ++ if (*(u64 *)loc != 0) { ++ goto invalid_relocation; ++ } ++ memcpy(loc, &val, 8); ++ break; ++ case R_X86_64_32: ++ if (*(u32 *)loc != 0) { ++ goto invalid_relocation; ++ } ++ memcpy(loc, &val, 4); ++ if (val != *(u32 *)loc && ++ (GELF_ST_TYPE(sym->st_info) != STT_SECTION)) { ++ goto overflow; ++ } ++ break; ++ case R_X86_64_32S: ++ if (*(s32 *)loc != 0) { ++ goto invalid_relocation; ++ } ++ memcpy(loc, &val, 4); ++ if ((s64)val != *(s32 *)loc && ++ (GELF_ST_TYPE(sym->st_info) != STT_SECTION)) { ++ goto overflow; ++ } ++ break; ++ case R_X86_64_TLSGD: ++ case R_X86_64_GOTTPOFF: ++ case R_X86_64_GOTPCRELX: ++ case R_X86_64_REX_GOTPCRELX: ++ if (sym->st_value == 0) { ++ goto overflow; ++ } ++ /* G + GOT + A */ ++ val = sym->st_value + (unsigned long)rel[i].r_addend; ++ /* fallthrough */ ++ case R_X86_64_PC32: ++ case R_X86_64_PLT32: ++ if (*(u32 *)loc != 0) { ++ goto invalid_relocation; ++ } ++ val -= (u64)real_loc; ++ memcpy(loc, &val, 4); ++ break; ++ case R_X86_64_PC64: ++ if (*(u64 *)loc != 0) { ++ goto invalid_relocation; ++ } ++ val -= (u64)real_loc; ++ memcpy(loc, &val, 8); ++ break; ++ case R_X86_64_TPOFF32: ++ tls_size = ALIGN(uelf->relf->tls_size, ++ uelf->relf->tls_align); ++ // %fs + val - tls_size ++ if (val >= tls_size) { ++ goto overflow; ++ } ++ val -= (u64)tls_size; ++ memcpy(loc, &val, 4); ++ break; ++ default: ++ log_error("Unknown rela relocation: %lu\n", ++ GELF_R_TYPE(rel[i].r_info)); ++ return -ENOEXEC; ++ } ++ } + return 0; + + invalid_relocation: +- log_error("upatch: Skipping invalid relocation target, \ +- existing value is nonzero for type %d, loc %p, name %s\n", +- (int)GELF_R_TYPE(rel[i].r_info), loc, sym_name); +- return -ENOEXEC; ++ log_error("upatch: Skipping invalid relocation target, \ ++ existing value is nonzero for type %d, loc %p, name %s\n", ++ (int)GELF_R_TYPE(rel[i].r_info), loc, sym_name); ++ return -ENOEXEC; + + overflow: +- log_error("upatch: overflow in relocation type %d name %s\n", +- (int)GELF_R_TYPE(rel[i].r_info), sym_name); +- return -ENOEXEC; ++ log_error("upatch: overflow in relocation type %d name %s\n", ++ (int)GELF_R_TYPE(rel[i].r_info), sym_name); ++ return -ENOEXEC; + } +diff --git a/upatch-manage/upatch-common.h b/upatch-manage/upatch-common.h +index 0c0784d330028124f4cd96a0b4d28263097e320b..46035ac41be83ca808015f40771c33215aa18809 100644 +--- a/upatch-manage/upatch-common.h ++++ b/upatch-manage/upatch-common.h +@@ -24,26 +24,26 @@ + #include + #include + +-#define ALLOC_LINK(_new, _list) \ +- { \ +- (_new) = calloc(1, sizeof(*(_new))); \ +- if (!(_new)) \ +- ERROR("calloc"); \ +- INIT_LIST_HEAD(&(_new)->list); \ +- if (_list) \ +- list_add(&(_new)->list, (_list)); \ +- } ++#define ALLOC_LINK(_new, _list) \ ++ do { \ ++ (_new) = calloc(1, sizeof(*(_new))); \ ++ if (!(_new)) \ ++ ERROR("calloc"); \ ++ INIT_LIST_HEAD(&(_new)->list); \ ++ if (_list) \ ++ list_add(&(_new)->list, (_list)); \ ++ } while (0) + + static inline int page_shift(long n) + { +- int res = -1; ++ int res = -1; + +- while (n) { +- res++; +- n >>= 1; +- } ++ while (n) { ++ res++; ++ n >>= 1; ++ } + +- return res; ++ return res; + } + + #ifndef PAGE_SIZE +@@ -52,11 +52,11 @@ static inline int page_shift(long n) + #define PAGE_SHIFT page_shift(PAGE_SIZE) + #endif + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +-#define ALIGN(x, a) (((x) + (a)-1) & (~((a)-1))) ++#define ALIGN(x, a) (((x) + (a) - 1) & (~((a) - 1))) + #define PAGE_ALIGN(x) ALIGN((x), (unsigned long)PAGE_SIZE) + +-#define ROUND_DOWN(x, m) ((x) & ~((m)-1)) +-#define ROUND_UP(x, m) (((x) + (m)-1) & ~((m)-1)) ++#define ROUND_DOWN(x, m) ((x) & ~((m) - 1)) ++#define ROUND_UP(x, m) (((x) + (m) - 1) & ~((m) - 1)) + + #define BIT(x) (1UL << (x)) + +@@ -64,10 +64,10 @@ static inline int page_shift(long n) + + static inline long get_microseconds(struct timeval *start, struct timeval *end) + { +- long sec = end->tv_sec - start->tv_sec; +- long usec = end->tv_usec - start->tv_usec; ++ long sec = end->tv_sec - start->tv_sec; ++ long usec = end->tv_usec - start->tv_usec; + +- return sec * SEC2MICRO + usec; ++ return sec * SEC2MICRO + usec; + } + + bool streql(const char *, const char *); +diff --git a/upatch-manage/upatch-elf.h b/upatch-manage/upatch-elf.h +index 51013b583f3ed2fe5c96e93dce0db6aadc87a9a1..45a06b98616d7d0995ca542b9e74700938d2986e 100644 +--- a/upatch-manage/upatch-elf.h ++++ b/upatch-manage/upatch-elf.h +@@ -45,120 +45,120 @@ + #define UPATCH_ID_LEN 40 + + struct upatch_func_addr { +- unsigned long new_addr; +- unsigned long new_size; +- unsigned long old_addr; +- unsigned long old_size; ++ unsigned long new_addr; ++ unsigned long new_size; ++ unsigned long old_addr; ++ unsigned long old_size; + }; + + struct upatch_info_func { +- struct upatch_func_addr addr; +- unsigned long old_insn[2]; +- unsigned long new_insn; +- char *name; ++ struct upatch_func_addr addr; ++ unsigned long old_insn[2]; ++ unsigned long new_insn; ++ char* name; + }; + + struct upatch_info { +- char magic[7]; // upatch magic +- char id[UPATCH_ID_LEN + 1]; // upatch id +- unsigned long size; // upatch_info and upatch_info_func size +- unsigned long start; // upatch vma start +- unsigned long end; // upatch vma end +- unsigned long changed_func_num; +- struct upatch_info_func *funcs; +- char *func_names; +- unsigned long func_names_size; ++ char magic[7]; // upatch magic ++ char id[UPATCH_ID_LEN + 1]; // upatch id ++ unsigned long size; // upatch_info and upatch_info_func size ++ unsigned long start; // upatch vma start ++ unsigned long end; // upatch vma end ++ unsigned long changed_func_num; ++ struct upatch_info_func* funcs; ++ char* func_names; ++ unsigned long func_names_size; + }; + + struct upatch_layout { +- /* The actual code + data. */ +- void *kbase; +- void *base; +- /* Total size. */ +- unsigned long size; +- /* The size of the executable code. */ +- unsigned long text_size; +- /* Size of RO section of the module (text+rodata) */ +- unsigned long ro_size; +- /* Size of RO after init section, not use it now */ +- unsigned long ro_after_init_size; +- /* The size of the info. */ +- unsigned long info_size; ++ /* The actual code + data. */ ++ void* kbase; ++ void* base; ++ /* Total size. */ ++ unsigned long size; ++ /* The size of the executable code. */ ++ unsigned long text_size; ++ /* Size of RO section of the module (text+rodata) */ ++ unsigned long ro_size; ++ /* Size of RO after init section, not use it now */ ++ unsigned long ro_after_init_size; ++ /* The size of the info. */ ++ unsigned long info_size; + }; + + struct upatch_patch_func { +- struct upatch_func_addr addr; +- unsigned long sympos; /* handle local symbols */ +- char *name; ++ struct upatch_func_addr addr; ++ unsigned long sympos; /* handle local symbols */ ++ char* name; + }; + + struct elf_info { +- const char *name; +- ino_t inode; +- void *patch_buff; +- size_t patch_size; +- +- GElf_Ehdr *hdr; +- GElf_Shdr *shdrs; +- char *shstrtab; +- +- unsigned int num_build_id; +- bool is_pie; +- bool is_dyn; ++ const char* name; ++ ino_t inode; ++ void* patch_buff; ++ size_t patch_size; ++ ++ GElf_Ehdr* hdr; ++ GElf_Shdr* shdrs; ++ char* shstrtab; ++ ++ unsigned int num_build_id; ++ bool is_pie; ++ bool is_dyn; + }; + + struct running_elf { +- struct elf_info info; ++ struct elf_info info; + +- unsigned long num_syms; +- char *strtab; +- char *dynstrtab; ++ unsigned long num_syms; ++ char* strtab; ++ char* dynstrtab; + +- GElf_Phdr *phdrs; +- GElf_Xword tls_size; +- GElf_Xword tls_align; ++ GElf_Phdr* phdrs; ++ GElf_Xword tls_size; ++ GElf_Xword tls_align; + +- struct { +- unsigned int sym, str; +- unsigned int rela_dyn, rela_plt; +- unsigned int dynsym, dynstr, dynamic; +- } index; ++ struct { ++ unsigned int sym, str; ++ unsigned int rela_dyn, rela_plt; ++ unsigned int dynsym, dynstr, dynamic; ++ } index; + +- /* load bias, used to handle ASLR */ +- unsigned long load_bias; +- unsigned long load_start; ++ /* load bias, used to handle ASLR */ ++ unsigned long load_bias; ++ unsigned long load_start; + }; + + struct upatch_elf { +- struct elf_info info; ++ struct elf_info info; + +- unsigned long num_syms; +- char *strtab; ++ unsigned long num_syms; ++ char* strtab; + +- struct { +- unsigned int sym, str; +- unsigned int upatch_funcs; +- unsigned int upatch_string; +- } index; ++ struct { ++ unsigned int sym, str; ++ unsigned int upatch_funcs; ++ unsigned int upatch_string; ++ } index; + +- unsigned long symoffs, stroffs, core_typeoffs; +- unsigned long jmp_offs; +- unsigned int jmp_cur_entry, jmp_max_entry; ++ unsigned long symoffs, stroffs, core_typeoffs; ++ unsigned long jmp_offs; ++ unsigned int jmp_cur_entry, jmp_max_entry; + +- /* memory layout for patch */ +- struct upatch_layout core_layout; ++ /* memory layout for patch */ ++ struct upatch_layout core_layout; + +- struct running_elf *relf; ++ struct running_elf* relf; + }; + +-int upatch_init(struct upatch_elf *, const char *); +-int binary_init(struct running_elf *, const char *); +-void upatch_close(struct upatch_elf *); +-void binary_close(struct running_elf *); ++int upatch_init(struct upatch_elf*, const char*); ++int binary_init(struct running_elf*, const char*); ++void upatch_close(struct upatch_elf*); ++void binary_close(struct running_elf*); + +-bool check_build_id(struct elf_info *, struct elf_info *); ++bool check_build_id(struct elf_info*, struct elf_info*); + +-bool is_upatch_section(const char *); ++bool is_upatch_section(const char*); + + bool is_note_section(GElf_Word); + +diff --git a/upatch-manage/upatch-manage.c b/upatch-manage/upatch-manage.c +index 8b10a4264c87b0d416213fd928234b81e3f28428..89d2fef1d2f8719656d8196776ee2b1630a45d70 100644 +--- a/upatch-manage/upatch-manage.c ++++ b/upatch-manage/upatch-manage.c +@@ -31,196 +31,199 @@ + #include "upatch-patch.h" + #include "upatch-stack-check.h" + +-#define PROG_VERSION "upatch-manage "BUILD_VERSION ++#define PROG_VERSION "upatch-manage " BUILD_VERSION + #define COMMAND_SIZE 4 + + enum loglevel loglevel = NORMAL; +-char *logprefix; ++char* logprefix; + +-char *command[COMMAND_SIZE] = { "", "patch", "unpatch", "info" }; ++char* command[COMMAND_SIZE] = {"", "patch", "unpatch", "info"}; + enum Command { +- DEFAULT, +- PATCH, +- UNPATCH, +- INFO, ++ DEFAULT, ++ PATCH, ++ UNPATCH, ++ INFO, + }; + + struct arguments { +- int cmd; +- int pid; +- char *upatch; +- char *binary; +- char *uuid; +- bool verbose; ++ int cmd; ++ int pid; ++ char* upatch; ++ char* binary; ++ char* uuid; ++ bool verbose; + }; + + static struct argp_option options[] = { +- { "verbose", 'v', NULL, 0, "Show verbose output", 0 }, +- { "uuid", 'U', "uuid", 0, "the uuid of the upatch", 0 }, +- { "pid", 'p', "pid", 0, "the pid of the user-space process", 0 }, +- { "upatch", 'u', "upatch", 0, "the upatch file", 0 }, +- { "binary", 'b', "binary", 0, "the binary file", 0 }, +- { "cmd", 0, "patch", 0, "Apply a upatch file to a user-space process", 0 }, +- { "cmd", 0, "unpatch", 0, +- "Unapply a upatch file to a user-space process", 0 }, +- { NULL } +-}; ++ {"verbose", 'v', NULL, 0, "Show verbose output", 0}, ++ {"uuid", 'U', "uuid", 0, "the uuid of the upatch", 0}, ++ {"pid", 'p', "pid", 0, "the pid of the user-space process", 0}, ++ {"upatch", 'u', "upatch", 0, "the upatch file", 0}, ++ {"binary", 'b', "binary", 0, "the binary file", 0}, ++ {"cmd", 0, "patch", 0, "Apply a upatch file to a user-space process", 0}, ++ {"cmd", 0, "unpatch", 0, "Unapply a upatch file to a user-space process", 0}, ++ {NULL}}; + + static char program_doc[] = "Operate a upatch file on the user-space process"; + + static char args_doc[] = +- " --pid --upatch --binary --uuid "; ++ " --pid --upatch --binary --uuid " ++ ""; + +-const char *argp_program_version = PROG_VERSION; ++const char* argp_program_version = PROG_VERSION; + +-static error_t check_opt(struct argp_state *state) ++static error_t check_opt(struct argp_state* state) + { +- struct arguments *arguments = state->input; +- +- if (arguments->cmd == DEFAULT) { +- argp_usage(state); +- return ARGP_ERR_UNKNOWN; +- } +- switch (arguments->cmd) { +- case PATCH: +- case UNPATCH: +- case INFO: +- if (!arguments->pid || arguments->upatch == NULL || +- arguments->binary == NULL || arguments->uuid == NULL) { +- argp_usage(state); +- return ARGP_ERR_UNKNOWN; +- } +- default: +- break; +- } +- return 0; ++ struct arguments* arguments = state->input; ++ ++ if (arguments->cmd == DEFAULT) { ++ argp_usage(state); ++ return ARGP_ERR_UNKNOWN; ++ } ++ switch (arguments->cmd) { ++ case PATCH: ++ case UNPATCH: ++ case INFO: ++ if (!arguments->pid || arguments->upatch == NULL || ++ arguments->binary == NULL || arguments->uuid == NULL) { ++ argp_usage(state); ++ return ARGP_ERR_UNKNOWN; ++ } ++ default: ++ break; ++ } ++ return 0; + } + +-static error_t parse_opt(int key, char *arg, struct argp_state *state) ++static error_t parse_opt(int key, char* arg, struct argp_state* state) + { +- struct arguments *arguments = state->input; +- +- switch (key) { +- case 'v': +- arguments->verbose = true; +- break; +- case 'p': +- arguments->pid = atoi(arg); +- break; +- case 'u': +- arguments->upatch = arg; +- break; +- case 'b': +- arguments->binary = arg; +- break; +- case 'U': +- arguments->uuid = arg; +- break; +- case ARGP_KEY_ARG: +- if (state->arg_num >= 1) +- argp_usage(state); +- if (arguments->cmd != DEFAULT) +- argp_usage(state); +- for (int i = 1; i < COMMAND_SIZE; ++i) { +- if (!strcmp(arg, command[i])) { +- arguments->cmd = i; +- break; +- } +- } +- break; +- case ARGP_KEY_END: +- return check_opt(state); +- default: +- return ARGP_ERR_UNKNOWN; +- } +- return 0; ++ struct arguments* arguments = state->input; ++ ++ switch (key) { ++ case 'v': ++ arguments->verbose = true; ++ break; ++ case 'p': ++ arguments->pid = atoi(arg); ++ break; ++ case 'u': ++ arguments->upatch = arg; ++ break; ++ case 'b': ++ arguments->binary = arg; ++ break; ++ case 'U': ++ arguments->uuid = arg; ++ break; ++ case ARGP_KEY_ARG: ++ if (state->arg_num >= 1) { ++ argp_usage(state); ++ } ++ if (arguments->cmd != DEFAULT) { ++ argp_usage(state); ++ } ++ for (int i = 1; i < COMMAND_SIZE; ++i) { ++ if (!strcmp(arg, command[i])) { ++ arguments->cmd = i; ++ break; ++ } ++ } ++ break; ++ case ARGP_KEY_END: ++ return check_opt(state); ++ default: ++ return ARGP_ERR_UNKNOWN; ++ } ++ return 0; + } + +-static struct argp argp = { options, parse_opt, args_doc, program_doc, NULL, NULL, NULL }; ++static struct argp argp = {options, parse_opt, args_doc, program_doc, ++ NULL, NULL, NULL}; + +-int patch_upatch(const char *uuid, const char *binary_path, const char *upatch_path, int pid) ++int patch_upatch(const char* uuid, const char* binary_path, ++ const char* upatch_path, int pid) + { +- struct upatch_elf uelf; +- struct running_elf relf; +- memset(&uelf, 0, sizeof(struct upatch_elf)); +- memset(&relf, 0, sizeof(struct running_elf)); +- +- int ret = upatch_init(&uelf, upatch_path); +- if (ret) { +- log_error("Failed to initialize patch, pid=%d, ret=%d\n", pid, ret); +- goto out; +- } +- +- ret = process_patch(pid, &uelf, &relf, uuid, binary_path); +- if (ret) { +- log_error("Failed to patch process, pid=%d, ret=%d\n", pid, ret); +- goto out; +- } ++ struct upatch_elf uelf; ++ struct running_elf relf; ++ memset(&uelf, 0, sizeof(struct upatch_elf)); ++ memset(&relf, 0, sizeof(struct running_elf)); ++ ++ int ret = upatch_init(&uelf, upatch_path); ++ if (ret) { ++ log_error("Failed to initialize patch, pid=%d, ret=%d\n", pid, ret); ++ goto out; ++ } ++ ++ ret = process_patch(pid, &uelf, &relf, uuid, binary_path); ++ if (ret) { ++ log_error("Failed to patch process, pid=%d, ret=%d\n", pid, ret); ++ goto out; ++ } + + out: +- upatch_close(&uelf); +- binary_close(&relf); ++ upatch_close(&uelf); ++ binary_close(&relf); + +- return ret; ++ return ret; + } + +-int unpatch_upatch(const char *uuid, int pid) ++int unpatch_upatch(const char* uuid, int pid) + { +- int ret = 0; ++ int ret = 0; + +- ret = process_unpatch(pid, uuid); +- if (ret) { +- log_error("Failed to unpatch process, pid=%d, ret=%d\n", pid, ret); +- return ret; +- } ++ ret = process_unpatch(pid, uuid); ++ if (ret) { ++ log_error("Failed to unpatch process, pid=%d, ret=%d\n", pid, ret); ++ return ret; ++ } + +- return 0; ++ return 0; + } + + int info_upatch(int pid) + { +- int ret = process_info(pid); +- if (ret != 0) { +- log_error("Failed to get patch info, pid=%d, ret=%d\n", pid, ret); +- return ret; +- } ++ int ret = process_info(pid); ++ if (ret != 0) { ++ log_error("Failed to get patch info, pid=%d, ret=%d\n", pid, ret); ++ return ret; ++ } + +- return 0; ++ return 0; + } + +-int main(int argc, char *argv[]) ++int main(int argc, char* argv[]) + { +- struct arguments args; +- int ret; +- +- memset(&args, 0, sizeof(struct arguments)); +- argp_parse(&argp, argc, argv, 0, NULL, &args); +- if (args.verbose) { +- loglevel = DEBUG; +- } +- +- logprefix = basename(args.upatch); +- log_debug("PID: %d\n", args.pid); +- log_debug("UUID: %s\n", args.uuid); +- log_debug("Patch: %s\n", args.upatch); +- log_debug("Binary: %s\n", args.binary); +- +- args.pid = args.pid & INT32_MAX; +- switch (args.cmd) { +- case PATCH: +- ret = patch_upatch(args.uuid, args.binary, args.upatch, args.pid); +- break; +- case UNPATCH: +- ret = unpatch_upatch(args.uuid, args.pid); +- break; +- case INFO: +- ret = info_upatch(args.pid); +- break; +- default: +- ERROR("Unknown command"); +- ret = EINVAL; +- break; +- } +- +- return abs(ret); ++ struct arguments args; ++ int ret; ++ ++ memset(&args, 0, sizeof(struct arguments)); ++ argp_parse(&argp, argc, argv, 0, NULL, &args); ++ if (args.verbose) { ++ loglevel = DEBUG; ++ } ++ ++ logprefix = basename(args.upatch); ++ log_debug("PID: %d\n", args.pid); ++ log_debug("UUID: %s\n", args.uuid); ++ log_debug("Patch: %s\n", args.upatch); ++ log_debug("Binary: %s\n", args.binary); ++ ++ args.pid = args.pid & INT32_MAX; ++ switch (args.cmd) { ++ case PATCH: ++ ret = patch_upatch(args.uuid, args.binary, args.upatch, args.pid); ++ break; ++ case UNPATCH: ++ ret = unpatch_upatch(args.uuid, args.pid); ++ break; ++ case INFO: ++ ret = info_upatch(args.pid); ++ break; ++ default: ++ ERROR("Unknown command"); ++ ret = EINVAL; ++ break; ++ } ++ ++ return abs(ret); + } +diff --git a/upatch-manage/upatch-patch.c b/upatch-manage/upatch-patch.c +index 8aaa3b460283f249a104412da3a24ed4f0fac091..0b38d46ef6eda3b9e22aff781043ecb1fe7036cd 100644 +--- a/upatch-manage/upatch-patch.c ++++ b/upatch-manage/upatch-patch.c +@@ -45,175 +45,173 @@ + /* If this is set, the section belongs in the init part of the module */ + #define BITS_PER_LONG sizeof(unsigned long) * 8 + +-static GElf_Off calculate_load_address(struct running_elf *relf, +- bool check_code) +-{ +- int i; +- GElf_Off min_addr = (unsigned long)-1; +- +- /* TODO: for ET_DYN, consider check PIE */ +- if (relf->info.hdr->e_type != ET_EXEC && +- relf->info.hdr->e_type != ET_DYN) { +- log_error("invalid elf type, it should be ET_EXEC or ET_DYN\n"); +- goto out; +- } +- +- for (i = 0; i < relf->info.hdr->e_phnum; ++i) { +- if (relf->phdrs[i].p_type != PT_LOAD) +- continue; +- if (!check_code || +- (check_code && (relf->phdrs[i].p_flags & PF_X))) +- min_addr = (min_addr > relf->phdrs[i].p_vaddr) ? +- relf->phdrs[i].p_vaddr : +- min_addr; +- // min_addr = min(min_addr, relf->phdrs[i].p_vaddr); +- } ++static GElf_Off calculate_load_address(struct running_elf* relf, bool check_code) ++{ ++ int i; ++ GElf_Off min_addr = (unsigned long)-1; ++ ++ /* TODO: for ET_DYN, consider check PIE */ ++ if (relf->info.hdr->e_type != ET_EXEC && relf->info.hdr->e_type != ET_DYN) { ++ log_error("invalid elf type, it should be ET_EXEC or ET_DYN\n"); ++ goto out; ++ } ++ ++ for (i = 0; i < relf->info.hdr->e_phnum; ++i) { ++ if (relf->phdrs[i].p_type != PT_LOAD) { ++ continue; ++ } ++ if (!check_code || (check_code && (relf->phdrs[i].p_flags & PF_X))) { ++ min_addr = (min_addr > relf->phdrs[i].p_vaddr) ++ ? relf->phdrs[i].p_vaddr ++ : min_addr; ++ } ++ // min_addr = min(min_addr, relf->phdrs[i].p_vaddr); ++ } + + out: +- return min_addr; ++ return min_addr; + } + +-static unsigned long calculate_mem_load(struct object_file *obj) ++static unsigned long calculate_mem_load(struct object_file* obj) + { +- struct obj_vm_area *ovma; +- unsigned long load_addr = (unsigned long)-1; ++ struct obj_vm_area* ovma; ++ unsigned long load_addr = (unsigned long)-1; ++ ++ list_for_each_entry(ovma, &obj->vma, list) { ++ if (ovma->inmem.prot & PROT_EXEC) { ++ load_addr = ++ (load_addr > ovma->inmem.start) ? ovma->inmem.start : load_addr; ++ } ++ } + +- list_for_each_entry(ovma, &obj->vma, list) { +- if (ovma->inmem.prot & PROT_EXEC) { +- load_addr = (load_addr > ovma->inmem.start) ? +- ovma->inmem.start : +- load_addr; +- } +- } ++ return load_addr; ++} + +- return load_addr; ++static int rewrite_section_headers(struct upatch_elf* uelf) ++{ ++ unsigned int i; ++ ++ /* Handle SHF_ALLOC in this part */ ++ ++ /* This should always be true, but let's be sure. */ ++ uelf->info.shdrs[0].sh_addr = 0; ++ uelf->info.shdrs[0].sh_addralign = 0; ++ ++ for (i = 1; i < uelf->info.hdr->e_shnum; i++) { ++ GElf_Shdr* shdr = &uelf->info.shdrs[i]; ++ if (shdr->sh_type != SHT_NOBITS && ++ uelf->info.patch_size < shdr->sh_offset + shdr->sh_size) { ++ log_error("upatch len %lu truncated\n", uelf->info.patch_size); ++ return -ENOEXEC; ++ } ++ ++ /* Mark all sections sh_addr with their address in the ++ temporary image. */ ++ shdr->sh_addr = (size_t)uelf->info.hdr + shdr->sh_offset; ++ log_debug("section %s at 0x%lx\n", uelf->info.shstrtab + shdr->sh_name, ++ shdr->sh_addr); ++ } ++ ++ return 0; + } + +-static int rewrite_section_headers(struct upatch_elf *uelf) ++static unsigned long get_offset(unsigned long* size, GElf_Shdr* sechdr) + { +- unsigned int i; ++ unsigned long ret; + +- /* Handle SHF_ALLOC in this part */ ++ ret = ALIGN(*size, (unsigned long)(sechdr->sh_addralign ?: 1)); ++ *size = (unsigned long)ret + sechdr->sh_size; ++ return ret; ++} + +- /* This should always be true, but let's be sure. */ +- uelf->info.shdrs[0].sh_addr = 0; +- uelf->info.shdrs[0].sh_addralign = 0; +- +- for (i = 1; i < uelf->info.hdr->e_shnum; i++) { +- GElf_Shdr *shdr = &uelf->info.shdrs[i]; +- if (shdr->sh_type != SHT_NOBITS && +- uelf->info.patch_size < shdr->sh_offset + shdr->sh_size) { +- log_error("upatch len %lu truncated\n", +- uelf->info.patch_size); +- return -ENOEXEC; +- } +- +- /* Mark all sections sh_addr with their address in the +- temporary image. */ +- shdr->sh_addr = (size_t)uelf->info.hdr + shdr->sh_offset; +- log_debug("section %s at 0x%lx\n", +- uelf->info.shstrtab + shdr->sh_name, shdr->sh_addr); +- } +- +- return 0; +-} +- +-static unsigned long get_offset(unsigned long *size, +- GElf_Shdr *sechdr) +-{ +- unsigned long ret; +- +- ret = ALIGN(*size, (unsigned long)(sechdr->sh_addralign ?: 1)); +- *size = (unsigned long)ret + sechdr->sh_size; +- return ret; +-} +- +-static void layout_upatch_info(struct upatch_elf *uelf) +-{ +- GElf_Shdr *upatch_func = uelf->info.shdrs + uelf->index.upatch_funcs; +- unsigned long num = upatch_func->sh_size / sizeof(struct upatch_patch_func); +- GElf_Shdr *upatch_string = uelf->info.shdrs + uelf->index.upatch_string; +- +- uelf->core_layout.info_size = uelf->core_layout.size; +- uelf->core_layout.size += sizeof(struct upatch_info) + +- num * sizeof(struct upatch_info_func) + upatch_string->sh_size; +- uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); +-} +- +-static void layout_jmptable(struct upatch_elf *uelf) +-{ +- uelf->jmp_cur_entry = 0; +- uelf->jmp_max_entry = JMP_TABLE_MAX_ENTRY; +- uelf->jmp_offs = ALIGN(uelf->core_layout.size, sizeof(unsigned long)); +- uelf->core_layout.size = +- uelf->jmp_offs + uelf->jmp_max_entry * get_jmp_table_entry(); +- uelf->core_layout.text_size = uelf->core_layout.size; +-} +- +-static void layout_sections(struct upatch_elf *uelf) +-{ +- static unsigned long const masks[][2] = { +- /* NOTE: all executable code must be the last section +- * in this array; otherwise modify the text_size +- * finder in the two loops below */ +- { SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL }, +- { SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL }, +- { SHF_RO_AFTER_INIT | SHF_ALLOC, ARCH_SHF_SMALL }, +- { SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL }, +- { ARCH_SHF_SMALL | SHF_ALLOC, 0 } +- }; +- unsigned int m, i; +- +- for (i = 0; i < uelf->info.hdr->e_shnum; i++) +- uelf->info.shdrs[i].sh_entsize = ~0UL; +- +- log_debug("upatch section allocation order:\n"); +- for (m = 0; m < ARRAY_SIZE(masks); ++m) { +- for (i = 0; i < uelf->info.hdr->e_shnum; ++i) { +- GElf_Shdr *s = &uelf->info.shdrs[i]; +- const char *sname = uelf->info.shstrtab + s->sh_name; +- +- if ((s->sh_flags & masks[m][0]) != masks[m][0] || +- (s->sh_flags & masks[m][1]) || +- s->sh_entsize != ~0UL) +- continue; +- +- s->sh_entsize = +- get_offset(&uelf->core_layout.size, s); +- log_debug("\tm = %d; %s: sh_entsize: 0x%lx\n", m, sname, +- s->sh_entsize); +- } +- switch (m) { +- case 0: /* executable */ +- uelf->core_layout.size = +- PAGE_ALIGN(uelf->core_layout.size); +- uelf->core_layout.text_size = uelf->core_layout.size; +- break; +- case 1: /* RO: text and ro-data */ +- uelf->core_layout.size = +- PAGE_ALIGN(uelf->core_layout.size); +- uelf->core_layout.ro_size = uelf->core_layout.size; +- break; +- case 2: /* RO after init */ +- uelf->core_layout.size = +- PAGE_ALIGN(uelf->core_layout.size); +- uelf->core_layout.ro_after_init_size = +- uelf->core_layout.size; +- break; +- case 3: /* whole core */ +- uelf->core_layout.size = +- PAGE_ALIGN(uelf->core_layout.size); +- break; +- default: +- break; +- } +- } ++static void layout_upatch_info(struct upatch_elf* uelf) ++{ ++ GElf_Shdr* upatch_func = uelf->info.shdrs + uelf->index.upatch_funcs; ++ unsigned long num = upatch_func->sh_size / sizeof(struct upatch_patch_func); ++ GElf_Shdr* upatch_string = uelf->info.shdrs + uelf->index.upatch_string; ++ ++ uelf->core_layout.info_size = uelf->core_layout.size; ++ uelf->core_layout.size += sizeof(struct upatch_info) + ++ num * sizeof(struct upatch_info_func) + ++ upatch_string->sh_size; ++ uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); ++} ++ ++static void layout_jmptable(struct upatch_elf* uelf) ++{ ++ uelf->jmp_cur_entry = 0; ++ uelf->jmp_max_entry = JMP_TABLE_MAX_ENTRY; ++ uelf->jmp_offs = ALIGN(uelf->core_layout.size, sizeof(unsigned long)); ++ uelf->core_layout.size = ++ uelf->jmp_offs + uelf->jmp_max_entry * get_jmp_table_entry(); ++ uelf->core_layout.text_size = uelf->core_layout.size; ++} ++ ++static void layout_sections(struct upatch_elf* uelf) ++{ ++ enum SectionType { ++ EXECUTABLE = 0, /* executable */ ++ RO_TEXT_AND_RODATA = 1, /* RO: text and ro-data */ ++ RO_AFTER_INIT = 2, /* RO after init */ ++ WHOLE_CORE = 3 /* whole core */ ++ }; ++ ++ static unsigned long const masks[][2] = { ++ /* NOTE: all executable code must be the last section ++ * in this array; otherwise modify the text_size ++ * finder in the two loops below */ ++ {SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL}, ++ {SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL}, ++ {SHF_RO_AFTER_INIT | SHF_ALLOC, ARCH_SHF_SMALL}, ++ {SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL}, ++ {ARCH_SHF_SMALL | SHF_ALLOC, 0}}; ++ unsigned int m, i; ++ ++ for (i = 0; i < uelf->info.hdr->e_shnum; i++) { ++ uelf->info.shdrs[i].sh_entsize = ~0UL; ++ } ++ ++ log_debug("upatch section allocation order:\n"); ++ for (m = 0; m < ARRAY_SIZE(masks); ++m) { ++ for (i = 0; i < uelf->info.hdr->e_shnum; ++i) { ++ GElf_Shdr* s = &uelf->info.shdrs[i]; ++ const char* sname = uelf->info.shstrtab + s->sh_name; ++ ++ if ((s->sh_flags & masks[m][0]) != masks[m][0] || ++ (s->sh_flags & masks[m][1]) || s->sh_entsize != ~0UL) { ++ continue; ++ } ++ ++ s->sh_entsize = get_offset(&uelf->core_layout.size, s); ++ log_debug("\tm = %d; %s: sh_entsize: 0x%lx\n", m, sname, ++ s->sh_entsize); ++ } ++ switch (m) { ++ case EXECUTABLE: ++ uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); ++ uelf->core_layout.text_size = uelf->core_layout.size; ++ break; ++ case RO_TEXT_AND_RODATA: ++ uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); ++ uelf->core_layout.ro_size = uelf->core_layout.size; ++ break; ++ case RO_AFTER_INIT: ++ uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); ++ uelf->core_layout.ro_after_init_size = uelf->core_layout.size; ++ break; ++ case WHOLE_CORE: ++ uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); ++ break; ++ default: ++ break; ++ } ++ } + } + + /* TODO: only included used symbol */ + static bool is_upatch_symbol(void) + { +- return true; ++ return true; + } + /* + * We only allocate and copy the strings needed by the parts of symtab +@@ -222,800 +220,831 @@ static bool is_upatch_symbol(void) + * linux-kernel thread starting with + * <73defb5e4bca04a6431392cc341112b1@localhost>. + */ +-static void layout_symtab(struct upatch_elf *uelf) ++static void layout_symtab(struct upatch_elf* uelf) + { +- GElf_Shdr *symsect = uelf->info.shdrs + uelf->index.sym; +- GElf_Shdr *strsect = uelf->info.shdrs + uelf->index.str; +- /* TODO: only support same arch as kernel now */ +- const GElf_Sym *src; +- unsigned long i, nsrc, ndst, strtab_size = 0; +- +- /* Put symbol section at end of init part of module. */ +- symsect->sh_flags |= SHF_ALLOC; +- symsect->sh_entsize = get_offset(&uelf->core_layout.size, symsect); +- log_debug("\t%s\n", uelf->info.shstrtab + symsect->sh_name); ++ GElf_Shdr* symsect = uelf->info.shdrs + uelf->index.sym; ++ GElf_Shdr* strsect = uelf->info.shdrs + uelf->index.str; ++ /* TODO: only support same arch as kernel now */ ++ const GElf_Sym* src; ++ unsigned long i, nsrc, ndst, strtab_size = 0; ++ ++ /* Put symbol section at end of init part of module. */ ++ symsect->sh_flags |= SHF_ALLOC; ++ symsect->sh_entsize = get_offset(&uelf->core_layout.size, symsect); ++ log_debug("\t%s\n", uelf->info.shstrtab + symsect->sh_name); ++ ++ src = (void*)uelf->info.hdr + symsect->sh_offset; ++ nsrc = symsect->sh_size / sizeof(*src); ++ ++ /* Compute total space required for the symbols' strtab. */ ++ for (ndst = i = 0; i < nsrc; i++) { ++ if (i == 0 || is_upatch_symbol()) { ++ strtab_size += strlen(&uelf->strtab[src[i].st_name]) + 1; ++ ndst++; ++ } ++ } + +- src = (void *)uelf->info.hdr + symsect->sh_offset; +- nsrc = symsect->sh_size / sizeof(*src); +- +- /* Compute total space required for the symbols' strtab. */ +- for (ndst = i = 0; i < nsrc; i++) { +- if (i == 0 || is_upatch_symbol()) { +- strtab_size += +- strlen(&uelf->strtab[src[i].st_name]) + 1; +- ndst++; +- } +- } +- +- /* Append room for core symbols at end of core part. */ +- uelf->symoffs = +- ALIGN(uelf->core_layout.size, symsect->sh_addralign ?: 1); +- uelf->stroffs = uelf->core_layout.size = +- uelf->symoffs + ndst * sizeof(GElf_Sym); +- uelf->core_layout.size += strtab_size; +- uelf->core_typeoffs = uelf->core_layout.size; +- uelf->core_layout.size += ndst * sizeof(char); +- uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); +- +- /* Put string table section at end of init part of module. */ +- strsect->sh_flags |= SHF_ALLOC; +- strsect->sh_entsize = get_offset(&uelf->core_layout.size, strsect); +- uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); +- log_debug("\t%s\n", uelf->info.shstrtab + strsect->sh_name); ++ /* Append room for core symbols at end of core part. */ ++ uelf->symoffs = ALIGN(uelf->core_layout.size, symsect->sh_addralign ?: 1); ++ uelf->stroffs = uelf->core_layout.size = ++ uelf->symoffs + ndst * sizeof(GElf_Sym); ++ uelf->core_layout.size += strtab_size; ++ uelf->core_typeoffs = uelf->core_layout.size; ++ uelf->core_layout.size += ndst * sizeof(char); ++ uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); ++ ++ /* Put string table section at end of init part of module. */ ++ strsect->sh_flags |= SHF_ALLOC; ++ strsect->sh_entsize = get_offset(&uelf->core_layout.size, strsect); ++ uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); ++ log_debug("\t%s\n", uelf->info.shstrtab + strsect->sh_name); + } + +-static void *upatch_alloc(struct object_file *obj, size_t sz) ++static void* upatch_alloc(struct object_file* obj, size_t sz) + { +- int ret; +- unsigned long addr; +- struct vm_hole *hole = NULL; ++ int ret; ++ unsigned long addr; ++ struct vm_hole* hole = NULL; ++ ++ addr = object_find_patch_region(obj, sz, &hole); ++ if (!addr) { ++ return NULL; ++ } + +- addr = object_find_patch_region(obj, sz, &hole); +- if (!addr) +- return NULL; +- +- addr = upatch_mmap_remote(proc2pctx(obj->proc), addr, sz, +- PROT_READ | PROT_WRITE | PROT_EXEC, +- MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, +- (unsigned long)-1, 0); +- if (addr == 0) { +- return NULL; +- } +- +- log_debug("Allocated 0x%lx bytes at 0x%lx of '%s'\n", sz, addr, obj->name); +- +- // log_debug("Marking this space as busy\n"); +- ret = vm_hole_split(hole, addr, addr + sz); +- if (ret) { +- // TODO: clear +- log_error("Failed to split vm hole\n"); +- return NULL; +- } +- +- return (void *)addr; +-} +- +-static void upatch_free(struct object_file *obj, void *base, +- unsigned long size) +-{ +- log_debug("Free patch memory %p\n", base); +- if (upatch_munmap_remote(proc2pctx(obj->proc), (unsigned long)base, size)) { +- log_error("Failed to free patch memory %p\n", base); +- } +-} +- +-static int __alloc_memory(struct object_file *obj_file, +- struct upatch_layout *layout) +-{ +- /* Do the allocs. */ +- layout->base = upatch_alloc(obj_file, layout->size); +- if (!layout->base) { +- return -errno; +- } +- +- layout->kbase = malloc(layout->size); +- if (!layout->kbase) { +- upatch_free(obj_file, layout->base, layout->size); +- return -errno; +- } +- +- memset(layout->kbase, 0, layout->size); +- +- return 0; +-} ++ addr = upatch_mmap_remote( ++ proc2pctx(obj->proc), addr, sz, PROT_READ | PROT_WRITE | PROT_EXEC, ++ MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, (unsigned long)-1, 0); ++ if (addr == 0) { ++ return NULL; ++ } + +-static int alloc_memory(struct upatch_elf *uelf, struct object_file *obj) +-{ +- int i, ret; ++ log_debug("Allocated 0x%lx bytes at 0x%lx of '%s'\n", sz, addr, obj->name); + +- /* Do the allocs. */ +- ret = __alloc_memory(obj, &uelf->core_layout); +- if (ret) { +- return ret; +- } ++ // log_debug("Marking this space as busy\n"); ++ ret = vm_hole_split(hole, addr, addr + sz); ++ if (ret) { ++ // TODO: clear ++ log_error("Failed to split vm hole\n"); ++ return NULL; ++ } + +- /* Transfer each section which specifies SHF_ALLOC */ +- log_debug("Final section addresses:\n"); +- for (i = 0; i < uelf->info.hdr->e_shnum; i++) { +- void *kdest; +- void *dest; +- GElf_Shdr *shdr = &uelf->info.shdrs[i]; ++ return (void*)addr; ++} + +- if (!(shdr->sh_flags & SHF_ALLOC)) { +- continue; +- } ++static void upatch_free(struct object_file* obj, void* base, ++ unsigned long size) ++{ ++ log_debug("Free patch memory %p\n", base); ++ if (upatch_munmap_remote(proc2pctx(obj->proc), (unsigned long)base, size)) { ++ log_error("Failed to free patch memory %p\n", base); ++ } ++} + +- kdest = uelf->core_layout.kbase + shdr->sh_entsize; +- dest = uelf->core_layout.base + shdr->sh_entsize; ++static int __alloc_memory(struct object_file* obj_file, ++ struct upatch_layout* layout) ++{ ++ /* Do the allocs. */ ++ layout->base = upatch_alloc(obj_file, layout->size); ++ if (!layout->base) { ++ return -errno; ++ } + +- if (shdr->sh_type != SHT_NOBITS) { +- memcpy(kdest, (void *)shdr->sh_addr, shdr->sh_size); +- } ++ layout->kbase = malloc(layout->size); ++ if (!layout->kbase) { ++ upatch_free(obj_file, layout->base, layout->size); ++ return -errno; ++ } + +- shdr->sh_addr = (unsigned long)kdest; +- /* overuse this attr to record user address */ +- shdr->sh_addralign = (unsigned long)dest; +- log_debug("\t0x%lx %s <- 0x%lx\n", (long)kdest, +- uelf->info.shstrtab + shdr->sh_name, (long)dest); +- } ++ memset(layout->kbase, 0, layout->size); + +- return 0; ++ return 0; + } + +-static int post_memory(struct upatch_elf *uelf, struct object_file *obj) ++static int alloc_memory(struct upatch_elf* uelf, struct object_file* obj) + { +- int ret = 0; ++ int i, ret; + +- log_debug("Post kbase %lx(%lx) to base %lx\n", +- (unsigned long)uelf->core_layout.kbase, +- uelf->core_layout.size, +- (unsigned long)uelf->core_layout.base); +- ret = upatch_process_mem_write(obj->proc, uelf->core_layout.kbase, +- (unsigned long)uelf->core_layout.base, +- uelf->core_layout.size); +- if (ret) { +- log_error("Failed to move kbase to base, ret=%d\n", ret); +- goto out; +- } ++ /* Do the allocs. */ ++ ret = __alloc_memory(obj, &uelf->core_layout); ++ if (ret) { ++ return ret; ++ } ++ ++ /* Transfer each section which specifies SHF_ALLOC */ ++ log_debug("Final section addresses:\n"); ++ for (i = 0; i < uelf->info.hdr->e_shnum; i++) { ++ void* kdest; ++ void* dest; ++ GElf_Shdr* shdr = &uelf->info.shdrs[i]; ++ ++ if (!(shdr->sh_flags & SHF_ALLOC)) { ++ continue; ++ } ++ ++ kdest = uelf->core_layout.kbase + shdr->sh_entsize; ++ dest = uelf->core_layout.base + shdr->sh_entsize; ++ ++ if (shdr->sh_type != SHT_NOBITS) { ++ memcpy(kdest, (void*)shdr->sh_addr, shdr->sh_size); ++ } ++ ++ shdr->sh_addr = (unsigned long)kdest; ++ /* overuse this attr to record user address */ ++ shdr->sh_addralign = (unsigned long)dest; ++ log_debug("\t0x%lx %s <- 0x%lx\n", (long)kdest, ++ uelf->info.shstrtab + shdr->sh_name, (long)dest); ++ } ++ ++ return 0; ++} ++ ++static int post_memory(struct upatch_elf* uelf, struct object_file* obj) ++{ ++ int ret = 0; ++ ++ log_debug("Post kbase %lx(%lx) to base %lx\n", ++ (unsigned long)uelf->core_layout.kbase, uelf->core_layout.size, ++ (unsigned long)uelf->core_layout.base); ++ ret = upatch_process_mem_write(obj->proc, uelf->core_layout.kbase, ++ (unsigned long)uelf->core_layout.base, ++ uelf->core_layout.size); ++ if (ret) { ++ log_error("Failed to move kbase to base, ret=%d\n", ret); ++ goto out; ++ } + + out: +- return ret; +-} +- +-static int upatch_info_alloc(struct upatch_elf *uelf, struct upatch_info *uinfo) +-{ +- GElf_Shdr *upatch_funcs = &uelf->info.shdrs[uelf->index.upatch_funcs]; +- size_t num = upatch_funcs->sh_size / sizeof(struct upatch_patch_func); +- +- uinfo->funcs = (void *)malloc(num * sizeof(*uinfo->funcs)); +- if (uinfo->funcs == NULL) { +- log_error("Failed to malloc uinfo->funcs\n"); +- return -ENOMEM; +- } +- return 0; +-} +- +-static void upatch_info_init(struct upatch_elf *uelf, struct upatch_info *uinfo) +-{ +- GElf_Shdr *ufuncs = &uelf->info.shdrs[uelf->index.upatch_funcs]; +- GElf_Shdr *ustring = &uelf->info.shdrs[uelf->index.upatch_string]; +- struct upatch_patch_func *funcs = (void *)uelf->info.hdr + ufuncs->sh_offset; +- char *names = (void *)uelf->info.hdr + ustring->sh_offset; +- +- uinfo->changed_func_num = ufuncs->sh_size / sizeof(struct upatch_patch_func); +- uinfo->func_names_size = ustring->sh_size; +- uinfo->func_names = names; +- +- for (unsigned long i = 0; i < uinfo->changed_func_num; i++) { +- uinfo->funcs[i].addr = funcs[i].addr; +- uinfo->funcs[i].addr.old_addr += uelf->relf->load_bias; +- uinfo->funcs[i].name = names; +- names += strlen(names) + 1; +- } +-} +- +-static int upatch_active_stack_check(struct upatch_elf *uelf, struct upatch_process *proc) +-{ +- struct upatch_info uinfo; +- int ret = 0; +- +- ret = upatch_info_alloc(uelf, &uinfo); +- if (ret < 0) { +- return ret; +- } +- upatch_info_init(uelf, &uinfo); +- ret = upatch_stack_check(&uinfo, proc, ACTIVE); +- free(uinfo.funcs); +- return ret; +-} +- +-static struct object_file *upatch_find_obj(struct upatch_elf *uelf, +- struct upatch_process *proc) +-{ +- struct object_file *obj = NULL; +- GElf_Off min_addr; +- +- list_for_each_entry(obj, &proc->objs, list) { +- if (obj->inode == uelf->relf->info.inode) { +- min_addr = calculate_load_address(uelf->relf, true); +- uelf->relf->load_start = calculate_mem_load(obj); +- uelf->relf->load_bias = uelf->relf->load_start - min_addr; +- log_debug("load_bias = %lx\n", uelf->relf->load_bias); +- return obj; +- } +- } +- log_error("Cannot find inode %lu in pid %d, file is not loaded\n", +- uelf->relf->info.inode, proc->pid); +- return NULL; +-} +-static int complete_info(struct upatch_elf *uelf, struct object_file *obj, const char *uuid) +-{ +- int ret = 0; +- struct upatch_info *uinfo = +- (void *)uelf->core_layout.kbase + uelf->core_layout.info_size; +- struct upatch_patch_func *upatch_funcs_addr = +- (void *)uelf->info.shdrs[uelf->index.upatch_funcs].sh_addr; +- GElf_Shdr *upatch_string = &uelf->info.shdrs[uelf->index.upatch_string]; +- +- memcpy(uinfo->magic, UPATCH_HEADER, strlen(UPATCH_HEADER)); +- memcpy(uinfo->id, uuid, strlen(uuid)); +- +- uinfo->size = uelf->core_layout.size - uelf->core_layout.info_size; +- uinfo->start = (unsigned long)uelf->core_layout.base; +- uinfo->end = +- (unsigned long)uelf->core_layout.base + uelf->core_layout.size; +- uinfo->changed_func_num = +- uelf->info.shdrs[uelf->index.upatch_funcs].sh_size / +- sizeof(struct upatch_patch_func); +- +- uinfo->func_names = (void *)uinfo + sizeof(*uinfo); +- uinfo->func_names_size = upatch_string->sh_size; +- memcpy(uinfo->func_names, (void *)upatch_string->sh_addr, upatch_string->sh_size); +- +- log_debug("Changed insn:\n"); +- uinfo->funcs = (void *)uinfo->func_names + uinfo->func_names_size; +- for (unsigned int i = 0; i < uinfo->changed_func_num; ++i) { +- struct upatch_info_func *upatch_func = &uinfo->funcs[i]; +- +- upatch_func->addr = upatch_funcs_addr[i].addr; +- upatch_func->addr.old_addr += uelf->relf->load_bias; +- ret = upatch_process_mem_read(obj->proc, upatch_func->addr.old_addr, +- &upatch_func->old_insn, +- get_origin_insn_len()); +- if (ret) { +- log_error("can't read origin insn at 0x%lx - %d\n", +- upatch_func->addr.old_addr, ret); +- goto out; +- } +- +- upatch_func->new_insn = get_new_insn(); +- +- log_debug("\t0x%lx(0x%lx 0x%lx -> 0x%lx 0x%lx)\n", upatch_func->addr.old_addr, +- upatch_func->old_insn[0], upatch_func->old_insn[1], +- upatch_func->new_insn, upatch_func->addr.new_addr); +- } ++ return ret; ++} ++ ++static int upatch_info_alloc(struct upatch_elf* uelf, ++ struct upatch_info* uinfo) ++{ ++ GElf_Shdr* upatch_funcs = &uelf->info.shdrs[uelf->index.upatch_funcs]; ++ size_t num = upatch_funcs->sh_size / sizeof(struct upatch_patch_func); ++ ++ uinfo->funcs = (void*)malloc(num * sizeof(*uinfo->funcs)); ++ if (uinfo->funcs == NULL) { ++ log_error("Failed to malloc uinfo->funcs\n"); ++ return -ENOMEM; ++ } ++ return 0; ++} ++ ++static void upatch_info_init(struct upatch_elf* uelf, ++ struct upatch_info* uinfo) ++{ ++ GElf_Shdr* ufuncs = &uelf->info.shdrs[uelf->index.upatch_funcs]; ++ GElf_Shdr* ustring = &uelf->info.shdrs[uelf->index.upatch_string]; ++ struct upatch_patch_func* funcs = (void*)uelf->info.hdr + ufuncs->sh_offset; ++ char* names = (void*)uelf->info.hdr + ustring->sh_offset; ++ ++ uinfo->changed_func_num = ++ ufuncs->sh_size / sizeof(struct upatch_patch_func); ++ uinfo->func_names_size = ustring->sh_size; ++ uinfo->func_names = names; ++ ++ for (unsigned long i = 0; i < uinfo->changed_func_num; i++) { ++ uinfo->funcs[i].addr = funcs[i].addr; ++ uinfo->funcs[i].addr.old_addr += uelf->relf->load_bias; ++ uinfo->funcs[i].name = names; ++ names += strlen(names) + 1; ++ } ++} ++ ++static int upatch_active_stack_check(struct upatch_elf* uelf, ++ struct upatch_process* proc) ++{ ++ struct upatch_info uinfo; ++ int ret = 0; ++ ++ ret = upatch_info_alloc(uelf, &uinfo); ++ if (ret < 0) { ++ return ret; ++ } ++ upatch_info_init(uelf, &uinfo); ++ ret = upatch_stack_check(&uinfo, proc, ACTIVE); ++ free(uinfo.funcs); ++ return ret; ++} ++ ++static struct object_file* upatch_find_obj(struct upatch_elf* uelf, ++ struct upatch_process* proc) ++{ ++ struct object_file* obj = NULL; ++ GElf_Off min_addr; ++ ++ list_for_each_entry(obj, &proc->objs, list) { ++ if (obj->inode == uelf->relf->info.inode) { ++ min_addr = calculate_load_address(uelf->relf, true); ++ uelf->relf->load_start = calculate_mem_load(obj); ++ uelf->relf->load_bias = uelf->relf->load_start - min_addr; ++ log_debug("load_bias = %lx\n", uelf->relf->load_bias); ++ return obj; ++ } ++ } ++ log_error("Cannot find inode %lu in pid %d, file is not loaded\n", ++ uelf->relf->info.inode, proc->pid); ++ return NULL; ++} ++static int complete_info(struct upatch_elf* uelf, ++ struct object_file* obj, ++ const char* uuid) ++{ ++ int ret = 0; ++ struct upatch_info* uinfo = ++ (void*)uelf->core_layout.kbase + uelf->core_layout.info_size; ++ struct upatch_patch_func* upatch_funcs_addr = ++ (void*)uelf->info.shdrs[uelf->index.upatch_funcs].sh_addr; ++ GElf_Shdr* upatch_string = &uelf->info.shdrs[uelf->index.upatch_string]; ++ ++ memcpy(uinfo->magic, UPATCH_HEADER, strlen(UPATCH_HEADER)); ++ memcpy(uinfo->id, uuid, strlen(uuid)); ++ ++ uinfo->size = uelf->core_layout.size - uelf->core_layout.info_size; ++ uinfo->start = (unsigned long)uelf->core_layout.base; ++ uinfo->end = (unsigned long)uelf->core_layout.base + uelf->core_layout.size; ++ uinfo->changed_func_num = ++ uelf->info.shdrs[uelf->index.upatch_funcs].sh_size / ++ sizeof(struct upatch_patch_func); ++ ++ uinfo->func_names = (void*)uinfo + sizeof(*uinfo); ++ uinfo->func_names_size = upatch_string->sh_size; ++ memcpy(uinfo->func_names, (void*)upatch_string->sh_addr, ++ upatch_string->sh_size); ++ ++ log_debug("Changed insn:\n"); ++ uinfo->funcs = (void*)uinfo->func_names + uinfo->func_names_size; ++ for (unsigned int i = 0; i < uinfo->changed_func_num; ++i) { ++ struct upatch_info_func* upatch_func = &uinfo->funcs[i]; ++ ++ 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) { ++ log_error("can't read origin insn at 0x%lx - %d\n", ++ upatch_func->addr.old_addr, ret); ++ 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("\t0x%lx(0x%lx 0x%lx -> 0x%lx 0x%lx)\n", ++ upatch_func->addr.old_addr, upatch_func->old_insn[0], ++ upatch_func->old_insn[1], upatch_func->new_insn, ++ upatch_func->addr.new_addr); ++ } + + out: +- return ret; +-} +- +-static int unapply_patch(struct object_file *obj, +- struct upatch_info_func *funcs, +- unsigned long changed_func_num) +-{ +- log_debug("Changed insn:\n"); +- for (unsigned int i = 0; i < changed_func_num; ++i) { +- log_debug("\t0x%lx(0x%lx -> 0x%lx)\n", funcs[i].addr.old_addr, +- funcs[i].new_insn, funcs[i].old_insn[0]); +- +- int ret = upatch_process_mem_write(obj->proc, &funcs[i].old_insn, +- (unsigned long)funcs[i].addr.old_addr, get_origin_insn_len()); +- +- if (ret) { +- log_error("Failed to write old insn at 0x%lx, ret=%d\n", +- funcs[i].addr.old_addr, ret); +- return ret; +- } +- } +- return 0; +-} +- +-static int apply_patch(struct upatch_elf *uelf, struct object_file *obj) +-{ +- int ret = 0; +- unsigned int i; +- struct upatch_info *uinfo = +- (void *)uelf->core_layout.kbase + uelf->core_layout.info_size; +- +- for (i = 0; i < uinfo->changed_func_num; ++i) { +- struct upatch_info_func *upatch_func = &uinfo->funcs[i]; +- +- // write jumper insn to first 8 bytes +- ret = upatch_process_mem_write(obj->proc, &upatch_func->new_insn, +- (unsigned long)upatch_func->addr.old_addr, get_upatch_insn_len()); +- if (ret) { +- log_error( +- "Failed to ptrace upatch func at 0x%lx(0x%lx) - %d\n", +- upatch_func->addr.old_addr, upatch_func->new_insn, +- ret); +- goto out; +- } +- // write 64bit new addr to second 8 bytes +- ret = upatch_process_mem_write(obj->proc, &upatch_func->addr.new_addr, +- (unsigned long)upatch_func->addr.old_addr + get_upatch_insn_len(), +- get_upatch_addr_len()); +- if (ret) { +- log_error( +- "Failed to ptrace upatch func at 0x%lx(0x%lx) - %d\n", +- upatch_func->addr.old_addr + get_upatch_insn_len(), +- upatch_func->addr.new_addr, ret); +- goto out; +- } +- } ++ return ret; ++} ++ ++static int unapply_patch(struct object_file* obj, ++ struct upatch_info_func* funcs, ++ unsigned long changed_func_num) ++{ ++ log_debug("Changed insn:\n"); ++ for (unsigned int i = 0; i < changed_func_num; ++i) { ++ log_debug("\t0x%lx(0x%lx -> 0x%lx)\n", funcs[i].addr.old_addr, ++ funcs[i].new_insn, funcs[i].old_insn[0]); ++ ++ int ret = upatch_process_mem_write( ++ obj->proc, &funcs[i].old_insn, ++ (unsigned long)funcs[i].addr.old_addr, get_origin_insn_len()); ++ ++ if (ret) { ++ log_error("Failed to write old insn at 0x%lx, ret=%d\n", ++ funcs[i].addr.old_addr, ret); ++ return ret; ++ } ++ } ++ return 0; ++} ++ ++static int apply_patch(struct upatch_elf* uelf, struct object_file* obj) ++{ ++ int ret = 0; ++ unsigned int i; ++ struct upatch_info* uinfo = ++ (void*)uelf->core_layout.kbase + uelf->core_layout.info_size; ++ ++ for (i = 0; i < uinfo->changed_func_num; ++i) { ++ struct upatch_info_func* upatch_func = &uinfo->funcs[i]; ++ ++ // write jumper insn to first 8 bytes ++ ret = upatch_process_mem_write( ++ obj->proc, &upatch_func->new_insn, ++ (unsigned long)upatch_func->addr.old_addr, get_upatch_insn_len()); ++ if (ret) { ++ log_error("Failed to ptrace upatch func at 0x%lx(0x%lx) - %d\n", ++ upatch_func->addr.old_addr, upatch_func->new_insn, ret); ++ goto out; ++ } ++ // write 64bit new addr to second 8 bytes ++ ret = upatch_process_mem_write( ++ obj->proc, &upatch_func->addr.new_addr, ++ (unsigned long)upatch_func->addr.old_addr + get_upatch_insn_len(), ++ get_upatch_addr_len()); ++ if (ret) { ++ log_error("Failed to ptrace upatch func at 0x%lx(0x%lx) - %d\n", ++ upatch_func->addr.old_addr + get_upatch_insn_len(), ++ upatch_func->addr.new_addr, ret); ++ goto out; ++ } ++ } + + out: +- if (ret) { +- unapply_patch(obj, uinfo->funcs, uinfo->changed_func_num); +- } +- return ret; +-} +- +-static int upatch_mprotect(struct upatch_elf *uelf, struct object_file *obj) +-{ +- int ret; +- +- if (uelf->core_layout.text_size > 0) { +- ret = upatch_mprotect_remote( +- proc2pctx(obj->proc), +- (unsigned long)uelf->core_layout.base, +- uelf->core_layout.text_size, PROT_READ | PROT_EXEC); +- if (ret < 0) { +- log_error("Failed to change upatch text protection to r-x"); +- return ret; +- } +- } +- +- if (uelf->core_layout.ro_size > uelf->core_layout.text_size) { +- ret = upatch_mprotect_remote( +- proc2pctx(obj->proc), +- (unsigned long)uelf->core_layout.base + uelf->core_layout.text_size, +- uelf->core_layout.ro_size - uelf->core_layout.text_size, +- PROT_READ); +- if (ret < 0) { +- log_error("Failed to change upatch ro protection to r--"); +- return ret; +- } +- } +- +- if (uelf->core_layout.ro_after_init_size > uelf->core_layout.ro_size) { +- ret = upatch_mprotect_remote( +- proc2pctx(obj->proc), +- (unsigned long)uelf->core_layout.base + uelf->core_layout.ro_size, +- uelf->core_layout.ro_after_init_size - uelf->core_layout.ro_size, +- PROT_READ); +- if (ret < 0) { +- log_error("Failed to change upatch ro init protection to r--"); +- return ret; +- } +- } +- +- if (uelf->core_layout.info_size > +- uelf->core_layout.ro_after_init_size) { +- ret = upatch_mprotect_remote( +- proc2pctx(obj->proc), +- (unsigned long)uelf->core_layout.base + uelf->core_layout.ro_after_init_size, +- uelf->core_layout.info_size - uelf->core_layout.ro_after_init_size, +- PROT_READ | PROT_WRITE); +- if (ret < 0) { +- log_error("Failed to change upatch rw protection to rw-"); +- return ret; +- } +- } +- +- if (uelf->core_layout.size > uelf->core_layout.info_size) { +- ret = upatch_mprotect_remote( +- proc2pctx(obj->proc), +- (unsigned long)uelf->core_layout.base + uelf->core_layout.info_size, +- uelf->core_layout.size - uelf->core_layout.info_size, +- PROT_READ); +- if (ret < 0) { +- log_error("Failed to change upatch info protection to r--"); +- return ret; +- } +- } +- +- return 0; +-} +- +-static int upatch_apply_patches(struct object_file *obj, +- struct upatch_elf *uelf, const char *uuid) +-{ +- int ret = 0; +- +- ret = rewrite_section_headers(uelf); +- if (ret) { +- return ret; +- } +- +- // Caculate upatch mem size +- layout_jmptable(uelf); +- layout_sections(uelf); +- layout_symtab(uelf); +- layout_upatch_info(uelf); +- +- log_debug("calculate core layout = %lx\n", uelf->core_layout.size); +- log_debug( +- "Core layout: text_size = %lx, ro_size = %lx, ro_after_init_size = " +- "%lx, info = %lx, size = %lx\n", +- uelf->core_layout.text_size, uelf->core_layout.ro_size, +- uelf->core_layout.ro_after_init_size, +- uelf->core_layout.info_size, uelf->core_layout.size); +- +- /* +- * Map patch as close to the original code as possible. +- * Otherwise we can't use 32-bit jumps. +- */ +- ret = alloc_memory(uelf, obj); +- if (ret) { +- log_error("Failed to alloc patch memory\n"); +- goto free; +- } +- +- ret = upatch_mprotect(uelf, obj); +- if (ret) { +- log_error("Failed to set patch memory permission\n"); +- goto free; +- } +- +- /* Fix up syms, so that st_value is a pointer to location. */ +- ret = simplify_symbols(uelf, obj); +- if (ret) { +- goto free; +- } +- +- /* upatch new address will be updated */ +- ret = apply_relocations(uelf); +- if (ret) { +- goto free; +- } +- +- /* upatch upatch info */ +- ret = complete_info(uelf, obj, uuid); +- if (ret) { +- goto free; +- } +- +- ret = post_memory(uelf, obj); +- if (ret) { +- goto free; +- } +- +- ret = apply_patch(uelf, obj); +- if (ret) { +- goto free; +- } +- +- ret = 0; +- goto out; ++ if (ret) { ++ unapply_patch(obj, uinfo->funcs, uinfo->changed_func_num); ++ } ++ return ret; ++} ++ ++static int upatch_mprotect(struct upatch_elf* uelf, struct object_file* obj) ++{ ++ int ret; ++ ++ if (uelf->core_layout.text_size > 0) { ++ ret = upatch_mprotect_remote( ++ proc2pctx(obj->proc), (unsigned long)uelf->core_layout.base, ++ uelf->core_layout.text_size, PROT_READ | PROT_EXEC); ++ if (ret < 0) { ++ log_error("Failed to change upatch text protection to r-x"); ++ return ret; ++ } ++ } ++ ++ if (uelf->core_layout.ro_size > uelf->core_layout.text_size) { ++ ret = upatch_mprotect_remote( ++ proc2pctx(obj->proc), ++ (unsigned long)uelf->core_layout.base + uelf->core_layout.text_size, ++ uelf->core_layout.ro_size - uelf->core_layout.text_size, PROT_READ); ++ if (ret < 0) { ++ log_error("Failed to change upatch ro protection to r--"); ++ return ret; ++ } ++ } ++ ++ if (uelf->core_layout.ro_after_init_size > uelf->core_layout.ro_size) { ++ ret = upatch_mprotect_remote( ++ proc2pctx(obj->proc), ++ (unsigned long)uelf->core_layout.base + uelf->core_layout.ro_size, ++ uelf->core_layout.ro_after_init_size - uelf->core_layout.ro_size, ++ PROT_READ); ++ if (ret < 0) { ++ log_error("Failed to change upatch ro init protection to r--"); ++ return ret; ++ } ++ } ++ ++ if (uelf->core_layout.info_size > uelf->core_layout.ro_after_init_size) { ++ ret = upatch_mprotect_remote( ++ proc2pctx(obj->proc), ++ (unsigned long)uelf->core_layout.base + ++ uelf->core_layout.ro_after_init_size, ++ uelf->core_layout.info_size - uelf->core_layout.ro_after_init_size, ++ PROT_READ | PROT_WRITE); ++ if (ret < 0) { ++ log_error("Failed to change upatch rw protection to rw-"); ++ return ret; ++ } ++ } ++ ++ if (uelf->core_layout.size > uelf->core_layout.info_size) { ++ ret = upatch_mprotect_remote( ++ proc2pctx(obj->proc), ++ (unsigned long)uelf->core_layout.base + uelf->core_layout.info_size, ++ uelf->core_layout.size - uelf->core_layout.info_size, PROT_READ); ++ if (ret < 0) { ++ log_error("Failed to change upatch info protection to r--"); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int upatch_apply_patches(struct object_file* obj, ++ struct upatch_elf* uelf, ++ const char* uuid) ++{ ++ int ret = 0; ++ ++ ret = rewrite_section_headers(uelf); ++ if (ret) { ++ return ret; ++ } ++ ++ // Caculate upatch mem size ++ layout_jmptable(uelf); ++ layout_sections(uelf); ++ layout_symtab(uelf); ++ layout_upatch_info(uelf); ++ ++ log_debug("calculate core layout = %lx\n", uelf->core_layout.size); ++ log_debug( ++ "Core layout: text_size = %lx, ro_size = %lx, ro_after_init_size = " ++ "%lx, info = %lx, size = %lx\n", ++ uelf->core_layout.text_size, uelf->core_layout.ro_size, ++ uelf->core_layout.ro_after_init_size, uelf->core_layout.info_size, ++ uelf->core_layout.size); ++ ++ /* ++ * Map patch as close to the original code as possible. ++ * Otherwise we can't use 32-bit jumps. ++ */ ++ ret = alloc_memory(uelf, obj); ++ if (ret) { ++ log_error("Failed to alloc patch memory\n"); ++ goto free; ++ } ++ ++ ret = upatch_mprotect(uelf, obj); ++ if (ret) { ++ log_error("Failed to set patch memory permission\n"); ++ goto free; ++ } ++ ++ /* Fix up syms, so that st_value is a pointer to location. */ ++ ret = simplify_symbols(uelf, obj); ++ if (ret) { ++ goto free; ++ } ++ ++ /* upatch new address will be updated */ ++ ret = apply_relocations(uelf); ++ if (ret) { ++ goto free; ++ } ++ ++ /* upatch upatch info */ ++ ret = complete_info(uelf, obj, uuid); ++ if (ret) { ++ goto free; ++ } ++ ++ ret = post_memory(uelf, obj); ++ if (ret) { ++ goto free; ++ } ++ ++ ret = apply_patch(uelf, obj); ++ if (ret) { ++ goto free; ++ } ++ ++ ret = 0; ++ goto out; + + // TODO: clear + free: +- upatch_free(obj, uelf->core_layout.base, uelf->core_layout.size); ++ upatch_free(obj, uelf->core_layout.base, uelf->core_layout.size); + out: +- return ret; +-} +- +-static void upatch_time_tick(int pid) { +- static struct timeval start_tv; +- static struct timeval end_tv; +- +- if ((end_tv.tv_sec != 0) || (end_tv.tv_usec != 0)) { +- memset(&start_tv, 0, sizeof(struct timeval)); +- memset(&end_tv, 0, sizeof(struct timeval)); +- } +- +- if ((start_tv.tv_sec == 0) && (start_tv.tv_usec == 0)) { +- gettimeofday(&start_tv, NULL); +- } else { +- gettimeofday(&end_tv, NULL); +- } +- +- if ((start_tv.tv_sec == 0) || (start_tv.tv_usec == 0) || +- (end_tv.tv_sec == 0) || (end_tv.tv_usec == 0)) { +- return; +- } +- +- long frozen_time = get_microseconds(&start_tv, &end_tv); +- log_debug("Process %d frozen time is %ld microsecond(s)\n", +- pid, frozen_time); +-} +- +-static struct object_patch *upatch_find_patch(struct upatch_process *proc, const char *uuid) +-{ +- struct object_file *obj = NULL; +- struct object_patch *patch = NULL; +- +- // Traverse all mapped memory and find all upatch memory +- list_for_each_entry(obj, &proc->objs, list) { +- if (!obj->is_patch) { +- continue; +- } +- list_for_each_entry(patch, &obj->applied_patch, list) { +- if (strncmp(patch->uinfo->id, uuid, UPATCH_ID_LEN) == 0) { +- return patch; +- } +- } +- } +- return NULL; +-} +- +-static int upatch_apply_prepare(struct upatch_elf *uelf, +- struct upatch_process *proc, struct object_file **obj) +-{ +- int ret = 0; +- +- for (int i = 0; i < STACK_CHECK_RETRY_TIMES; i++) { +- ret = upatch_process_attach(proc); +- if (ret < 0) { +- log_error("Failed to attach process\n"); +- goto detach; +- } +- +- *obj = upatch_find_obj(uelf, proc); +- if (*obj == NULL) { +- ret = -ENODATA; +- goto detach; +- } +- +- ret = upatch_active_stack_check(uelf, proc); +- if (ret != -EBUSY) { +- return ret; +- } +- upatch_process_detach(proc); +- sleep(1); +- } ++ return ret; ++} ++ ++static void upatch_time_tick(int pid) ++{ ++ static struct timeval start_tv; ++ static struct timeval end_tv; ++ ++ if ((end_tv.tv_sec != 0) || (end_tv.tv_usec != 0)) { ++ memset(&start_tv, 0, sizeof(struct timeval)); ++ memset(&end_tv, 0, sizeof(struct timeval)); ++ } ++ ++ if ((start_tv.tv_sec == 0) && (start_tv.tv_usec == 0)) { ++ gettimeofday(&start_tv, NULL); ++ } else { ++ gettimeofday(&end_tv, NULL); ++ } ++ ++ if ((start_tv.tv_sec == 0) || (start_tv.tv_usec == 0) || ++ (end_tv.tv_sec == 0) || (end_tv.tv_usec == 0)) { ++ return; ++ } ++ ++ long frozen_time = get_microseconds(&start_tv, &end_tv); ++ log_debug("Process %d frozen time is %ld microsecond(s)\n", pid, ++ frozen_time); ++} ++ ++static struct object_patch* upatch_find_patch(struct upatch_process* proc, ++ const char* uuid) ++{ ++ struct object_file* obj = NULL; ++ struct object_patch* patch = NULL; ++ ++ // Traverse all mapped memory and find all upatch memory ++ list_for_each_entry(obj, &proc->objs, list) { ++ if (!obj->is_patch) { ++ continue; ++ } ++ list_for_each_entry(patch, &obj->applied_patch, list) { ++ if (strncmp(patch->uinfo->id, uuid, UPATCH_ID_LEN) == 0) { ++ return patch; ++ } ++ } ++ } ++ return NULL; ++} ++ ++static int upatch_apply_prepare(struct upatch_elf* uelf, ++ struct upatch_process* proc, ++ struct object_file** obj) ++{ ++ int ret = 0; ++ ++ for (int i = 0; i < STACK_CHECK_RETRY_TIMES; i++) { ++ ret = upatch_process_attach(proc); ++ if (ret < 0) { ++ log_error("Failed to attach process\n"); ++ goto detach; ++ } ++ ++ *obj = upatch_find_obj(uelf, proc); ++ if (*obj == NULL) { ++ ret = -ENODATA; ++ goto detach; ++ } ++ ++ ret = upatch_active_stack_check(uelf, proc); ++ if (ret != -EBUSY) { ++ return ret; ++ } ++ upatch_process_detach(proc); ++ sleep(1); ++ } + detach: +- upatch_process_detach(proc); +- return ret; +-} +- +-int process_patch(int pid, struct upatch_elf *uelf, struct running_elf *relf, const char *uuid, const char *binary_path) +-{ +- struct upatch_process proc; +- struct object_file *obj = NULL; +- +- // 查看process的信息,pid: maps, mem, cmdline, exe +- int ret = upatch_process_init(&proc, pid); +- if (ret < 0) { +- log_error("Failed to init process\n"); +- goto out; +- } +- +- printf("Patch '%s' to ", uuid); +- upatch_process_print_short(&proc); +- +- ret = upatch_process_mem_open(&proc, MEM_READ); +- if (ret < 0) { +- log_error("Failed to open process memory\n"); +- goto out_free; +- } +- +- // use uprobe to interpose function. the program has been executed to the entry +- // point +- +- /* +- * For each object file that we want to patch (either binary itself or +- * shared library) we need its ELF structure to perform relocations. +- * Because we know uniq BuildID of the object the section addresses +- * stored in the patch are valid for the original object. +- */ +- // 解析process的mem-maps,获得各个块的内存映射以及phdr +- ret = upatch_process_map_object_files(&proc); +- if (ret < 0) { +- log_error("Failed to read process memory mapping\n"); +- goto out_free; +- } +- struct object_patch *patch = upatch_find_patch(&proc, uuid); +- if (patch != NULL) { +- log_error("Patch '%s' already exists\n", uuid); +- goto out_free; +- } +- ret = binary_init(relf, binary_path); ++ upatch_process_detach(proc); ++ return ret; ++} ++ ++int process_patch(int pid, ++ struct upatch_elf* uelf, ++ struct running_elf* relf, ++ const char* uuid, ++ const char* binary_path) ++{ ++ struct upatch_process proc; ++ struct object_file* obj = NULL; ++ ++ // 查看process的信息,pid: maps, mem, cmdline, exe ++ int ret = upatch_process_init(&proc, pid); ++ if (ret < 0) { ++ log_error("Failed to init process\n"); ++ goto out; ++ } ++ ++ printf("Patch '%s' to ", uuid); ++ upatch_process_print_short(&proc); ++ ++ ret = upatch_process_mem_open(&proc, MEM_READ); ++ if (ret < 0) { ++ log_error("Failed to open process memory\n"); ++ goto out_free; ++ } ++ ++ // use uprobe to interpose function. the program has been executed to the ++ // entry point ++ ++ /* ++ * For each object file that we want to patch (either binary itself or ++ * shared library) we need its ELF structure to perform relocations. ++ * Because we know uniq BuildID of the object the section addresses ++ * stored in the patch are valid for the original object. ++ */ ++ // 解析process的mem-maps,获得各个块的内存映射以及phdr ++ ret = upatch_process_map_object_files(&proc); ++ if (ret < 0) { ++ log_error("Failed to read process memory mapping\n"); ++ goto out_free; ++ } ++ struct object_patch* patch = upatch_find_patch(&proc, uuid); ++ if (patch != NULL) { ++ log_error("Patch '%s' already exists\n", uuid); ++ goto out_free; ++ } ++ ret = binary_init(relf, binary_path); + if (ret) { + log_error("Failed to load binary\n"); + goto out_free; + } + + uelf->relf = relf; +- upatch_time_tick(pid); +- +- ret = upatch_apply_prepare(uelf, &proc, &obj); +- if (ret < 0) { +- goto out_free; +- } +- // 应用 +- ret = upatch_apply_patches(obj, uelf, uuid); +- if (ret < 0) { +- log_error("Failed to apply patch\n"); +- goto out_free; +- } ++ upatch_time_tick(pid); ++ ++ ret = upatch_apply_prepare(uelf, &proc, &obj); ++ if (ret < 0) { ++ goto out_free; ++ } ++ // 应用 ++ ret = upatch_apply_patches(obj, uelf, uuid); ++ if (ret < 0) { ++ log_error("Failed to apply patch\n"); ++ goto out_free; ++ } + + out_free: +- upatch_process_detach(&proc); +- upatch_time_tick(pid); +- upatch_process_destroy(&proc); ++ upatch_process_detach(&proc); ++ upatch_time_tick(pid); ++ upatch_process_destroy(&proc); + out: +- return ret; +-} +- +-static int upatch_unapply_patches(struct object_file *obj, struct upatch_info *uinfo) +-{ +- int ret = 0; +- +- ret = unapply_patch(obj, uinfo->funcs, uinfo->changed_func_num); +- if (ret) { +- return ret; +- } +- +- log_debug("munmap upatch layout core:\n"); +- upatch_free(obj, (void *)uinfo->start, uinfo->end - uinfo->start); +- return ret; +-} +- +-static int upatch_unapply_prepare(struct upatch_process *proc, +- const char *uuid, struct object_patch **patch) +-{ +- int ret = 0; +- +- for (int i = 0; i < STACK_CHECK_RETRY_TIMES; i++) { +- ret = upatch_process_attach(proc); +- if (ret < 0) { +- log_error("Failed to attach process\n"); +- goto detach; +- } +- *patch = upatch_find_patch(proc, uuid); +- if (*patch == NULL) { +- log_error("Patch '%s' is not found\n", uuid); +- ret = -ENODATA; +- goto detach; +- } +- ret = upatch_stack_check((*patch)->uinfo, proc, DEACTIVE); +- if (ret != -EBUSY) { +- return ret; +- } +- upatch_process_detach(proc); +- sleep(1); +- } ++ return ret; ++} ++ ++static int upatch_unapply_patches(struct object_file* obj, ++ struct upatch_info* uinfo) ++{ ++ int ret = 0; ++ ++ ret = unapply_patch(obj, uinfo->funcs, uinfo->changed_func_num); ++ if (ret) { ++ return ret; ++ } ++ ++ log_debug("munmap upatch layout core:\n"); ++ upatch_free(obj, (void*)uinfo->start, uinfo->end - uinfo->start); ++ return ret; ++} ++ ++static int upatch_unapply_prepare(struct upatch_process* proc, ++ const char* uuid, ++ struct object_patch** patch) ++{ ++ int ret = 0; ++ ++ for (int i = 0; i < STACK_CHECK_RETRY_TIMES; i++) { ++ ret = upatch_process_attach(proc); ++ if (ret < 0) { ++ log_error("Failed to attach process\n"); ++ goto detach; ++ } ++ *patch = upatch_find_patch(proc, uuid); ++ if (*patch == NULL) { ++ log_error("Patch '%s' is not found\n", uuid); ++ ret = -ENODATA; ++ goto detach; ++ } ++ ret = upatch_stack_check((*patch)->uinfo, proc, DEACTIVE); ++ if (ret != -EBUSY) { ++ return ret; ++ } ++ upatch_process_detach(proc); ++ sleep(1); ++ } + detach: +- upatch_process_detach(proc); +- return ret; +-} +- +-int process_unpatch(int pid, const char *uuid) +-{ +- struct upatch_process proc; +- struct object_patch *patch = NULL; +- +- // 查看process的信息,pid: maps, mem, cmdline, exe +- int ret = upatch_process_init(&proc, pid); +- if (ret < 0) { +- log_error("Failed to init process\n"); +- goto out; +- } +- +- printf("Unpatch '%s' from ", uuid); +- upatch_process_print_short(&proc); +- +- ret = upatch_process_mem_open(&proc, MEM_READ); +- if (ret < 0) { +- log_error("Failed to open process memory\n"); +- goto out_free; +- } +- +- // use uprobe to interpose function. the program has been executed to the entry +- // point +- +- /* +- * For each object file that we want to patch (either binary itself or +- * shared library) we need its ELF structure to perform relocations. +- * Because we know uniq BuildID of the object the section addresses +- * stored in the patch are valid for the original object. +- */ +- // 解析process的mem-maps,获得各个块的内存映射以及phdr +- ret = upatch_process_map_object_files(&proc); +- if (ret < 0) { +- log_error("Failed to read process memory mapping\n"); +- goto out_free; +- } +- +- upatch_time_tick(pid); +- ret = upatch_unapply_prepare(&proc, uuid, &patch); +- if (ret < 0) { +- goto out_free; +- } +- // 应用 +- ret = upatch_unapply_patches(patch->obj, patch->uinfo); +- if (ret < 0) { +- log_error("Failed to remove patch\n"); +- goto out_free; +- } ++ upatch_process_detach(proc); ++ return ret; ++} ++ ++int process_unpatch(int pid, const char* uuid) ++{ ++ struct upatch_process proc; ++ struct object_patch* patch = NULL; ++ ++ // 查看process的信息,pid: maps, mem, cmdline, exe ++ int ret = upatch_process_init(&proc, pid); ++ if (ret < 0) { ++ log_error("Failed to init process\n"); ++ goto out; ++ } ++ ++ printf("Unpatch '%s' from ", uuid); ++ upatch_process_print_short(&proc); ++ ++ ret = upatch_process_mem_open(&proc, MEM_READ); ++ if (ret < 0) { ++ log_error("Failed to open process memory\n"); ++ goto out_free; ++ } ++ ++ // use uprobe to interpose function. the program has been executed to the ++ // entry point ++ ++ /* ++ * For each object file that we want to patch (either binary itself or ++ * shared library) we need its ELF structure to perform relocations. ++ * Because we know uniq BuildID of the object the section addresses ++ * stored in the patch are valid for the original object. ++ */ ++ // 解析process的mem-maps,获得各个块的内存映射以及phdr ++ ret = upatch_process_map_object_files(&proc); ++ if (ret < 0) { ++ log_error("Failed to read process memory mapping\n"); ++ goto out_free; ++ } ++ ++ upatch_time_tick(pid); ++ ret = upatch_unapply_prepare(&proc, uuid, &patch); ++ if (ret < 0) { ++ goto out_free; ++ } ++ // 应用 ++ ret = upatch_unapply_patches(patch->obj, patch->uinfo); ++ if (ret < 0) { ++ log_error("Failed to remove patch\n"); ++ goto out_free; ++ } + + out_free: +- upatch_process_detach(&proc); +- upatch_time_tick(pid); +- upatch_process_destroy(&proc); ++ upatch_process_detach(&proc); ++ upatch_time_tick(pid); ++ upatch_process_destroy(&proc); + + out: +- return ret; ++ return ret; + } + +-static int upatch_info(struct upatch_process *proc) ++static int upatch_info(struct upatch_process* proc) + { +- struct object_file *obj = NULL; +- struct object_patch *patch = NULL; +- bool found = false; +- +- list_for_each_entry(obj, &proc->objs, list) { +- if (obj->is_patch) { +- found = true; +- break; +- } +- } +- +- if (!found) +- return found; ++ struct object_file* obj = NULL; ++ struct object_patch* patch = NULL; ++ bool found = false; ++ ++ list_for_each_entry(obj, &proc->objs, list) { ++ if (obj->is_patch) { ++ found = true; ++ break; ++ } ++ } + +- found = false; +- list_for_each_entry(patch, &obj->applied_patch, list) { +- // TODO: check upatch_info id +- found = true; +- break; +- } ++ if (!found) { ++ return found; ++ } ++ ++ found = false; ++ list_for_each_entry(patch, &obj->applied_patch, list) { ++ // TODO: check upatch_info id ++ found = true; ++ break; ++ } + +- return found; ++ return found; + } + + int process_info(int pid) + { +- int ret; +- struct upatch_process proc; +- char *status = "error"; +- +- // TODO: check build id +- // TODO: 栈解析 +- // 查看process的信息,pid: maps, mem, cmdline, exe +- ret = upatch_process_init(&proc, pid); +- if (ret < 0) { +- log_error("Failed to init process\n"); +- goto out; +- } +- +- ret = upatch_process_mem_open(&proc, MEM_READ); +- if (ret < 0) { +- log_error("Failed to open process memory\n"); +- goto out_free; +- } +- +- ret = upatch_process_map_object_files(&proc); +- if (ret < 0) { +- log_error("Failed to read process memory mapping\n"); +- goto out_free; +- } +- +- ret = upatch_info(&proc); +- if (ret) { +- status = "actived"; +- } +- else { +- status = "removed"; +- } +- +- ret = 0; ++ int ret; ++ struct upatch_process proc; ++ char* status = "error"; ++ ++ // TODO: check build id ++ // TODO: 栈解析 ++ // 查看process的信息,pid: maps, mem, cmdline, exe ++ ret = upatch_process_init(&proc, pid); ++ if (ret < 0) { ++ log_error("Failed to init process\n"); ++ goto out; ++ } ++ ++ ret = upatch_process_mem_open(&proc, MEM_READ); ++ if (ret < 0) { ++ log_error("Failed to open process memory\n"); ++ goto out_free; ++ } ++ ++ ret = upatch_process_map_object_files(&proc); ++ if (ret < 0) { ++ log_error("Failed to read process memory mapping\n"); ++ goto out_free; ++ } ++ ++ ret = upatch_info(&proc); ++ if (ret) { ++ status = "actived"; ++ } else { ++ status = "removed"; ++ } ++ ++ ret = 0; + + out_free: +- upatch_process_destroy(&proc); ++ upatch_process_destroy(&proc); + + out: +- log_debug("%s\n", status); +- return ret; ++ log_debug("%s\n", status); ++ return ret; + } +diff --git a/upatch-manage/upatch-process.c b/upatch-manage/upatch-process.c +index a8b1e2b10590981e297c5c10da43515812b2fa00..07d2af5c5f305038751b482c8d1e23f31c9703b5 100644 +--- a/upatch-manage/upatch-process.c ++++ b/upatch-manage/upatch-process.c +@@ -48,788 +48,851 @@ static const int MAX_ATTACH_ATTEMPTS = 3; + */ + static int lock_process(int pid) + { +- int fd; +- char path[128]; +- +- log_debug("Locking PID %d...", pid); +- snprintf(path, sizeof(path), "/proc/%d/maps", pid); +- +- fd = open(path, O_RDONLY); +- if (fd < 0) { +- log_error("Failed to open file '%s'\n", path); +- return -1; +- } +- log_debug("OK\n"); +- +- return fd; ++ ssize_t ret; ++ int fd; ++ char path[128]; ++ ++ log_debug("Locking PID %d...", pid); ++ ++ ret = snprintf(path, sizeof(path), "/proc/%d/maps", pid); ++ if (ret < 0 || ret >= (ssize_t)sizeof(path)) { ++ log_error("snprintf failed to format path\n"); ++ return -1; ++ } ++ ++ fd = open(path, O_RDONLY); ++ if (fd < 0) { ++ log_error("Failed to open file '%s'\n", path); ++ return -1; ++ } ++ log_debug("OK\n"); ++ ++ return fd; + } + + static void unlock_process(int fdmaps) + { +- int errsv = errno; +- close(fdmaps); +- errno = errsv; ++ int errsv = errno; ++ close(fdmaps); ++ errno = errsv; + } + + // TODO: get addr_space +-static int upatch_coroutines_init(struct upatch_process *proc) ++static int upatch_coroutines_init(struct upatch_process* proc) + { +- INIT_LIST_HEAD(&proc->coro.coros); ++ INIT_LIST_HEAD(&proc->coro.coros); + +- return 0; ++ return 0; + } + +-static int process_get_comm(struct upatch_process *proc) ++static int process_get_comm(struct upatch_process* proc) + { +- char path[128]; +- char realpath[PATH_MAX]; +- char *bn, *c; +- ssize_t ret; +- +- snprintf(path, sizeof(path), "/proc/%d/exe", proc->pid); +- log_debug("Reading from '%s'...", path); +- +- ret = readlink(path, realpath, sizeof(realpath)); +- if (ret < 0) { +- return -1; +- } +- +- realpath[ret] = '\0'; +- bn = basename(realpath); +- strncpy(path, bn, sizeof(path) - 1); +- if ((c = strstr(path, " (deleted)"))) { +- *c = '\0'; +- } +- +- proc->comm[sizeof(proc->comm) - 1] = '\0'; +- memcpy(proc->comm, path, sizeof(proc->comm) - 1); +- // TODO: the comm is ldxxx +- log_debug("OK\n"); +- +- return 0; ++ char path[128]; ++ char realpath[PATH_MAX]; ++ char *bn, *c; ++ ssize_t ret; ++ ++ ret = snprintf(path, sizeof(path), "/proc/%d/exe", proc->pid); ++ if (ret < 0 || ret >= (ssize_t)sizeof(path)) { ++ log_error("snprintf failed to format path\n"); ++ return -1; ++ } ++ log_debug("Reading from '%s'...", path); ++ ++ ret = readlink(path, realpath, sizeof(realpath)); ++ if (ret < 0) { ++ return -1; ++ } ++ ++ realpath[ret] = '\0'; ++ bn = basename(realpath); ++ strncpy(path, bn, sizeof(path) - 1); ++ if ((c = strstr(path, " (deleted)"))) { ++ *c = '\0'; ++ } ++ ++ proc->comm[sizeof(proc->comm) - 1] = '\0'; ++ memcpy(proc->comm, path, sizeof(proc->comm) - 1); ++ // TODO: the comm is ldxxx ++ log_debug("OK\n"); ++ ++ return 0; + } + +-int upatch_process_init(struct upatch_process *proc, int pid) ++int upatch_process_init(struct upatch_process* proc, int pid) + { +- int fdmaps; ++ int fdmaps; + +- fdmaps = lock_process(pid); +- if (fdmaps < 0) { +- goto out_err; +- } ++ fdmaps = lock_process(pid); ++ if (fdmaps < 0) { ++ goto out_err; ++ } + +- memset(proc, 0, sizeof(*proc)); ++ memset(proc, 0, sizeof(*proc)); + +- proc->pid = pid; +- proc->fdmaps = fdmaps; +- proc->memfd = -1; ++ proc->pid = pid; ++ proc->fdmaps = fdmaps; ++ proc->memfd = -1; + +- INIT_LIST_HEAD(&proc->ptrace.pctxs); +- INIT_LIST_HEAD(&proc->objs); +- INIT_LIST_HEAD(&proc->vmaholes); +- proc->num_objs = 0; ++ INIT_LIST_HEAD(&proc->ptrace.pctxs); ++ INIT_LIST_HEAD(&proc->objs); ++ INIT_LIST_HEAD(&proc->vmaholes); ++ proc->num_objs = 0; + +- if (upatch_coroutines_init(proc)) { +- goto out_unlock; +- } ++ if (upatch_coroutines_init(proc)) { ++ goto out_unlock; ++ } + +- if (process_get_comm(proc)) { +- goto out_unlock; +- } ++ if (process_get_comm(proc)) { ++ goto out_unlock; ++ } + +- return 0; ++ return 0; + + out_unlock: +- unlock_process(fdmaps); ++ unlock_process(fdmaps); + out_err: +- return -1; ++ return -1; + } + +-static void upatch_object_memfree(struct object_file *obj) ++static void upatch_object_memfree(struct object_file* obj) + { +- struct object_patch *opatch, *opatch_safe; +- struct obj_vm_area *ovma, *ovma_safe; +- +- if (obj->name) { +- free(obj->name); +- } +- +- list_for_each_entry_safe(opatch, opatch_safe, &obj->applied_patch, list) { +- if (opatch->uinfo) { +- if (opatch->uinfo->funcs) { +- free(opatch->uinfo->funcs); +- } +- free(opatch->uinfo); +- } +- free(opatch); +- } +- +- list_for_each_entry_safe(ovma, ovma_safe, &obj->vma, list) { +- free(ovma); +- } ++ struct object_patch *opatch, *opatch_safe; ++ struct obj_vm_area *ovma, *ovma_safe; ++ ++ if (obj->name) { ++ free(obj->name); ++ } ++ ++ list_for_each_entry_safe(opatch, opatch_safe, &obj->applied_patch, list) { ++ if (opatch->uinfo) { ++ if (opatch->uinfo->funcs) { ++ free(opatch->uinfo->funcs); ++ } ++ free(opatch->uinfo); ++ } ++ free(opatch); ++ } ++ ++ list_for_each_entry_safe(ovma, ovma_safe, &obj->vma, list) { ++ free(ovma); ++ } + } + +-static void upatch_process_memfree(struct upatch_process *proc) ++static void upatch_process_memfree(struct upatch_process* proc) + { +- struct upatch_ptrace_ctx *p, *p_safe; +- struct object_file *obj, *obj_safe; +- struct vm_hole *hole, *hole_safe; +- +- list_for_each_entry_safe(p, p_safe, &proc->ptrace.pctxs, list) { +- free(p); +- } +- +- list_for_each_entry_safe(hole, hole_safe, &proc->vmaholes, list) { +- free(hole); +- } +- +- list_for_each_entry_safe(obj, obj_safe, &proc->objs, list) { +- upatch_object_memfree(obj); +- free(obj); +- } ++ struct upatch_ptrace_ctx *p, *p_safe; ++ struct object_file *obj, *obj_safe; ++ struct vm_hole *hole, *hole_safe; ++ ++ list_for_each_entry_safe(p, p_safe, &proc->ptrace.pctxs, list) { ++ free(p); ++ } ++ ++ list_for_each_entry_safe(hole, hole_safe, &proc->vmaholes, list) { ++ free(hole); ++ } ++ ++ list_for_each_entry_safe(obj, obj_safe, &proc->objs, list) { ++ upatch_object_memfree(obj); ++ free(obj); ++ } + } + +-void upatch_process_destroy(struct upatch_process *proc) ++void upatch_process_destroy(struct upatch_process* proc) + { +- unlock_process(proc->fdmaps); +- upatch_process_memfree(proc); ++ unlock_process(proc->fdmaps); ++ upatch_process_memfree(proc); + } + +-static void process_print_cmdline(struct upatch_process *proc) ++static void process_print_cmdline(struct upatch_process* proc) + { +- char buf[PATH_MAX]; +- ssize_t i, rv; +- +- snprintf(buf, PATH_MAX, "/proc/%d/cmdline", proc->pid); +- int fd = open(buf, O_RDONLY); +- if (fd == -1) { +- log_error("Failed to open file '%s'\n", buf); +- return; +- } +- +- while (1) { +- rv = read(fd, buf, sizeof(buf)); +- +- if (rv == -1) { +- if (errno == EINTR) { +- continue; +- } +- log_error("Failed to read cmdline\n"); +- goto err_close; +- } +- +- if (rv == 0) { +- break; +- } +- +- for (i = 0; i < rv; i++) { +- if (isprint(buf[i])) { +- log_debug("%c", buf[i]); +- } +- else { +- log_debug(" "); +- } +- } +- } ++ char buf[PATH_MAX]; ++ ssize_t i, rv; ++ ssize_t ret; ++ ++ ret = snprintf(buf, PATH_MAX, "/proc/%d/cmdline", proc->pid); ++ if (ret < 0 || ret >= (ssize_t)sizeof(buf)) { ++ log_error("snprintf failed to format buf\n"); ++ return; ++ } ++ ++ ++ int fd = open(buf, O_RDONLY); ++ if (fd == -1) { ++ log_error("Failed to open file '%s'\n", buf); ++ return; ++ } ++ ++ while (1) { ++ rv = read(fd, buf, sizeof(buf)); ++ ++ if (rv == -1) { ++ if (errno == EINTR) { ++ continue; ++ } ++ log_error("Failed to read cmdline\n"); ++ goto err_close; ++ } ++ ++ if (rv == 0) { ++ break; ++ } ++ ++ for (i = 0; i < rv; i++) { ++ if (isprint(buf[i])) { ++ log_debug("%c", buf[i]); ++ } else { ++ log_debug(" "); ++ } ++ } ++ } + + err_close: +- close(fd); ++ close(fd); + } + +-void upatch_process_print_short(struct upatch_process *proc) ++void upatch_process_print_short(struct upatch_process* proc) + { +- log_debug("process %d, cmdline: ", proc->pid); +- process_print_cmdline(proc); +- log_debug("\n"); ++ log_debug("process %d, cmdline: ", proc->pid); ++ process_print_cmdline(proc); ++ log_debug("\n"); + } + +-int upatch_process_mem_open(struct upatch_process *proc, int mode) ++int upatch_process_mem_open(struct upatch_process* proc, int mode) + { +- char path[PATH_MAX]; +- +- if (proc->memfd >= 0) { +- close(proc->memfd); +- } +- +- snprintf(path, sizeof(path), "/proc/%d/mem", proc->pid); +- proc->memfd = open(path, mode == MEM_WRITE ? O_RDWR : O_RDONLY); +- if (proc->memfd < 0) { +- log_error("Failed to open file '%s'\n", path); +- return -1; +- } +- +- return 0; ++ char path[PATH_MAX]; ++ ssize_t ret; ++ ++ if (proc->memfd >= 0) { ++ close(proc->memfd); ++ } ++ ++ ret = snprintf(path, sizeof(path), "/proc/%d/mem", proc->pid); ++ if (ret < 0 || ret >= (ssize_t)sizeof(path)) { ++ log_error("snprintf failed to format path\n"); ++ return -1; ++ } ++ ++ proc->memfd = open(path, mode == MEM_WRITE ? O_RDWR : O_RDONLY); ++ if (proc->memfd < 0) { ++ log_error("Failed to open file '%s'\n", path); ++ return -1; ++ } ++ ++ return 0; + } + +-static unsigned int perms2prot(const char *perms) ++static unsigned int perms2prot(const char* perms) + { +- unsigned int prot = 0; +- +- if (perms[0] == 'r') +- prot |= PROT_READ; +- if (perms[1] == 'w') +- prot |= PROT_WRITE; +- if (perms[2] == 'x') +- prot |= PROT_EXEC; +- /* Ignore 'p'/'s' flag, we don't need it */ +- return prot; ++ unsigned int prot = 0; ++ ++ if (perms[0] == 'r') { ++ prot |= PROT_READ; ++ } ++ if (perms[1] == 'w') { ++ prot |= PROT_WRITE; ++ } ++ if (perms[2] == 'x') { ++ prot |= PROT_EXEC; ++ } ++ /* Ignore 'p'/'s' flag, we don't need it */ ++ return prot; + } + +-static struct vm_hole *process_add_vm_hole(struct upatch_process *proc, +- unsigned long hole_start, +- unsigned long hole_end) ++static struct vm_hole* process_add_vm_hole(struct upatch_process* proc, ++ unsigned long hole_start, ++ unsigned long hole_end) + { +- struct vm_hole *hole; ++ struct vm_hole* hole; + +- hole = malloc(sizeof(*hole)); +- if (hole == NULL) +- return NULL; ++ hole = malloc(sizeof(*hole)); ++ if (hole == NULL) { ++ return NULL; ++ } + +- memset(hole, 0, sizeof(*hole)); +- hole->start = hole_start; +- hole->end = hole_end; ++ memset(hole, 0, sizeof(*hole)); ++ hole->start = hole_start; ++ hole->end = hole_end; + +- list_add(&hole->list, &proc->vmaholes); ++ list_add(&hole->list, &proc->vmaholes); + +- return hole; ++ return hole; + } + +-static int process_get_object_type(struct upatch_process *proc, +- struct vm_area *vma, char *name, +- unsigned char *buf, size_t bufsize) ++static int process_get_object_type(struct upatch_process* proc, ++ struct vm_area* vma, ++ char* name, ++ unsigned char* buf, ++ size_t bufsize) + { +- int ret, type = OBJECT_UNKNOWN; +- +- ret = upatch_process_mem_read(proc, vma->start, buf, bufsize); +- if (ret < 0) +- return -1; +- +- if (vma->prot == PROT_READ && +- !strncmp(name, "[anonymous]", strlen("[anonymous]")) && +- !memcmp(buf, UPATCH_HEADER, UPATCH_HEADER_LEN)) { +- type = OBJECT_UPATCH; +- } else if (!memcmp(buf, ELFMAG, SELFMAG)) { +- type = OBJECT_ELF; +- } else { +- type = OBJECT_UNKNOWN; +- } +- +- return type; ++ int ret, type = OBJECT_UNKNOWN; ++ ++ ret = upatch_process_mem_read(proc, vma->start, buf, bufsize); ++ if (ret < 0) { ++ return -1; ++ } ++ ++ if (vma->prot == PROT_READ && ++ !strncmp(name, "[anonymous]", strlen("[anonymous]")) && ++ !memcmp(buf, UPATCH_HEADER, UPATCH_HEADER_LEN)) { ++ type = OBJECT_UPATCH; ++ } else if (!memcmp(buf, ELFMAG, SELFMAG)) { ++ type = OBJECT_ELF; ++ } else { ++ type = OBJECT_UNKNOWN; ++ } ++ ++ return type; + } + +-static int vm_area_same(struct vm_area *a, struct vm_area *b) ++static int vm_area_same(struct vm_area* a, struct vm_area* b) + { +- return ((a->start == b->start) && (a->end == b->end) && +- (a->prot == b->prot)); ++ return ((a->start == b->start) && (a->end == b->end) && ++ (a->prot == b->prot)); + } + +-static int object_add_vm_area(struct object_file *o, struct vm_area *vma, +- struct vm_hole *hole) ++static int object_add_vm_area(struct object_file* o, ++ struct vm_area* vma, ++ struct vm_hole* hole) + { +- struct obj_vm_area *ovma; +- +- if (o->previous_hole == NULL) +- o->previous_hole = hole; +- list_for_each_entry(ovma, &o->vma, list) { +- if (vm_area_same(vma, &ovma->inmem)) +- return 0; +- } +- ovma = malloc(sizeof(*ovma)); +- if (!ovma) +- return -1; +- memset(ovma, 0, sizeof(*ovma)); +- ovma->inmem = *vma; +- list_add(&ovma->list, &o->vma); +- return 0; ++ struct obj_vm_area* ovma; ++ ++ if (o->previous_hole == NULL) { ++ o->previous_hole = hole; ++ } ++ ++ list_for_each_entry(ovma, &o->vma, list) { ++ if (vm_area_same(vma, &ovma->inmem)) { ++ return 0; ++ } ++ } ++ ovma = malloc(sizeof(*ovma)); ++ if (!ovma) { ++ return -1; ++ } ++ memset(ovma, 0, sizeof(*ovma)); ++ ovma->inmem = *vma; ++ list_add(&ovma->list, &o->vma); ++ return 0; + } + +-static struct object_file * +-process_new_object(struct upatch_process *proc, dev_t dev, ino_t inode, +- const char *name, struct vm_area *vma, struct vm_hole *hole) ++static struct object_file* process_new_object(struct upatch_process* proc, ++ dev_t dev, ++ ino_t inode, ++ const char* name, ++ struct vm_area* vma, ++ struct vm_hole* hole) + { +- struct object_file *o; +- +- log_debug("Creating object file '%s' for %lx:%lu...", name, dev, inode); +- +- o = malloc(sizeof(*o)); +- if (!o) { +- log_error("FAILED\n"); +- return NULL; +- } +- memset(o, 0, sizeof(struct object_file)); +- +- INIT_LIST_HEAD(&o->list); +- INIT_LIST_HEAD(&o->vma); +- INIT_LIST_HEAD(&o->applied_patch); +- o->num_applied_patch = 0; +- o->proc = proc; +- o->dev = dev; +- o->inode = inode; +- o->is_patch = 0; +- +- o->previous_hole = hole; +- if (object_add_vm_area(o, vma, hole) < 0) { +- log_error("Cannot add vm area for %s\n", name); +- free(o); +- return NULL; +- } +- +- o->name = strdup(name); +- o->is_elf = 0; +- +- list_add(&o->list, &proc->objs); +- proc->num_objs++; +- +- log_debug("OK\n"); +- return o; ++ struct object_file* o; ++ ++ log_debug("Creating object file '%s' for %lx:%lu...", name, dev, inode); ++ ++ o = malloc(sizeof(*o)); ++ if (!o) { ++ log_error("FAILED\n"); ++ return NULL; ++ } ++ memset(o, 0, sizeof(struct object_file)); ++ ++ INIT_LIST_HEAD(&o->list); ++ INIT_LIST_HEAD(&o->vma); ++ INIT_LIST_HEAD(&o->applied_patch); ++ o->num_applied_patch = 0; ++ o->proc = proc; ++ o->dev = dev; ++ o->inode = inode; ++ o->is_patch = 0; ++ ++ o->previous_hole = hole; ++ if (object_add_vm_area(o, vma, hole) < 0) { ++ log_error("Cannot add vm area for %s\n", name); ++ free(o); ++ return NULL; ++ } ++ ++ o->name = strdup(name); ++ o->is_elf = 0; ++ ++ list_add(&o->list, &proc->objs); ++ proc->num_objs++; ++ ++ log_debug("OK\n"); ++ return o; + } + +-static void link_funcs_name(struct upatch_info *uinfo) ++static void link_funcs_name(struct upatch_info* uinfo) + { +- unsigned long idx = 0; ++ unsigned long idx = 0; + +- for (unsigned long i = 0; i < uinfo->changed_func_num; i++) { +- char *name = (char *)uinfo->func_names + idx; ++ for (unsigned long i = 0; i < uinfo->changed_func_num; i++) { ++ char* name = (char*)uinfo->func_names + idx; + +- uinfo->funcs[i].name = name; +- idx += strlen(name) + 1; +- } ++ uinfo->funcs[i].name = name; ++ idx += strlen(name) + 1; ++ } + } + +-static void free_object_patch(struct object_patch *opatch) ++static void free_object_patch(struct object_patch* opatch) + { +- if (opatch == NULL) { +- return; +- } +- if (opatch->uinfo != NULL) { +- if (opatch->uinfo->funcs != NULL) { +- free(opatch->uinfo->funcs); +- } +- if (opatch->uinfo->func_names != NULL) { +- free(opatch->uinfo->func_names); +- } +- free(opatch->uinfo); +- } +- free(opatch); ++ if (opatch == NULL) { ++ return; ++ } ++ if (opatch->uinfo != NULL) { ++ if (opatch->uinfo->funcs != NULL) { ++ free(opatch->uinfo->funcs); ++ } ++ if (opatch->uinfo->func_names != NULL) { ++ free(opatch->uinfo->func_names); ++ } ++ free(opatch->uinfo); ++ } ++ free(opatch); + } + +-static int add_upatch_object(struct upatch_process *proc, +- struct object_file *o, unsigned long src, unsigned char *header_buf) ++static int add_upatch_object(struct upatch_process* proc, ++ struct object_file* o, ++ unsigned long src, ++ unsigned char* header_buf) + { +- struct object_patch *opatch; +- +- opatch = malloc(sizeof(struct object_patch)); +- if (opatch == NULL) { +- log_error("malloc opatch failed\n"); +- return -1; +- } +- +- opatch->obj = o; +- opatch->uinfo = malloc(sizeof(struct upatch_info)); +- if (opatch->uinfo == NULL) { +- log_error("malloc opatch->uinfo failed\n"); +- free(opatch); +- return -1; +- } +- +- memcpy(opatch->uinfo->magic, header_buf, sizeof(struct upatch_info)); +- +- opatch->uinfo->func_names = malloc(opatch->uinfo->func_names_size); +- if (opatch->uinfo->func_names == NULL) { +- log_error("Failed to malloc funcs_names\n"); +- free_object_patch(opatch); +- return -ENOMEM; +- } +- if (upatch_process_mem_read(proc, src, +- opatch->uinfo->func_names, opatch->uinfo->func_names_size)) { +- log_error("Cannot read patch func names at 0x%lx\n", src); +- free_object_patch(opatch); +- return -1; +- } +- +- src += opatch->uinfo->func_names_size; +- opatch->uinfo->funcs = malloc(opatch->uinfo->changed_func_num * +- sizeof(struct upatch_info_func)); +- if (upatch_process_mem_read(proc, src, opatch->uinfo->funcs, +- opatch->uinfo->changed_func_num * sizeof(struct upatch_info_func))) { +- log_error("can't read patch funcs at 0x%lx\n", src); +- free_object_patch(opatch); +- return -1; +- } +- link_funcs_name(opatch->uinfo); +- list_add(&opatch->list, &o->applied_patch); +- o->num_applied_patch++; +- o->is_patch = 1; +- return 0; ++ struct object_patch* opatch; ++ ++ opatch = malloc(sizeof(struct object_patch)); ++ if (opatch == NULL) { ++ log_error("malloc opatch failed\n"); ++ return -1; ++ } ++ ++ opatch->obj = o; ++ opatch->uinfo = malloc(sizeof(struct upatch_info)); ++ if (opatch->uinfo == NULL) { ++ log_error("malloc opatch->uinfo failed\n"); ++ free(opatch); ++ return -1; ++ } ++ ++ memcpy(opatch->uinfo->magic, header_buf, sizeof(struct upatch_info)); ++ ++ opatch->uinfo->func_names = malloc(opatch->uinfo->func_names_size); ++ if (opatch->uinfo->func_names == NULL) { ++ log_error("Failed to malloc funcs_names\n"); ++ free_object_patch(opatch); ++ return -ENOMEM; ++ } ++ if (upatch_process_mem_read(proc, src, opatch->uinfo->func_names, ++ opatch->uinfo->func_names_size)) { ++ log_error("Cannot read patch func names at 0x%lx\n", src); ++ free_object_patch(opatch); ++ return -1; ++ } ++ ++ src += opatch->uinfo->func_names_size; ++ opatch->uinfo->funcs = malloc(opatch->uinfo->changed_func_num * ++ sizeof(struct upatch_info_func)); ++ if (upatch_process_mem_read(proc, src, opatch->uinfo->funcs, ++ opatch->uinfo->changed_func_num * ++ sizeof(struct upatch_info_func))) { ++ log_error("can't read patch funcs at 0x%lx\n", src); ++ free_object_patch(opatch); ++ return -1; ++ } ++ link_funcs_name(opatch->uinfo); ++ list_add(&opatch->list, &o->applied_patch); ++ o->num_applied_patch++; ++ o->is_patch = 1; ++ return 0; + } + /** + * Returns: 0 if everything is ok, -1 on error. + */ +-static int process_add_object_vma(struct upatch_process *proc, dev_t dev, +- ino_t inode, char *name, struct vm_area *vma, +- struct vm_hole *hole) ++static int process_add_object_vma(struct upatch_process* proc, ++ dev_t dev, ++ ino_t inode, ++ char* name, ++ struct vm_area* vma, ++ struct vm_hole* hole) + { +- int object_type; +- unsigned char header_buf[1024]; +- struct object_file *o; +- +- /* Event though process_get_object_type() return -1, +- * we still need continue process. */ +- object_type = process_get_object_type(proc, vma, name, header_buf, +- sizeof(header_buf)); +- +- if (object_type != OBJECT_UPATCH) { +- /* Is not a upatch, look if this is a vm_area of an already +- * enlisted object. +- */ +- list_for_each_entry_reverse(o, &proc->objs, list) { +- if ((dev && inode && o->dev == dev && +- o->inode == (ino_t)inode) || +- (dev == 0 && !strcmp(o->name, name))) { +- return object_add_vm_area(o, vma, hole); +- } +- } +- } +- +- o = process_new_object(proc, dev, inode, name, vma, hole); +- if (o == NULL) { +- return -1; +- } +- +- if (object_type == OBJECT_UPATCH) { +- unsigned long src = vma->start + sizeof(struct upatch_info); +- if (add_upatch_object(proc, o, src, header_buf) != 0) { +- return -1; +- } +- } +- if (object_type == OBJECT_ELF) { +- o->is_elf = 1; +- } +- +- return 0; ++ int object_type; ++ unsigned char header_buf[1024]; ++ struct object_file* o; ++ ++ /* Event though process_get_object_type() return -1, ++ * we still need continue process. */ ++ object_type = process_get_object_type(proc, vma, name, header_buf, ++ sizeof(header_buf)); ++ ++ if (object_type != OBJECT_UPATCH) { ++ /* Is not a upatch, look if this is a vm_area of an already ++ * enlisted object. ++ */ ++ list_for_each_entry_reverse(o, &proc->objs, list) { ++ if ((dev && inode && o->dev == dev && o->inode == (ino_t)inode) || ++ (dev == 0 && !strcmp(o->name, name))) { ++ return object_add_vm_area(o, vma, hole); ++ } ++ } ++ } ++ ++ o = process_new_object(proc, dev, inode, name, vma, hole); ++ if (o == NULL) { ++ return -1; ++ } ++ ++ if (object_type == OBJECT_UPATCH) { ++ unsigned long src = vma->start + sizeof(struct upatch_info); ++ if (add_upatch_object(proc, o, src, header_buf) != 0) { ++ return -1; ++ } ++ } ++ if (object_type == OBJECT_ELF) { ++ o->is_elf = 1; ++ } ++ ++ return 0; + } + +-int upatch_process_parse_proc_maps(struct upatch_process *proc) ++int upatch_process_parse_proc_maps(struct upatch_process* proc) + { +- FILE *f; +- int ret, is_libc_base_set = 0; +- unsigned long hole_start = 0; +- struct vm_hole *hole = NULL; +- +- /* +- * 1. Create the list of all objects in the process +- * 2. Check whether we have patch for any of them +- * 3. If we have at least one patch, create files for all +- * of the object (we might have references to them +- * in the patch). +- */ +- int fd = dup(proc->fdmaps); +- if (fd < 0) { +- log_error("unable to dup fd %d", proc->fdmaps); +- return -1; +- } +- +- lseek(fd, 0, SEEK_SET); +- f = fdopen(fd, "r"); +- if (f == NULL) { +- log_error("unable to fdopen %d", fd); +- close(fd); +- return -1; +- } +- +- do { +- struct vm_area vma; +- char line[1024]; +- unsigned long start, end, offset; +- unsigned int maj, min, inode; +- char perms[5], name_[256], *name = name_; +- int r; +- +- if (!fgets(line, sizeof(line), f)) { +- break; +- } +- +- r = sscanf(line, "%lx-%lx %s %lx %x:%x %d %255s", &start, &end, +- perms, &offset, &maj, &min, &inode, name_); +- if (r == EOF) { +- log_error("Failed to read maps: unexpected EOF"); +- goto error; +- } +- +- if (r != 8) { +- strcpy(name, "[anonymous]"); +- } +- +- vma.start = start; +- vma.end = end; +- vma.offset = offset; +- vma.prot = perms2prot(perms); +- +- /* Hole must be at least 2 pages for guardians */ +- if (start - hole_start > (unsigned long)(2 * PAGE_SIZE)) { +- hole = process_add_vm_hole(proc, hole_start + (unsigned long)PAGE_SIZE, +- start - (unsigned long)PAGE_SIZE); +- if (hole == NULL) { +- log_error("Failed to add vma hole"); +- goto error; +- } +- } +- hole_start = end; +- +- name = name[0] == '/' ? basename(name) : name; +- +- ret = process_add_object_vma(proc, makedev(maj, min), inode, +- name, &vma, hole); +- if (ret < 0) { +- log_error("Failed to add object vma"); +- goto error; +- } +- +- if (!is_libc_base_set && !strncmp(basename(name), "libc", 4) && +- vma.prot & PROT_EXEC) { +- proc->libc_base = start; +- is_libc_base_set = 1; +- } +- +- } while (1); +- fclose(f); +- close(fd); +- +- log_debug("Found %d object file(s)\n", proc->num_objs); +- +- if (!is_libc_base_set) { +- log_error("Can't find libc_base required for manipulations: %d", +- proc->pid); +- return -1; +- } +- +- return 0; ++ FILE* f; ++ int ret, is_libc_base_set = 0; ++ unsigned long hole_start = 0; ++ struct vm_hole* hole = NULL; ++ ++ /* ++ * 1. Create the list of all objects in the process ++ * 2. Check whether we have patch for any of them ++ * 3. If we have at least one patch, create files for all ++ * of the object (we might have references to them ++ * in the patch). ++ */ ++ int fd = dup(proc->fdmaps); ++ if (fd < 0) { ++ log_error("unable to dup fd %d", proc->fdmaps); ++ return -1; ++ } ++ ++ lseek(fd, 0, SEEK_SET); ++ f = fdopen(fd, "r"); ++ if (f == NULL) { ++ log_error("unable to fdopen %d", fd); ++ close(fd); ++ return -1; ++ } ++ ++ do { ++ struct vm_area vma; ++ char line[1024]; ++ unsigned long start, end, offset; ++ unsigned int maj, min, inode; ++ char perms[5], name_[256], *name = name_; ++ int r; ++ ++ if (!fgets(line, sizeof(line), f)) { ++ break; ++ } ++ ++ r = sscanf(line, "%lx-%lx %s %lx %x:%x %d %255s", &start, &end, perms, ++ &offset, &maj, &min, &inode, name_); ++ if (r == EOF) { ++ log_error("Failed to read maps: unexpected EOF"); ++ goto error; ++ } ++ ++ if (r != 8) { ++ strcpy(name, "[anonymous]"); ++ } ++ ++ vma.start = start; ++ vma.end = end; ++ vma.offset = offset; ++ vma.prot = perms2prot(perms); ++ ++ /* Hole must be at least 2 pages for guardians */ ++ if (start - hole_start > (unsigned long)(2 * PAGE_SIZE)) { ++ hole = ++ process_add_vm_hole(proc, hole_start + (unsigned long)PAGE_SIZE, ++ start - (unsigned long)PAGE_SIZE); ++ if (hole == NULL) { ++ log_error("Failed to add vma hole"); ++ goto error; ++ } ++ } ++ hole_start = end; ++ ++ name = name[0] == '/' ? basename(name) : name; ++ ++ ret = process_add_object_vma(proc, makedev(maj, min), inode, name, &vma, ++ hole); ++ if (ret < 0) { ++ log_error("Failed to add object vma"); ++ goto error; ++ } ++ ++ if (!is_libc_base_set && !strncmp(basename(name), "libc", 4) && ++ (vma.prot & PROT_EXEC)) { ++ proc->libc_base = start; ++ is_libc_base_set = 1; ++ } ++ ++ } while (1); ++ ++ if (fclose(f) != 0) { ++ log_error("Failed to close file\n"); ++ close(fd); ++ return -1; ++ } ++ ++ close(fd); ++ ++ log_debug("Found %d object file(s)\n", proc->num_objs); ++ ++ if (!is_libc_base_set) { ++ log_error("Can't find libc_base required for manipulations: %d", ++ proc->pid); ++ return -1; ++ } ++ ++ return 0; + + error: +- fclose(f); +- close(fd); +- return -1; ++ fclose(f); ++ close(fd); ++ return -1; + } + +-int upatch_process_map_object_files(struct upatch_process *proc) ++int upatch_process_map_object_files(struct upatch_process* proc) + { +- // we can get plt/got table from mem's elf_segments +- // Now we read them from the running file +- return upatch_process_parse_proc_maps(proc); ++ // we can get plt/got table from mem's elf_segments ++ // Now we read them from the running file ++ return upatch_process_parse_proc_maps(proc); + } + +-static int process_list_threads(struct upatch_process *proc, int **ppids, +- size_t *npids, size_t *alloc) ++static int process_list_threads(struct upatch_process* proc, ++ int** ppids, ++ size_t* npids, ++ size_t* alloc) + { +- DIR *dir = NULL; +- struct dirent *de; +- char path[PATH_MAX]; +- int *pids = *ppids; +- +- snprintf(path, sizeof(path), "/proc/%d/task", proc->pid); +- +- dir = opendir(path); +- if (!dir) { +- log_error("Failed to open directory '%s'\n", path); +- goto dealloc; +- } +- +- *npids = 0; +- while ((de = readdir(dir))) { +- int *t; +- if (de->d_name[0] == '.') { +- continue; +- } +- +- if (*npids >= *alloc) { +- *alloc = *alloc ? *alloc * 2 : 1; +- +- t = realloc(pids, *alloc * sizeof(*pids)); +- if (t == NULL) { +- log_error("Failed to (re)allocate memory for pids\n"); +- goto dealloc; +- } +- +- pids = t; +- } +- +- pids[*npids] = atoi(de->d_name); +- (*npids)++; +- } +- closedir(dir); +- +- *ppids = pids; +- +- return (int)*npids; ++ DIR* dir = NULL; ++ struct dirent* de; ++ char path[PATH_MAX]; ++ int* pids = *ppids; ++ ssize_t ret; ++ ++ ret = snprintf(path, sizeof(path), "/proc/%d/task", proc->pid); ++ if (ret < 0 || ret >= (ssize_t)sizeof(path)) { ++ log_error("snprintf failed to format path\n"); ++ return -1; ++ } ++ ++ dir = opendir(path); ++ if (!dir) { ++ log_error("Failed to open directory '%s'\n", path); ++ goto dealloc; ++ } ++ ++ *npids = 0; ++ while ((de = readdir(dir))) { ++ int* t; ++ if (de->d_name[0] == '.') { ++ continue; ++ } ++ ++ if (*npids >= *alloc) { ++ *alloc = *alloc ? *alloc * 2 : 1; ++ ++ t = realloc(pids, *alloc * sizeof(*pids)); ++ if (t == NULL) { ++ log_error("Failed to (re)allocate memory for pids\n"); ++ goto dealloc; ++ } ++ ++ pids = t; ++ } ++ ++ pids[*npids] = atoi(de->d_name); ++ (*npids)++; ++ } ++ closedir(dir); ++ ++ *ppids = pids; ++ ++ return (int)*npids; + + dealloc: +- if (dir) { +- closedir(dir); +- } +- free(pids); +- *ppids = NULL; +- *alloc = *npids = 0; +- return -1; ++ if (dir) { ++ closedir(dir); ++ } ++ free(pids); ++ *ppids = NULL; ++ *alloc = *npids = 0; ++ return -1; + } + +-int upatch_process_attach(struct upatch_process *proc) ++int upatch_process_attach(struct upatch_process* proc) + { +- int *pids = NULL, ret; +- size_t i, npids = 0, alloc = 0, prevnpids = 0, nattempts; +- +- if (upatch_process_mem_open(proc, MEM_WRITE) < 0) { +- return -1; +- } +- +- for (nattempts = 0; nattempts < MAX_ATTACH_ATTEMPTS; nattempts++) { +- ret = process_list_threads(proc, &pids, &npids, &alloc); +- if (ret == -1) +- goto detach; +- +- if (nattempts == 0) { +- log_debug("Found %lu thread(s), attaching...\n", npids); +- } else { +- /* +- * FIXME(pboldin): This is wrong, amount of threads can +- * be the same because some new spawned and some old +- * died +- */ +- if (prevnpids == npids) +- break; +- +- log_debug("Found %lu new thread(s), attaching...\n", +- prevnpids - npids); +- } +- +- for (i = prevnpids; i < npids; i++) { +- int pid = pids[i]; +- +- // if (process_has_thread_pid(proc, pid)) { +- // log_debug("already have pid %d\n", pid); +- // continue; +- // } +- +- ret = upatch_ptrace_attach_thread(proc, pid); +- if (ret < 0) +- goto detach; +- } +- +- prevnpids = npids; +- } +- +- if (nattempts == MAX_ATTACH_ATTEMPTS) { +- log_error("Unable to catch up with process, bailing\n"); +- goto detach; +- } +- +- log_debug("Attached to %lu thread(s): %d", npids, pids[0]); +- for (i = 1; i < npids; i++) { +- log_debug(", %d", pids[i]); +- } +- log_debug("\n"); +- +- free(pids); +- return 0; ++ int *pids = NULL, ret; ++ size_t i, npids = 0, alloc = 0, prevnpids = 0, nattempts; ++ ++ if (upatch_process_mem_open(proc, MEM_WRITE) < 0) { ++ return -1; ++ } ++ ++ for (nattempts = 0; nattempts < MAX_ATTACH_ATTEMPTS; nattempts++) { ++ ret = process_list_threads(proc, &pids, &npids, &alloc); ++ if (ret == -1) { ++ goto detach; ++ } ++ ++ if (nattempts == 0) { ++ log_debug("Found %lu thread(s), attaching...\n", npids); ++ } else { ++ /* ++ * FIXME(pboldin): This is wrong, amount of threads can ++ * be the same because some new spawned and some old ++ * died ++ */ ++ if (prevnpids == npids) { ++ break; ++ } ++ ++ log_debug("Found %lu new thread(s), attaching...\n", ++ prevnpids - npids); ++ } ++ ++ for (i = prevnpids; i < npids; i++) { ++ int pid = pids[i]; ++ ++ // if (process_has_thread_pid(proc, pid)) { ++ // log_debug("already have pid %d\n", pid); ++ // continue; ++ // } ++ ++ ret = upatch_ptrace_attach_thread(proc, pid); ++ if (ret < 0) { ++ goto detach; ++ } ++ } ++ ++ prevnpids = npids; ++ } ++ ++ if (nattempts == MAX_ATTACH_ATTEMPTS) { ++ log_error("Unable to catch up with process, bailing\n"); ++ goto detach; ++ } ++ ++ log_debug("Attached to %lu thread(s): %d", npids, pids[0]); ++ for (i = 1; i < npids; i++) { ++ log_debug(", %d", pids[i]); ++ } ++ log_debug("\n"); ++ ++ free(pids); ++ return 0; + + detach: +- upatch_process_detach(proc); +- free(pids); +- return -1; ++ upatch_process_detach(proc); ++ free(pids); ++ return -1; + } + +-void upatch_process_detach(struct upatch_process *proc) ++void upatch_process_detach(struct upatch_process* proc) + { +- struct upatch_ptrace_ctx *p, *ptmp; +- int status; +- pid_t pid; +- +- if (proc->memfd >= 0 && close(proc->memfd) < 0) { +- log_error("Failed to close memfd"); +- } +- proc->memfd = -1; +- +- list_for_each_entry_safe(p, ptmp, &proc->ptrace.pctxs, list) { +- /** +- * If upatch_ptrace_detach(p) return -ESRCH, there are two situations, +- * as described below: +- * 1. the specified thread does not exist, it means the thread dead +- * during the attach processing, so we need to wait for the thread +- * to exit; +- * 2. the specified thread is not currently being traced by us, +- * or is not stopped, so we just ignore it; +- * +- * We using the running variable of the struct upatch_ptrace_ctx to +- * distinguish them: +- * 1. if pctx->running = 0, it means the thread is traced by us, we +- * will wait for the thread to exit; +- * 2. if pctx->running = 1, it means we can not sure about the status of +- * the thread, we just ignore it; +- */ +- if (upatch_ptrace_detach(p) == -ESRCH && !p->running) { +- do { +- pid = waitpid(p->pid, &status, __WALL); +- } while (pid > 0 && !WIFEXITED(status)); +- } +- list_del(&p->list); +- free(p); +- } +- log_debug("Process detached\n"); ++ struct upatch_ptrace_ctx *p, *ptmp; ++ int status; ++ pid_t pid; ++ ++ if (proc->memfd >= 0 && close(proc->memfd) < 0) { ++ log_error("Failed to close memfd"); ++ } ++ proc->memfd = -1; ++ ++ list_for_each_entry_safe(p, ptmp, &proc->ptrace.pctxs, list) { ++ /** ++ * If upatch_ptrace_detach(p) return -ESRCH, there are two situations, ++ * as described below: ++ * 1. the specified thread does not exist, it means the thread dead ++ * during the attach processing, so we need to wait for the thread ++ * to exit; ++ * 2. the specified thread is not currently being traced by us, ++ * or is not stopped, so we just ignore it; ++ * ++ * We using the running variable of the struct upatch_ptrace_ctx to ++ * distinguish them: ++ * 1. if pctx->running = 0, it means the thread is traced by us, we ++ * will wait for the thread to exit; ++ * 2. if pctx->running = 1, it means we can not sure about the status of ++ * the thread, we just ignore it; ++ */ ++ if (upatch_ptrace_detach(p) == -ESRCH && !p->running) { ++ do { ++ pid = waitpid(p->pid, &status, __WALL); ++ } while (pid > 0 && !WIFEXITED(status)); ++ } ++ list_del(&p->list); ++ free(p); ++ } ++ log_debug("Process detached\n"); + } + +-static inline struct vm_hole *next_hole(struct vm_hole *hole, +- struct list_head *head) ++static inline struct vm_hole* next_hole(struct vm_hole* hole, ++ struct list_head* head) + { +- if (hole == NULL || hole->list.next == head) +- return NULL; ++ if (hole == NULL || hole->list.next == head) { ++ return NULL; ++ } + +- return list_entry(hole->list.next, struct vm_hole, list); ++ return list_entry(hole->list.next, struct vm_hole, list); + } + +-static inline struct vm_hole *prev_hole(struct vm_hole *hole, +- struct list_head *head) ++static inline struct vm_hole* prev_hole(struct vm_hole* hole, ++ struct list_head* head) + { +- if (hole == NULL || hole->list.prev == head) +- return NULL; ++ if (hole == NULL || hole->list.prev == head) { ++ return NULL; ++ } + +- return list_entry(hole->list.prev, struct vm_hole, list); ++ return list_entry(hole->list.prev, struct vm_hole, list); + } + +-static inline unsigned long hole_size(struct vm_hole *hole) ++static inline unsigned long hole_size(struct vm_hole* hole) + { +- if (hole == NULL) +- return 0; +- return hole->end - hole->start; ++ if (hole == NULL) { ++ return 0; ++ } ++ return hole->end - hole->start; + } + +-int vm_hole_split(struct vm_hole *hole, unsigned long alloc_start, +- unsigned long alloc_end) ++int vm_hole_split(struct vm_hole* hole, ++ unsigned long alloc_start, ++ unsigned long alloc_end) + { +- unsigned long page_size = (unsigned long)PAGE_SIZE; ++ unsigned long page_size = (unsigned long)PAGE_SIZE; + +- alloc_start = ROUND_DOWN(alloc_start, page_size) - page_size; +- alloc_end = ROUND_UP(alloc_end, page_size) + page_size; ++ alloc_start = ROUND_DOWN(alloc_start, page_size) - page_size; ++ alloc_end = ROUND_UP(alloc_end, page_size) + page_size; + +- if (alloc_start > hole->start) { +- struct vm_hole *left = NULL; ++ if (alloc_start > hole->start) { ++ struct vm_hole* left = NULL; + +- left = malloc(sizeof(*hole)); +- if (left == NULL) { +- log_error("Failed to malloc for vm hole"); +- return -1; +- } ++ left = malloc(sizeof(*hole)); ++ if (left == NULL) { ++ log_error("Failed to malloc for vm hole"); ++ return -1; ++ } + +- left->start = hole->start; +- left->end = alloc_start; ++ left->start = hole->start; ++ left->end = alloc_start; + +- list_add(&left->list, &hole->list); +- } ++ list_add(&left->list, &hole->list); ++ } + +- /* Reuse hole pointer as the right hole since it is pointed to by +- * the `previous_hole` of some `object_file`. */ +- hole->start = alloc_end; +- hole->end = hole->end > alloc_end ? hole->end : alloc_end; ++ /* Reuse hole pointer as the right hole since it is pointed to by ++ * the `previous_hole` of some `object_file`. */ ++ hole->start = alloc_end; ++ hole->end = hole->end > alloc_end ? hole->end : alloc_end; + +- return 0; ++ return 0; + } + + /* +@@ -841,49 +904,50 @@ int vm_hole_split(struct vm_hole *hole, unsigned long alloc_start, + * from the obj. + * eg: R_AARCH64_ADR_GOT_PAGE + */ +-unsigned long object_find_patch_region(struct object_file *obj, size_t memsize, +- struct vm_hole **hole) ++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; +- struct vm_hole *right_hole = next_hole(left_hole, head); +- unsigned long region_start = 0; +- struct obj_vm_area *sovma; +- unsigned long obj_start, obj_end; +- +- 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; +- +- log_debug("Looking for patch region for '%s'...\n", obj->name); +- +- while (right_hole != NULL || left_hole != NULL) { +- if (hole_size(right_hole) > memsize) { +- *hole = right_hole; +- region_start = right_hole->start; +- if (region_start + memsize - obj_start > MAX_DISTANCE) { +- continue; +- } +- goto found; +- } +- if (hole_size(left_hole) > memsize) { +- *hole = left_hole; +- region_start = left_hole->end - memsize; +- if (obj_end - region_start > MAX_DISTANCE) { +- continue; +- } +- goto found; +- } +- right_hole = next_hole(right_hole, head); +- left_hole = prev_hole(left_hole, head); +- } +- log_error("Cannot find suitable region for patch '%s'\n", obj->name); +- return -1UL; ++ struct list_head* head = &obj->proc->vmaholes; ++ struct vm_hole* left_hole = obj->previous_hole; ++ struct vm_hole* right_hole = next_hole(left_hole, head); ++ unsigned long region_start = 0; ++ struct obj_vm_area* sovma; ++ unsigned long obj_start, obj_end; ++ ++ 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; ++ ++ log_debug("Looking for patch region for '%s'...\n", obj->name); ++ ++ while (right_hole != NULL || left_hole != NULL) { ++ if (hole_size(right_hole) > memsize) { ++ *hole = right_hole; ++ region_start = right_hole->start; ++ if (region_start + memsize - obj_start > MAX_DISTANCE) { ++ continue; ++ } ++ goto found; ++ } ++ if (hole_size(left_hole) > memsize) { ++ *hole = left_hole; ++ region_start = left_hole->end - memsize; ++ if (obj_end - region_start > MAX_DISTANCE) { ++ continue; ++ } ++ goto found; ++ } ++ right_hole = next_hole(right_hole, head); ++ left_hole = prev_hole(left_hole, head); ++ } ++ log_error("Cannot find suitable region for patch '%s'\n", obj->name); ++ return -1UL; + found: +- region_start = (region_start >> PAGE_SHIFT) << PAGE_SHIFT; +- log_debug("Found patch region for '%s' 0xat %lx\n", obj->name, +- region_start); ++ region_start = (region_start >> PAGE_SHIFT) << PAGE_SHIFT; ++ log_debug("Found patch region for '%s' 0xat %lx\n", obj->name, ++ region_start); + +- return region_start; ++ return region_start; + } +diff --git a/upatch-manage/upatch-process.h b/upatch-manage/upatch-process.h +index a98b542cfa59847e6f423f82ca4aa9aa7d66af8e..0d32aacc8605a2259384b09c9bff45bc633aab25 100644 +--- a/upatch-manage/upatch-process.h ++++ b/upatch-manage/upatch-process.h +@@ -38,114 +38,115 @@ + #endif + + enum { +- MEM_READ, +- MEM_WRITE, ++ MEM_READ, ++ MEM_WRITE, + }; + + struct object_file { +- struct list_head list; +- struct upatch_process *proc; ++ struct list_head list; ++ struct upatch_process* proc; + +- /* Device the object resides on */ +- dev_t dev; +- ino_t inode; ++ /* Device the object resides on */ ++ dev_t dev; ++ ino_t inode; + +- /* Object name (as seen in /proc//maps) */ +- char *name; ++ /* Object name (as seen in /proc//maps) */ ++ char* name; + +- /* List of object's VM areas */ +- struct list_head vma; ++ /* List of object's VM areas */ ++ struct list_head vma; + +- /* Pointer to the previous hole in the patient's mapping */ +- struct vm_hole *previous_hole; ++ /* Pointer to the previous hole in the patient's mapping */ ++ struct vm_hole* previous_hole; + +- /* Pointer to the applied patch list, if any */ +- struct list_head applied_patch; +- /* The number of applied patch */ +- size_t num_applied_patch; ++ /* Pointer to the applied patch list, if any */ ++ struct list_head applied_patch; ++ /* The number of applied patch */ ++ size_t num_applied_patch; + +- /* Is that a patch for some object? */ +- unsigned int is_patch; ++ /* Is that a patch for some object? */ ++ unsigned int is_patch; + +- /* Is it an ELF or a mmap'ed regular file? */ +- unsigned int is_elf; ++ /* Is it an ELF or a mmap'ed regular file? */ ++ unsigned int is_elf; + }; + + struct vm_area { +- unsigned long start; +- unsigned long end; +- unsigned long offset; +- unsigned int prot; ++ unsigned long start; ++ unsigned long end; ++ unsigned long offset; ++ unsigned int prot; + }; + + struct vm_hole { +- unsigned long start; +- unsigned long end; +- struct list_head list; ++ unsigned long start; ++ unsigned long end; ++ struct list_head list; + }; + + struct obj_vm_area { +- struct list_head list; +- struct vm_area inmem; ++ struct list_head list; ++ struct vm_area inmem; + }; + + struct object_patch { +- struct list_head list; +- struct upatch_info *uinfo; +- struct object_file *obj; ++ struct list_head list; ++ struct upatch_info* uinfo; ++ struct object_file* obj; + }; + + struct upatch_process { +- /* Pid of target process */ +- int pid; ++ /* Pid of target process */ ++ int pid; + +- /* memory fd of /proc//mem */ +- int memfd; ++ /* memory fd of /proc//mem */ ++ int memfd; + +- /* /proc//maps FD, also works as lock */ +- int fdmaps; ++ /* /proc//maps FD, also works as lock */ ++ int fdmaps; + +- /* Process name */ +- char comm[16]; ++ /* Process name */ ++ char comm[16]; + +- /* List of process objects */ +- struct list_head objs; +- int num_objs; ++ /* List of process objects */ ++ struct list_head objs; ++ int num_objs; + +- /* List ptrace contexts (one per each thread) */ +- struct { +- struct list_head pctxs; +- } ptrace; ++ /* List ptrace contexts (one per each thread) */ ++ struct { ++ struct list_head pctxs; ++ } ptrace; + +- struct { +- struct list_head coros; +- } coro; ++ struct { ++ struct list_head coros; ++ } coro; + +- /* List of free VMA areas */ +- struct list_head vmaholes; ++ /* List of free VMA areas */ ++ struct list_head vmaholes; + +- // TODO: other base? +- /* libc's base address to use as a worksheet */ +- unsigned long libc_base; ++ // TODO: other base? ++ /* libc's base address to use as a worksheet */ ++ unsigned long libc_base; + }; + +-int upatch_process_init(struct upatch_process *, int); ++int upatch_process_init(struct upatch_process*, int); + +-void upatch_process_destroy(struct upatch_process *); ++void upatch_process_destroy(struct upatch_process*); + +-void upatch_process_print_short(struct upatch_process *); ++void upatch_process_print_short(struct upatch_process*); + +-int upatch_process_mem_open(struct upatch_process *, int); ++int upatch_process_mem_open(struct upatch_process*, int); + +-int upatch_process_map_object_files(struct upatch_process *); ++int upatch_process_map_object_files(struct upatch_process*); + +-int upatch_process_attach(struct upatch_process *); ++int upatch_process_attach(struct upatch_process*); + +-void upatch_process_detach(struct upatch_process *proc); ++void upatch_process_detach(struct upatch_process* proc); + +-int vm_hole_split(struct vm_hole *, unsigned long, unsigned long); ++int vm_hole_split(struct vm_hole*, unsigned long, unsigned long); + +-unsigned long object_find_patch_region(struct object_file *, size_t, +- struct vm_hole **); ++unsigned long object_find_patch_region(struct object_file*, ++ size_t, ++ struct vm_hole**); + + #endif +diff --git a/upatch-manage/upatch-ptrace.c b/upatch-manage/upatch-ptrace.c +index 03296bc2c2ad1fdbd54bc010134d18c061f8d06e..7459a4d6269f3fe29e78650bdf986e41ce1f0d53 100644 +--- a/upatch-manage/upatch-ptrace.c ++++ b/upatch-manage/upatch-ptrace.c +@@ -27,260 +27,279 @@ + #include + #include + #include +- ++#ifdef __riscv ++/* user_regs_struct defined here */ ++#include ++#endif + #include "upatch-common.h" + #include "upatch-ptrace.h" + + /* process's memory access */ +-int upatch_process_mem_read(struct upatch_process *proc, unsigned long src, +- void *dst, size_t size) ++int upatch_process_mem_read(struct upatch_process* proc, ++ unsigned long src, ++ void* dst, ++ size_t size) + { +- ssize_t r = pread(proc->memfd, dst, size, (off_t)src); ++ ssize_t r = pread(proc->memfd, dst, size, (off_t)src); + +- return r != (ssize_t)size ? -1 : 0; ++ return r != (ssize_t)size ? -1 : 0; + } + +-static int upatch_process_mem_write_ptrace(struct upatch_process *proc, +- const void *src, unsigned long dst, size_t size) ++static int upatch_process_mem_write_ptrace(struct upatch_process* proc, ++ const void* src, ++ unsigned long dst, ++ size_t size) + { +- long ret; +- +- while (ROUND_DOWN(size, sizeof(long)) != 0) { +- ret = ptrace(PTRACE_POKEDATA, proc->pid, dst, *(const unsigned long *)src); +- if (ret) { +- return -1; +- } +- dst += sizeof(long); +- src += sizeof(long); +- size -= sizeof(long); +- } +- +- if (size) { +- long tmp; +- +- tmp = ptrace(PTRACE_PEEKDATA, proc->pid, dst, NULL); +- if (tmp == -1 && errno) { +- return -1; +- } +- memcpy(&tmp, src, size); +- +- ret = ptrace(PTRACE_POKEDATA, proc->pid, dst, tmp); +- if (ret) { +- return -1; +- } +- } +- +- return 0; ++ long ret; ++ ++ while (ROUND_DOWN(size, sizeof(long)) != 0) { ++ ret = ++ ptrace(PTRACE_POKEDATA, proc->pid, dst, *(const unsigned long*)src); ++ if (ret) { ++ return -1; ++ } ++ dst += sizeof(long); ++ src += sizeof(long); ++ size -= sizeof(long); ++ } ++ ++ if (size) { ++ long tmp; ++ ++ tmp = ptrace(PTRACE_PEEKDATA, proc->pid, dst, NULL); ++ if (tmp == -1 && errno) { ++ return -1; ++ } ++ memcpy(&tmp, src, size); ++ ++ ret = ptrace(PTRACE_POKEDATA, proc->pid, dst, tmp); ++ if (ret) { ++ return -1; ++ } ++ } ++ ++ return 0; + } + +-int upatch_process_mem_write(struct upatch_process *proc, const void *src, +- unsigned long dst, size_t size) ++int upatch_process_mem_write(struct upatch_process* proc, ++ const void* src, ++ unsigned long dst, ++ size_t size) + { +- static int use_pwrite = 1; +- ssize_t w; +- +- if (use_pwrite) { +- w = pwrite(proc->memfd, src, size, (off_t)dst); +- } +- if (!use_pwrite || (w == -1 && errno == EINVAL)) { +- use_pwrite = 0; +- return upatch_process_mem_write_ptrace(proc, src, dst, size); +- } +- +- return w != (ssize_t)size ? -1 : 0; ++ static int use_pwrite = 1; ++ ssize_t w; ++ ++ if (use_pwrite) { ++ w = pwrite(proc->memfd, src, size, (off_t)dst); ++ } ++ if (!use_pwrite || (w == -1 && errno == EINVAL)) { ++ use_pwrite = 0; ++ return upatch_process_mem_write_ptrace(proc, src, dst, size); ++ } ++ ++ return w != (ssize_t)size ? -1 : 0; + } + + static struct upatch_ptrace_ctx* upatch_ptrace_ctx_alloc( +- struct upatch_process *proc) ++ struct upatch_process* proc) + { +- struct upatch_ptrace_ctx *p; ++ struct upatch_ptrace_ctx* p; + +- p = malloc(sizeof(*p)); +- if (!p) { +- return NULL; +- } ++ p = malloc(sizeof(*p)); ++ if (!p) { ++ return NULL; ++ } + +- memset(p, 0, sizeof(*p)); ++ memset(p, 0, sizeof(*p)); + +- p->execute_until = 0UL; +- p->running = 1; +- p->proc = proc; ++ p->execute_until = 0UL; ++ p->running = 1; ++ p->proc = proc; + +- INIT_LIST_HEAD(&p->list); +- list_add(&p->list, &proc->ptrace.pctxs); ++ INIT_LIST_HEAD(&p->list); ++ list_add(&p->list, &proc->ptrace.pctxs); + +- return p; ++ return p; + } + +-int upatch_ptrace_attach_thread(struct upatch_process *proc, int tid) ++int upatch_ptrace_attach_thread(struct upatch_process* proc, int tid) + { +- struct upatch_ptrace_ctx *pctx = upatch_ptrace_ctx_alloc(proc); +- if (pctx == NULL) { +- log_error("Failed to alloc ptrace context"); +- return -1; +- } +- +- pctx->pid = tid; +- log_debug("Attaching to %d...", tid); +- +- long ret = ptrace(PTRACE_ATTACH, tid, NULL, NULL); +- if (ret < 0) { +- log_error("Failed to attach thread, pid=%d, ret=%ld\n", tid, ret); +- return -1; +- } +- +- do { +- int status = 0; +- +- ret = waitpid(tid, &status, __WALL); +- if (ret < 0) { +- log_error("Failed to wait thread, tid=%d, ret=%ld\n", tid, ret); +- return -1; +- } +- +- /* We are expecting SIGSTOP */ +- if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) { +- break; +- } +- +- /* If we got SIGTRAP because we just got out of execve, wait +- * for the SIGSTOP +- */ +- if (WIFSTOPPED(status)) { +- status = (WSTOPSIG(status) == SIGTRAP) ? 0 : WSTOPSIG(status); +- } else if (WIFSIGNALED(status)) { +- /* Resend signal */ +- status = WTERMSIG(status); +- } +- +- ret = ptrace(PTRACE_CONT, tid, NULL, (void *)(uintptr_t)status); +- if (ret < 0) { +- log_error("Failed to continue thread, tid=%d, ret=%ld\n", tid, ret); +- return -1; +- } +- } while (1); +- +- pctx->running = 0; +- +- log_debug("OK\n"); +- return 0; ++ struct upatch_ptrace_ctx* pctx = upatch_ptrace_ctx_alloc(proc); ++ if (pctx == NULL) { ++ log_error("Failed to alloc ptrace context"); ++ return -1; ++ } ++ ++ pctx->pid = tid; ++ log_debug("Attaching to %d...", tid); ++ ++ long ret = ptrace(PTRACE_ATTACH, tid, NULL, NULL); ++ if (ret < 0) { ++ log_error("Failed to attach thread, pid=%d, ret=%ld\n", tid, ret); ++ return -1; ++ } ++ ++ do { ++ int status = 0; ++ ++ ret = waitpid(tid, &status, __WALL); ++ if (ret < 0) { ++ log_error("Failed to wait thread, tid=%d, ret=%ld\n", tid, ret); ++ return -1; ++ } ++ ++ /* We are expecting SIGSTOP */ ++ if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) { ++ break; ++ } ++ ++ /* If we got SIGTRAP because we just got out of execve, wait ++ * for the SIGSTOP ++ */ ++ if (WIFSTOPPED(status)) { ++ status = (WSTOPSIG(status) == SIGTRAP) ? 0 : WSTOPSIG(status); ++ } else if (WIFSIGNALED(status)) { ++ /* Resend signal */ ++ status = WTERMSIG(status); ++ } ++ ++ ret = ptrace(PTRACE_CONT, tid, NULL, (void*)(uintptr_t)status); ++ if (ret < 0) { ++ log_error("Failed to continue thread, tid=%d, ret=%ld\n", tid, ret); ++ return -1; ++ } ++ } while (1); ++ ++ pctx->running = 0; ++ ++ log_debug("OK\n"); ++ return 0; + } + +-int wait_for_stop(struct upatch_ptrace_ctx *pctx, const void *data) ++int wait_for_stop(struct upatch_ptrace_ctx* pctx, const void* data) + { +- long ret; +- int status = 0, pid = (int)(uintptr_t)data ?: pctx->pid; +- log_debug("wait_for_stop(pctx->pid=%d, pid=%d)\n", pctx->pid, pid); +- +- while (1) { +- ret = ptrace(PTRACE_CONT, pctx->pid, NULL, (void *)(uintptr_t)status); +- if (ret < 0) { +- log_error("Cannot start tracee %d, ret=%ld\n", pctx->pid, ret); +- return -1; +- } +- +- ret = waitpid(pid, &status, __WALL); +- if (ret < 0) { +- log_error("Cannot wait tracee %d, ret=%ld\n", pid, ret); +- return -1; +- } +- +- if (WIFSTOPPED(status)) { +- if (WSTOPSIG(status) == SIGSTOP || WSTOPSIG(status) == SIGTRAP) { +- break; +- } +- status = WSTOPSIG(status); +- continue; +- } +- +- status = WIFSIGNALED(status) ? WTERMSIG(status) : 0; +- } +- +- return 0; ++ long ret; ++ int status = 0, pid = (int)(uintptr_t)data ?: pctx->pid; ++ log_debug("wait_for_stop(pctx->pid=%d, pid=%d)\n", pctx->pid, pid); ++ ++ while (1) { ++ ret = ptrace(PTRACE_CONT, pctx->pid, NULL, (void*)(uintptr_t)status); ++ if (ret < 0) { ++ log_error("Cannot start tracee %d, ret=%ld\n", pctx->pid, ret); ++ return -1; ++ } ++ ++ ret = waitpid(pid, &status, __WALL); ++ if (ret < 0) { ++ log_error("Cannot wait tracee %d, ret=%ld\n", pid, ret); ++ return -1; ++ } ++ ++ if (WIFSTOPPED(status)) { ++ if (WSTOPSIG(status) == SIGSTOP || WSTOPSIG(status) == SIGTRAP) { ++ break; ++ } ++ status = WSTOPSIG(status); ++ continue; ++ } ++ ++ status = WIFSIGNALED(status) ? WTERMSIG(status) : 0; ++ } ++ ++ return 0; + } + +-int upatch_ptrace_detach(struct upatch_ptrace_ctx *pctx) ++int upatch_ptrace_detach(struct upatch_ptrace_ctx* pctx) + { +- if (!pctx->pid) { +- return 0; +- } +- +- log_debug("Detaching from %d...", pctx->pid); +- long ret = ptrace(PTRACE_DETACH, pctx->pid, NULL, NULL); +- if (ret < 0) { +- log_error("Failed to detach from process, pid=%d, ret=%ld\n", pctx->pid, ret); +- return -errno; +- } +- log_debug("OK\n"); +- +- pctx->running = 1; +- pctx->pid = 0; +- return 0; ++ if (!pctx->pid) { ++ return 0; ++ } ++ ++ log_debug("Detaching from %d...", pctx->pid); ++ long ret = ptrace(PTRACE_DETACH, pctx->pid, NULL, NULL); ++ if (ret < 0) { ++ log_error("Failed to detach from process, pid=%d, ret=%ld\n", pctx->pid, ++ ret); ++ return -errno; ++ } ++ log_debug("OK\n"); ++ ++ pctx->running = 1; ++ pctx->pid = 0; ++ return 0; + } + +-long upatch_execute_remote(struct upatch_ptrace_ctx *pctx, +- const unsigned char *code, size_t codelen, +- struct user_regs_struct *pregs) ++long upatch_execute_remote(struct upatch_ptrace_ctx* pctx, ++ const unsigned char* code, ++ size_t codelen, ++ struct user_regs_struct* pregs) + { +- return upatch_arch_execute_remote_func( +- pctx, code, codelen, pregs, wait_for_stop, NULL); ++ return upatch_arch_execute_remote_func(pctx, code, codelen, pregs, ++ wait_for_stop, NULL); + } + +-unsigned long upatch_mmap_remote(struct upatch_ptrace_ctx *pctx, +- unsigned long addr, size_t length, unsigned long prot, +- unsigned long flags, unsigned long fd, unsigned long offset) ++unsigned long upatch_mmap_remote(struct upatch_ptrace_ctx* pctx, ++ unsigned long addr, ++ size_t length, ++ unsigned long prot, ++ unsigned long flags, ++ unsigned long fd, ++ unsigned long offset) + { +- long ret; +- unsigned long res = 0; +- +- log_debug("mmap_remote: 0x%lx+%lx, %lx, %lx, %lu, %lx\n", addr, length, +- prot, flags, fd, offset); +- ret = upatch_arch_syscall_remote(pctx, __NR_mmap, (unsigned long)addr, +- length, prot, flags, fd, offset, &res); +- if (ret < 0) { +- return 0; +- } +- if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { +- errno = -(int)res; +- return 0; +- } +- return res; ++ long ret; ++ unsigned long res = 0; ++ ++ log_debug("mmap_remote: 0x%lx+%lx, %lx, %lx, %lu, %lx\n", addr, length, ++ prot, flags, fd, offset); ++ ret = upatch_arch_syscall_remote(pctx, __NR_mmap, (unsigned long)addr, ++ length, prot, flags, fd, offset, &res); ++ if (ret < 0) { ++ return 0; ++ } ++ if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { ++ errno = -(int)res; ++ return 0; ++ } ++ return res; + } + +-int upatch_mprotect_remote(struct upatch_ptrace_ctx *pctx, unsigned long addr, +- size_t length, unsigned long prot) ++int upatch_mprotect_remote(struct upatch_ptrace_ctx* pctx, ++ unsigned long addr, ++ size_t length, ++ unsigned long prot) + { +- long ret; +- unsigned long res; +- +- log_debug("mprotect_remote: 0x%lx+%lx\n", addr, length); +- ret = upatch_arch_syscall_remote(pctx, __NR_mprotect, +- (unsigned long)addr, length, prot, 0, +- 0, 0, &res); +- if (ret < 0) +- return -1; +- if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { +- errno = -(int)res; +- return -1; +- } +- +- return 0; ++ long ret; ++ unsigned long res; ++ ++ log_debug("mprotect_remote: 0x%lx+%lx\n", addr, length); ++ ret = upatch_arch_syscall_remote(pctx, __NR_mprotect, (unsigned long)addr, ++ length, prot, 0, 0, 0, &res); ++ if (ret < 0) { ++ return -1; ++ } ++ if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { ++ errno = -(int)res; ++ return -1; ++ } ++ ++ return 0; + } + +-int upatch_munmap_remote(struct upatch_ptrace_ctx *pctx, unsigned long addr, +- size_t length) ++int upatch_munmap_remote(struct upatch_ptrace_ctx* pctx, ++ unsigned long addr, ++ size_t length) + { +- long ret; +- unsigned long res; +- +- log_debug("munmap_remote: 0x%lx+%lx\n", addr, length); +- ret = upatch_arch_syscall_remote(pctx, __NR_munmap, (unsigned long)addr, +- length, 0, 0, 0, 0, &res); +- if (ret < 0) +- return -1; +- if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { +- errno = -(int)res; +- return -1; +- } +- return 0; ++ long ret; ++ unsigned long res; ++ ++ log_debug("munmap_remote: 0x%lx+%lx\n", addr, length); ++ ret = upatch_arch_syscall_remote(pctx, __NR_munmap, (unsigned long)addr, ++ length, 0, 0, 0, 0, &res); ++ if (ret < 0) ++ return -1; ++ if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { ++ errno = -(int)res; ++ return -1; ++ } ++ return 0; + } +diff --git a/upatch-manage/upatch-ptrace.h b/upatch-manage/upatch-ptrace.h +index b88c656fdcc4e4a1ac4164d763b08878b617ebb3..967d5247bf2d272e0817d13f96085027d4f6d4c4 100644 +--- a/upatch-manage/upatch-ptrace.h ++++ b/upatch-manage/upatch-ptrace.h +@@ -22,64 +22,90 @@ + #define __UPATCH_PTRACE__ + + #include ++#ifdef __riscv ++#include ++#endif + +-#include "upatch-process.h" + #include "list.h" + #include "log.h" ++#include "upatch-process.h" + + #define MAX_ERRNO 4095 + + struct upatch_ptrace_ctx { +- int pid; +- int running; +- unsigned long execute_until; +- struct upatch_process *proc; +- struct list_head list; ++ int pid; ++ int running; ++ unsigned long execute_until; ++ struct upatch_process* proc; ++ struct list_head list; + }; + + #define proc2pctx(proc) \ +- list_first_entry(&(proc)->ptrace.pctxs, struct upatch_ptrace_ctx, list) +- +-int upatch_process_mem_read(struct upatch_process *proc, unsigned long src, +- void *dst, size_t size); +- +-int upatch_process_mem_write(struct upatch_process *, const void *, unsigned long, +- size_t); +- +-int upatch_ptrace_attach_thread(struct upatch_process *, int); +- +-int upatch_ptrace_detach(struct upatch_ptrace_ctx *); +- +-int wait_for_stop(struct upatch_ptrace_ctx *, const void *); +- +-void copy_regs(struct user_regs_struct *, struct user_regs_struct *); +- +-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); +- +-long upatch_arch_syscall_remote(struct upatch_ptrace_ctx *, int, unsigned long, +- unsigned long, unsigned long, unsigned long, +- unsigned long, unsigned long, unsigned long *); +- +-unsigned long upatch_mmap_remote(struct upatch_ptrace_ctx *pctx, +- unsigned long addr, size_t length, unsigned long prot, +- unsigned long flags, unsigned long fd, unsigned long offset); +- +-int upatch_mprotect_remote(struct upatch_ptrace_ctx *pctx, unsigned long addr, +- size_t length, unsigned long prot); +- +-int upatch_munmap_remote(struct upatch_ptrace_ctx *, unsigned long, size_t); +- +-long upatch_execute_remote(struct upatch_ptrace_ctx *, const unsigned char *, +- size_t, struct user_regs_struct *); ++ list_first_entry(&(proc)->ptrace.pctxs, struct upatch_ptrace_ctx, list) ++ ++int upatch_process_mem_read(struct upatch_process* proc, ++ unsigned long src, ++ void* dst, ++ size_t size); ++ ++int upatch_process_mem_write(struct upatch_process*, ++ const void*, ++ unsigned long, ++ size_t); ++ ++int upatch_ptrace_attach_thread(struct upatch_process*, int); ++ ++int upatch_ptrace_detach(struct upatch_ptrace_ctx*); ++ ++int wait_for_stop(struct upatch_ptrace_ctx*, const void*); ++ ++void copy_regs(struct user_regs_struct*, struct user_regs_struct*); ++ ++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); ++ ++long upatch_arch_syscall_remote(struct upatch_ptrace_ctx*, ++ int, ++ unsigned long, ++ unsigned long, ++ unsigned long, ++ unsigned long, ++ unsigned long, ++ unsigned long, ++ unsigned long*); ++ ++unsigned long upatch_mmap_remote(struct upatch_ptrace_ctx* pctx, ++ unsigned long addr, ++ size_t length, ++ unsigned long prot, ++ unsigned long flags, ++ unsigned long fd, ++ unsigned long offset); ++ ++int upatch_mprotect_remote(struct upatch_ptrace_ctx* pctx, ++ unsigned long addr, ++ size_t length, ++ unsigned long prot); ++ ++int upatch_munmap_remote(struct upatch_ptrace_ctx*, unsigned long, size_t); ++ ++long upatch_execute_remote(struct upatch_ptrace_ctx*, ++ const unsigned char*, ++ size_t, ++ struct user_regs_struct*); + + 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 +diff --git a/upatch-manage/upatch-relocation.c b/upatch-manage/upatch-relocation.c +index a4200b6980fe638844483742b8498caa5746303c..688cbc0be6c19db0cd17b813a323e27965eccde7 100644 +--- a/upatch-manage/upatch-relocation.c ++++ b/upatch-manage/upatch-relocation.c +@@ -25,32 +25,35 @@ + + int apply_relocations(struct upatch_elf *uelf) + { +- unsigned int i; +- int err = 0; +- +- /* Now do relocations. */ +- for (i = 1; i < uelf->info.hdr->e_shnum; i++) { +- unsigned int infosec = uelf->info.shdrs[i].sh_info; +- const char *name = +- uelf->info.shstrtab + uelf->info.shdrs[i].sh_name; +- +- /* Not a valid relocation section? */ +- if (infosec >= uelf->info.hdr->e_shnum) +- continue; +- +- /* Don't bother with non-allocated sections */ +- if (!(uelf->info.shdrs[infosec].sh_flags & SHF_ALLOC)) +- continue; +- +- log_debug("Relocate '%s'\n", name); +- if (uelf->info.shdrs[i].sh_type == SHT_REL) { +- return -EPERM; +- } else if (uelf->info.shdrs[i].sh_type == SHT_RELA) { +- err = apply_relocate_add(uelf, uelf->index.sym, i); +- } +- +- if (err < 0) +- break; +- } +- return err; ++ unsigned int i; ++ int err = 0; ++ ++ /* Now do relocations. */ ++ for (i = 1; i < uelf->info.hdr->e_shnum; i++) { ++ unsigned int infosec = uelf->info.shdrs[i].sh_info; ++ const char *name = ++ uelf->info.shstrtab + uelf->info.shdrs[i].sh_name; ++ ++ /* Not a valid relocation section? */ ++ if (infosec >= uelf->info.hdr->e_shnum) { ++ continue; ++ } ++ ++ /* Don't bother with non-allocated sections */ ++ if (!(uelf->info.shdrs[infosec].sh_flags & SHF_ALLOC)) { ++ continue; ++ } ++ ++ log_debug("Relocate '%s'\n", name); ++ if (uelf->info.shdrs[i].sh_type == SHT_REL) { ++ return -EPERM; ++ } else if (uelf->info.shdrs[i].sh_type == SHT_RELA) { ++ err = apply_relocate_add(uelf, uelf->index.sym, i); ++ } ++ ++ if (err < 0) { ++ break; ++ } ++ } ++ return err; + } +\ No newline at end of file +diff --git a/upatch-manage/upatch-resolve.c b/upatch-manage/upatch-resolve.c +index 5f1c2de524e549f263070ab8ad67d96d7c16eb66..b92c63590c4888fbfea9e46f03186f2c931286a7 100644 +--- a/upatch-manage/upatch-resolve.c ++++ b/upatch-manage/upatch-resolve.c +@@ -301,33 +301,33 @@ int simplify_symbols(struct upatch_elf *uelf, struct object_file *obj) + name = uelf->strtab + sym[i].st_name; + + switch (sym[i].st_shndx) { +- case SHN_COMMON: +- log_debug("Unsupported common symbol '%s'\n", name); +- ret = -ENOEXEC; +- break; +- case SHN_ABS: +- break; +- case SHN_UNDEF: +- elf_addr = resolve_symbol(uelf, obj, name, sym[i]); +- if (!elf_addr) { ++ case SHN_COMMON: ++ log_debug("Unsupported common symbol '%s'\n", name); + ret = -ENOEXEC; +- } +- sym[i].st_value = elf_addr; +- log_debug("Resolved symbol '%s' at 0x%lx\n", +- name, (unsigned long)sym[i].st_value); +- break; +- case SHN_LIVEPATCH: +- sym[i].st_value += uelf->relf->load_bias; +- log_debug("Resolved livepatch symbol '%s' at 0x%lx\n", +- name, (unsigned long)sym[i].st_value); +- break; +- default: +- /* use real address to calculate secbase */ +- secbase = uelf->info.shdrs[sym[i].st_shndx].sh_addralign; +- sym[i].st_value += secbase; +- log_debug("Symbol '%s' at 0x%lx\n", +- name, (unsigned long)sym[i].st_value); +- break; ++ break; ++ case SHN_ABS: ++ break; ++ case SHN_UNDEF: ++ elf_addr = resolve_symbol(uelf, obj, name, sym[i]); ++ if (!elf_addr) { ++ ret = -ENOEXEC; ++ } ++ sym[i].st_value = elf_addr; ++ log_debug("Resolved symbol '%s' at 0x%lx\n", ++ name, (unsigned long)sym[i].st_value); ++ break; ++ case SHN_LIVEPATCH: ++ sym[i].st_value += uelf->relf->load_bias; ++ log_debug("Resolved livepatch symbol '%s' at 0x%lx\n", ++ name, (unsigned long)sym[i].st_value); ++ break; ++ default: ++ /* use real address to calculate secbase */ ++ secbase = uelf->info.shdrs[sym[i].st_shndx].sh_addralign; ++ sym[i].st_value += secbase; ++ log_debug("Symbol '%s' at 0x%lx\n", ++ name, (unsigned long)sym[i].st_value); ++ break; + } + } + diff --git a/syscare.spec b/syscare.spec index 91148be..5f2aa2f 100644 --- a/syscare.spec +++ b/syscare.spec @@ -5,7 +5,7 @@ ############################################ Name: syscare Version: 1.2.2 -Release: 4 +Release: 5 Summary: System hot-fix service License: MulanPSL-2.0 and GPL-2.0-only URL: https://gitee.com/openeuler/syscare @@ -30,6 +30,7 @@ Patch0008: 0008-all-remove-signal-handler.patch Patch0009: 0009-project-update-Cargo.lock.patch Patch0010: 0010-syscare-remove-working-directory-check.patch Patch0011: 0011-all-fix-cargo-clippy-warnings.patch +Patch0012: 0012-backport-riscv64-to-master.patch ############### Description ################ %description @@ -136,6 +137,9 @@ Syscare patch building toolset. ################ Change log ################ ############################################ %changelog +* Wed Apr 23 2025 yuanchicheng - 1.2.2-5 +- backport riscv64 to master + * Thu Feb 20 2025 renoseven - 1.2.2-4 - all: fix rust 1.84 compile failure - syscare: fix cannot find working directory -- Gitee