mirror of
https://github.com/dynup/kpatch
synced 2025-01-02 18:52:02 +00:00
create-diff-object: add ORC section support
Finally add support for processing the ORC unwinder sections. The ORC unwinder sections are more special than the other special sections, so they need their own dedicated function to process them, though the code is similar to kpatch_regenerate_special_sections(). BTW, upstream livepatch still doesn't support the ORC unwinder. That change will be coming soon (probably Linux 4.19). Fixes #785. Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
This commit is contained in:
parent
a8133b1dc3
commit
1330dcc43d
@ -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)
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user