diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 9290440..6f94ee8 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -861,13 +861,74 @@ static bool _kpatch_line_macro_change_only(struct kpatch_elf *kelf, return true; } +static bool _kpatch_line_macro_change_only_aarch64(struct kpatch_elf *kelf, + struct section *sec) +{ + unsigned char *start1, *start2; + unsigned long size, offset, insn_len; + struct rela *rela; + int lineonly = 0, found; + + insn_len = insn_length(kelf, 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 = sec->twin->data->d_buf; + start2 = sec->data->d_buf; + size = sec->sh.sh_size; + for (offset = 0; offset < size; offset += insn_len) { + if (!memcmp(start1 + offset, start2 + offset, insn_len)) + continue; + + /* Verify mov w2 */ + if (((start1[offset] & 0b11111) != 0x2) || (start1[offset+3] != 0x52) || + ((start1[offset] & 0b11111) != 0x2) || (start2[offset+3] != 0x52)) + return false; + + /* + * Verify zero or more string relas followed by a + * warn_slowpath_* or another similar rela. + */ + found = 0; + list_for_each_entry(rela, &sec->rela->relas, list) { + if (rela->offset < offset + insn_len) + continue; + if (rela->string) + continue; + if (!strncmp(rela->sym->name, "__warned.", 9) || + !strncmp(rela->sym->name, "__already_done.", 15)) + continue; + if (!strcmp(rela->sym->name, "__warn_printk")) { + found = 1; + break; + } + return false; + } + if (!found) + return false; + + lineonly = 1; + } + + if (!lineonly) + ERROR("no instruction changes detected for changed section %s", + sec->name); + + return true; +} + static bool kpatch_line_macro_change_only(struct kpatch_elf *kelf, struct section *sec) { switch(kelf->arch) { case AARCH64: - /* TODO */ - return false; + return _kpatch_line_macro_change_only_aarch64(kelf, sec); case PPC64: case S390: case X86_64: diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 751b76e..d037312 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -277,6 +277,8 @@ unsigned int insn_length(struct kpatch_elf *kelf, void *addr) char *insn = addr; switch(kelf->arch) { + case AARCH64: + return 4; case X86_64: insn_init(&decoded_insn, addr, 1);