From a5cfe73c3c3065975e0ceca98a854df91c142d93 Mon Sep 17 00:00:00 2001 From: Longjun Luo Date: Wed, 22 Feb 2023 17:15:22 +0800 Subject: [PATCH 1/2] upatch: remove useless FDE from eh_frame section Basically, eh_frame consists of CIE and FDE. In upatch, we may remove useless symbol in the relocation list of eh_frame. And it will make the link to have the error - "no .eh_frame_hdr table..." To solve this problem, we need to remove useless FDE in the eh_frame at the same time. Signed-off-by: Longjun Luo --- upatch/upatch-diff/create-diff-object.c | 15 +++- upatch/upatch-diff/elf-common.h | 11 +++ upatch/upatch-diff/elf-create.c | 1 - upatch/upatch-diff/elf-debug.c | 105 ++++++++++++++++++++++++ upatch/upatch-diff/elf-debug.h | 2 + upatch/upatch-diff/log.h | 7 +- 6 files changed, 137 insertions(+), 4 deletions(-) diff --git a/upatch/upatch-diff/create-diff-object.c b/upatch/upatch-diff/create-diff-object.c index c578b5e..3813940 100644 --- a/upatch/upatch-diff/create-diff-object.c +++ b/upatch/upatch-diff/create-diff-object.c @@ -712,19 +712,27 @@ static int include_new_globals(struct upatch_elf *uelf) static void include_debug_sections(struct upatch_elf *uelf) { - struct section *sec; struct rela *rela, *saferela; + struct section *sec = NULL, *eh_sec = NULL; /* include all .debug_* sections */ list_for_each_entry(sec, &uelf->sections, list) { if (is_debug_section(sec)) { sec->include = 1; + if (!is_rela_section(sec) && sec->secsym) sec->secsym->include = 1; + + if (!is_rela_section(sec) && is_eh_frame(sec)) + eh_sec = sec; } } - /* modify relocation entry here, remove unincluded symbol in debug relocation section */ + /* + * modify relocation entry here + * remove unincluded symbol in debug relocation section + * for eh_frame section, sync the FDE at the same time + */ list_for_each_entry(sec, &uelf->sections, list) { if (!is_rela_section(sec) || !is_debug_section(sec)) continue; @@ -733,6 +741,9 @@ static void include_debug_sections(struct upatch_elf *uelf) if (!rela->sym->sec->include) list_del(&rela->list); } + + if (eh_sec) + upatch_rebuild_eh_frame(eh_sec); } /* currently, there si no special section need to be handled */ diff --git a/upatch/upatch-diff/elf-common.h b/upatch/upatch-diff/elf-common.h index c521fb7..06dae62 100644 --- a/upatch/upatch-diff/elf-common.h +++ b/upatch/upatch-diff/elf-common.h @@ -71,6 +71,17 @@ static inline bool is_except_section(struct section *sec) return !strncmp(sec->name, ".gcc_except_table", 17); } +static inline bool is_eh_frame(struct section *sec) +{ + char *name; + if (is_rela_section(sec)) + name = sec->base->name; + else + name = sec->name; + + return !strncmp(name, ".eh_frame", 9); +} + static inline bool is_debug_section(struct section *sec) { char *name; diff --git a/upatch/upatch-diff/elf-create.c b/upatch/upatch-diff/elf-create.c index 319020c..07af694 100644 --- a/upatch/upatch-diff/elf-create.c +++ b/upatch/upatch-diff/elf-create.c @@ -429,7 +429,6 @@ void upatch_rebuild_relocations(struct upatch_elf *uelf) if (!symtab) ERROR("missing .symtab section in rebuild relocations. \n"); - list_for_each_entry(relasec, &uelf->sections, list) { if (!is_rela_section(relasec)) continue; diff --git a/upatch/upatch-diff/elf-debug.c b/upatch/upatch-diff/elf-debug.c index 9451468..c21310b 100644 --- a/upatch/upatch-diff/elf-debug.c +++ b/upatch/upatch-diff/elf-debug.c @@ -88,7 +88,112 @@ next: } } +/* debuginfo releated */ +static inline bool skip_bytes(unsigned char **iter, unsigned char *end, unsigned int len) +{ + if ((unsigned int)(end - *iter) < len) { + *iter = end; + return false; + } + *iter += len; + return true; +} + +void upatch_rebuild_eh_frame(struct section *sec) +{ + void *eh_frame; + unsigned long long frame_size; + struct rela *rela; + unsigned char *data, *data_end; + unsigned int hdr_length, hdr_id; + unsigned int current_offset; + unsigned int count = 0; + + /* sanity check */ + if (!is_eh_frame(sec) || is_rela_section(sec)) + return; + + list_for_each_entry(rela, &sec->rela->relas, list) + count ++; + + /* currently, only delete is possible */ + if (sec->rela->sh.sh_entsize != 0 && + count == sec->rela->sh.sh_size / sec->rela->sh.sh_entsize) + return; + + log_debug("sync modification for eh_frame \n"); + + data = sec->data->d_buf; + data_end = sec->data->d_buf + sec->data->d_size; + + /* in this time, some relcation entries may have been deleted */ + frame_size = 0; + eh_frame = malloc(sec->data->d_size); + if (!eh_frame) + ERROR("malloc eh_frame failed \n"); + + /* 8 is the offset of PC begin */ + current_offset = 8; + list_for_each_entry(rela, &sec->rela->relas, list) { + unsigned int offset = rela->offset; + bool found_rela = false; + log_debug("handle relocaton offset at 0x%x \n", offset); + while (data != data_end) { + void *__src = data; + + log_debug("current handle offset is 0x%x \n", current_offset); + + REQUIRE(skip_bytes(&data, data_end, 4), "no length to be read"); + hdr_length = *(unsigned int *)(data - 4); + + REQUIRE(hdr_length != 0xffffffff, "64 bit .eh_frame is not supported"); + /* if it is 0, we reach the end. */ + if (hdr_length == 0) + break; + + REQUIRE(skip_bytes(&data, data_end, 4), "no length to be read"); + hdr_id = *(unsigned int *)(data - 4); + + REQUIRE(skip_bytes(&data, data_end, hdr_length - 4), "no length to be read"); + + if (current_offset == offset) + found_rela = true; + + /* CIE or relocation releated FDE */ + if (hdr_id == 0 || found_rela) { + memcpy(eh_frame + frame_size, __src, hdr_length + 4); + /* update rela offset to point to new offset, and also hdr_id */ + if (found_rela) { + /* 4 is the offset of hdr_id and 8 is the offset of PC begin */ + *(unsigned int *)(eh_frame + frame_size + 4) = frame_size + 4; + rela->offset = frame_size + 8; + } + + frame_size += (hdr_length + 4); + } else { + log_debug("remove FDE at 0x%x \n", current_offset); + } + /* hdr_length(value) + hdr_length(body) */ + current_offset += (4 + hdr_length); + + if (found_rela) + break; + } + if (!found_rela) + ERROR("No FDE found for relocation at 0x%x \n", offset); + } + + /* + * FIXME: data may not reach the data_end, since we have found + * all FDE for relocation entries, the only problem here is + * we may miss the CIE, but CIE is in the beginning ? + */ + + sec->data->d_buf = eh_frame; + sec->data->d_size = frame_size; + sec->sh.sh_size = frame_size; +} diff --git a/upatch/upatch-diff/elf-debug.h b/upatch/upatch-diff/elf-debug.h index b9c1c7a..60e3b2f 100644 --- a/upatch/upatch-diff/elf-debug.h +++ b/upatch/upatch-diff/elf-debug.h @@ -30,6 +30,8 @@ void upatch_print_changes(struct upatch_elf *); void upatch_dump_kelf(struct upatch_elf *); +void upatch_rebuild_eh_frame(struct section *); + #endif /* __UPATCH_ELF_DEBUG_H_ */ diff --git a/upatch/upatch-diff/log.h b/upatch/upatch-diff/log.h index a1fb18d..190fb03 100644 --- a/upatch/upatch-diff/log.h +++ b/upatch/upatch-diff/log.h @@ -42,7 +42,6 @@ enum exit_status{ #define ERROR(format, ...) \ error(EXIT_STATUS_ERROR, 0, "ERROR: %s: %s: %d: " format, logprefix, __FUNCTION__, __LINE__, ##__VA_ARGS__) - #define DIFF_FATAL(format, ...) \ error(EXIT_STATUS_DIFF_FATAL, 0, "ERROR: %s: %s: %d: " format, logprefix, __FUNCTION__, __LINE__, ##__VA_ARGS__) @@ -57,6 +56,12 @@ enum exit_status{ printf(format, ##__VA_ARGS__); \ }) +#define REQUIRE(COND, message) \ + do \ + if (!(COND)) \ + ERROR(message); \ + while (0) + enum loglevel { DEBUG, NORMAL, -- Gitee From 40898488ac6bbee018a09d9b5e3f3e168fc3855e Mon Sep 17 00:00:00 2001 From: Longjun Luo Date: Thu, 23 Feb 2023 19:01:50 +0800 Subject: [PATCH 2/2] upatch: add support for .text.startup. prefix Signed-off-by: Longjun Luo --- upatch/upatch-diff/create-diff-object.c | 5 +++-- upatch/upatch-diff/elf-compare.c | 4 +--- upatch/upatch-diff/elf-debug.c | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/upatch/upatch-diff/create-diff-object.c b/upatch/upatch-diff/create-diff-object.c index 3813940..77b37f6 100644 --- a/upatch/upatch-diff/create-diff-object.c +++ b/upatch/upatch-diff/create-diff-object.c @@ -210,6 +210,7 @@ static bool is_bundleable(struct symbol *sym) /* handle .text.unlikely. and then .text. */ char *func_prefix[] = { ".text.unlikely.", + ".text.startup.", /* used for cold startup main function */ ".text.hot.", ".text.", NULL, @@ -338,7 +339,7 @@ static bool locals_match(struct running_elf *relf, int idx, } if (!found){ - log_debug("can't find %s - in running_sym", running_sym->name); + log_debug("can't find %s - in running_sym \n", running_sym->name); return false; } } @@ -368,7 +369,7 @@ static bool locals_match(struct running_elf *relf, int idx, } if (!found){ - log_debug("can't find %s - in sym", sym->name); + log_debug("can't find %s - in sym \n", sym->name); return false; } } diff --git a/upatch/upatch-diff/elf-compare.c b/upatch/upatch-diff/elf-compare.c index fed51eb..3049249 100644 --- a/upatch/upatch-diff/elf-compare.c +++ b/upatch/upatch-diff/elf-compare.c @@ -28,7 +28,7 @@ #include "elf-compare.h" #include "elf-insn.h" -static int compare_correlated_symbol(struct symbol *sym, struct symbol *symtwin) +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 || @@ -51,8 +51,6 @@ static int compare_correlated_symbol(struct symbol *sym, struct symbol *symtwin) /* * For local symbols, we handle them based on their matching sections. */ - - return 0; } void upatch_compare_symbols(struct upatch_elf *uelf) diff --git a/upatch/upatch-diff/elf-debug.c b/upatch/upatch-diff/elf-debug.c index c21310b..83d2618 100644 --- a/upatch/upatch-diff/elf-debug.c +++ b/upatch/upatch-diff/elf-debug.c @@ -187,7 +187,7 @@ void upatch_rebuild_eh_frame(struct section *sec) /* * FIXME: data may not reach the data_end, since we have found * all FDE for relocation entries, the only problem here is - * we may miss the CIE, but CIE is in the beginning ? + * we may miss the CIE, but CIE is always in the beginning ? */ sec->data->d_buf = eh_frame; -- Gitee