diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index e430a8d..72cd44b 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -1998,6 +1998,94 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf, sec->base->data->d_size = dest_offset; } +#define ORC_IP_PTR_SIZE 4 + +/* + * This function is similar to kpatch_regenerate_special_section(), but + * customized for the ORC-related sections. ORC is more special than the other + * special sections because each ORC entry is split into .orc_unwind (struct + * orc_entry) and .orc_unwind_ip. + */ +static void kpatch_regenerate_orc_sections(struct kpatch_elf *kelf) +{ + struct rela *rela, *safe; + char *src, *dest, *str; + unsigned int src_idx = 0, dest_idx = 0, orc_entry_size; + struct section *orc_sec, *ip_sec; + + + str = getenv("ORC_STRUCT_SIZE"); + if (!str) + return; + orc_entry_size = atoi(str); + + LIST_HEAD(newrelas); + + orc_sec = find_section_by_name(&kelf->sections, ".orc_unwind"); + ip_sec = find_section_by_name(&kelf->sections, ".orc_unwind_ip"); + + if (!orc_sec || !ip_sec) + return; + + if (orc_sec->sh.sh_size % orc_entry_size != 0) + ERROR("bad .orc_unwind size"); + + if (ip_sec->sh.sh_size != + (orc_sec->sh.sh_size / orc_entry_size) * ORC_IP_PTR_SIZE) + ERROR(".orc_unwind/.orc_unwind_ip size mismatch"); + + src = orc_sec->data->d_buf; + dest = malloc(orc_sec->sh.sh_size); + if (!dest) + ERROR("malloc"); + + list_for_each_entry_safe(rela, safe, &ip_sec->rela->relas, list) { + + if (rela->sym->type != STT_FUNC || !rela->sym->sec->include) + goto next; + + /* copy orc entry */ + memcpy(dest + (dest_idx * orc_entry_size), + src + (src_idx * orc_entry_size), + orc_entry_size); + + /* move ip rela */ + list_del(&rela->list); + list_add_tail(&rela->list, &newrelas); + rela->offset = dest_idx * ORC_IP_PTR_SIZE; + rela->sym->include = 1; + + dest_idx++; +next: + src_idx++; + } + + if (!dest_idx) { + /* no changed or global functions referenced */ + orc_sec->status = ip_sec->status = ip_sec->rela->status = SAME; + orc_sec->include = ip_sec->include = ip_sec->rela->include = 0; + free(dest); + return; + } + + /* overwrite with new relas list */ + list_replace(&newrelas, &ip_sec->rela->relas); + + /* include the sections */ + orc_sec->include = ip_sec->include = ip_sec->rela->include = 1; + + /* + * Update data buf/size. + * + * The ip section can keep its old (zeroed data), though its size has + * possibly decreased. The ip rela section's data buf and size will be + * regenerated in kpatch_rebuild_rela_section_data(). + */ + orc_sec->data->d_buf = dest; + orc_sec->data->d_size = dest_idx * orc_entry_size; + ip_sec->data->d_size = dest_idx * ORC_IP_PTR_SIZE; +} + static void kpatch_check_relocations(struct kpatch_elf *kelf) { struct rela *rela; @@ -2278,6 +2366,8 @@ static void kpatch_process_special_sections(struct kpatch_elf *kelf) sec->rela->include = 0; } } + + kpatch_regenerate_orc_sections(kelf); } static struct sym_compare_type *kpatch_elf_locals(struct kpatch_elf *kelf) diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index ff5ae5c..10fc1bf 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -260,30 +260,35 @@ find_special_section_data() { fi [[ "$CONFIG_PARAVIRT" -eq 0 ]] && AWK_OPTIONS="-vskip_p=1" + [[ "$CONFIG_UNWINDER_ORC" -eq 0 ]] && AWK_OPTIONS="$AWK_OPTIONS -vskip_o=1" + SPECIAL_VARS="$(readelf -wi "$VMLINUX" | - gawk --non-decimal-data $AWK_OPTIONS ' - BEGIN { a = b = p = e = 0 } + gawk --non-decimal-data "$AWK_OPTIONS" ' + BEGIN { a = b = p = e = o = 0 } # Set state if name matches a == 0 && /DW_AT_name.* alt_instr[[:space:]]*$/ {a = 1; next} b == 0 && /DW_AT_name.* bug_entry[[:space:]]*$/ {b = 1; next} p == 0 && /DW_AT_name.* paravirt_patch_site[[:space:]]*$/ {p = 1; next} e == 0 && /DW_AT_name.* exception_table_entry[[:space:]]*$/ {e = 1; next} + o == 0 && /DW_AT_name.* orc_entry[[:space:]]*$/ {o = 1; next} # Reset state unless this abbrev describes the struct size a == 1 && !/DW_AT_byte_size/ { a = 0; next } b == 1 && !/DW_AT_byte_size/ { b = 0; next } p == 1 && !/DW_AT_byte_size/ { p = 0; next } e == 1 && !/DW_AT_byte_size/ { e = 0; next } + o == 1 && !/DW_AT_byte_size/ { o = 0; next } # Now that we know the size, stop parsing for it a == 1 {printf("export ALT_STRUCT_SIZE=%d\n", $4); a = 2} b == 1 {printf("export BUG_STRUCT_SIZE=%d\n", $4); b = 2} p == 1 {printf("export PARA_STRUCT_SIZE=%d\n", $4); p = 2} e == 1 {printf("export EX_STRUCT_SIZE=%d\n", $4); e = 2} + o == 1 {printf("export ORC_STRUCT_SIZE=%d\n", $4); o = 2} # Bail out once we have everything - a == 2 && b == 2 && (p == 2 || skip_p) && e == 2 {exit}')" + a == 2 && b == 2 && (p == 2 || skip_p) && e == 2 && (o == 2 || skip_o) {exit}')" [[ -n "$SPECIAL_VARS" ]] && eval "$SPECIAL_VARS" @@ -291,6 +296,7 @@ find_special_section_data() { [[ -z "$BUG_STRUCT_SIZE" ]] && die "can't find special struct bug_entry size" [[ -z "$EX_STRUCT_SIZE" ]] && die "can't find special struct paravirt_patch_site size" [[ -z "$PARA_STRUCT_SIZE" && "$CONFIG_PARAVIRT" -ne 0 ]] && die "can't find special struct paravirt_patch_site size" + [[ -z "$ORC_STRUCT_SIZE" && "$CONFIG_UNWINDER_ORC" -ne 0 ]] && die "can't find special struct orc_entry size" return } @@ -667,12 +673,17 @@ else KBUILD_EXTRA_SYMBOLS="$SYMVERSFILE" fi -# optional kernel configs: CONFIG_PARAVIRT +# optional kernel configs: if grep -q "CONFIG_PARAVIRT=y" "$CONFIGFILE"; then CONFIG_PARAVIRT=1 else CONFIG_PARAVIRT=0 fi +if grep -q "CONFIG_UNWINDER_ORC=y" "$CONFIGFILE"; then + CONFIG_UNWINDER_ORC=1 +else + CONFIG_UNWINDER_ORC=0 +fi # unsupported kernel option checking: CONFIG_DEBUG_INFO_SPLIT grep -q "CONFIG_DEBUG_INFO_SPLIT=y" "$CONFIGFILE" && die "kernel option 'CONFIG_DEBUG_INFO_SPLIT' not supported"