From patchwork Tue May 24 00:16:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Madhavan T. Venkataraman" X-Patchwork-Id: 12859581 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 776B7C433EF for ; Tue, 24 May 2022 00:21:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Cc:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=ejSJhHek+O03HukgZIuq7akjhDJWjDwhk8EcqLP4pFg=; b=s+1Gvjdd8IQZiu 0is8pOuy1ZYWTPwzh6/eXnDEuwyYftCZ/Pe92AFPa214tXXK4+a8Bu/mAtDb8Gd3CyiH+5X4Oi3tj 6bsAjSQIDUcU/56aE0R5Ri+tmWjxsOkV1RjByDrkRNKxSBaLmBgFbxEw28/VJpSr4lbO3Jy2bcPAx JCqefFBbkiYEQEzp+JMiHKLU3hOwuBHWPNxtGjWv8H6QaIc5z4mGruAm9YbCTHTOETQ8yhaZxa891 yFCEesS3ItY5z4sfuXU1e69P4CYv+g9RhJydKuHtSrd7QeZqbEexjCjwBt3ajGPDKl/rU5uwkr2H8 WPRzz4Plr4XcNR2FA7pw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1ntIHJ-006Kkt-L7; Tue, 24 May 2022 00:20:01 +0000 Received: from linux.microsoft.com ([13.77.154.182]) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1ntIEN-006JO7-Fg for linux-arm-kernel@lists.infradead.org; Tue, 24 May 2022 00:17:07 +0000 Received: from x64host.home (unknown [47.189.24.195]) by linux.microsoft.com (Postfix) with ESMTPSA id 842B720B894E; Mon, 23 May 2022 17:16:58 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 842B720B894E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1653351419; bh=BEl4S6V4psJ66iKSePCr3T5CVSyK8EsXFe/iBjZM0v4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=k2zG0DYZvlsYpgRdexocd7OeDGDCNKI+d036LGCk7yKAzCNYvqROeDaXKJ5BXpKtT B8KAuKBX+OJZXyU9cCqe799CTiHdOMz5rgxzuGu0uhal/n07siJJdTaPsojSDS9BBE enRPtc86MihszD3Lgnu9+cEmpUt93pEGdSJqpRaI= From: madvenka@linux.microsoft.com To: jpoimboe@redhat.com, peterz@infradead.org, chenzhongjin@huawei.com, mark.rutland@arm.com, broonie@kernel.org, nobuta.keiya@fujitsu.com, sjitindarsingh@gmail.com, catalin.marinas@arm.com, will@kernel.org, jamorris@linux.microsoft.com, linux-arm-kernel@lists.infradead.org, live-patching@vger.kernel.org, linux-kernel@vger.kernel.org, madvenka@linux.microsoft.com Subject: [RFC PATCH v2 12/20] objtool: arm64: Generate ORC data from CFI for object files Date: Mon, 23 May 2022 19:16:29 -0500 Message-Id: <20220524001637.1707472-13-madvenka@linux.microsoft.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220524001637.1707472-1-madvenka@linux.microsoft.com> References: <20220524001637.1707472-1-madvenka@linux.microsoft.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220523_171659_661515_E989071D X-CRM114-Status: GOOD ( 26.11 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org From: "Madhavan T. Venkataraman" Call orc_create() from cmd_fpv() to generate the ORC sections in object files for dynamic frame pointer validation. Signed-off-by: Madhavan T. Venkataraman --- arch/arm64/include/asm/orc_types.h | 35 ++++++++ tools/arch/arm64/include/asm/orc_types.h | 35 ++++++++ tools/objtool/Build | 1 + tools/objtool/arch/arm64/Build | 1 + tools/objtool/arch/arm64/include/arch/elf.h | 9 ++ .../arch/arm64/include/arch/endianness.h | 9 ++ tools/objtool/arch/arm64/orc.c | 86 +++++++++++++++++++ tools/objtool/builtin-fpv.c | 4 + tools/objtool/include/objtool/insn.h | 2 + tools/objtool/include/objtool/objtool.h | 1 + tools/objtool/insn.c | 20 +++++ tools/objtool/orc_gen.c | 12 ++- tools/objtool/sync-check.sh | 7 ++ 13 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/include/asm/orc_types.h create mode 100644 tools/arch/arm64/include/asm/orc_types.h create mode 100644 tools/objtool/arch/arm64/include/arch/elf.h create mode 100644 tools/objtool/arch/arm64/include/arch/endianness.h create mode 100644 tools/objtool/arch/arm64/orc.c diff --git a/arch/arm64/include/asm/orc_types.h b/arch/arm64/include/asm/orc_types.h new file mode 100644 index 000000000000..c7bb690ca7d9 --- /dev/null +++ b/arch/arm64/include/asm/orc_types.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Author: Madhavan T. Venkataraman (madvenka@linux.microsoft.com) + * + * Copyright (C) 2022 Microsoft Corporation + */ + +#ifndef _ORC_TYPES_H +#define _ORC_TYPES_H + +#include +#include +#include + +/* + * The ORC_REG_* registers are base registers which are used to find other + * registers on the stack. + * + * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the + * address of the previous frame: the caller's SP before it called the current + * function. + * + * ORC_REG_UNDEFINED means the corresponding register's value didn't change in + * the current frame. + * + * We only use base registers SP and FP -- which the previous SP is based on -- + * and PREV_SP and UNDEFINED -- which the previous FP is based on. + */ +#define ORC_REG_UNDEFINED 0 +#define ORC_REG_PREV_SP 1 +#define ORC_REG_SP 2 +#define ORC_REG_FP 3 +#define ORC_REG_MAX 4 + +#endif /* _ORC_TYPES_H */ diff --git a/tools/arch/arm64/include/asm/orc_types.h b/tools/arch/arm64/include/asm/orc_types.h new file mode 100644 index 000000000000..c7bb690ca7d9 --- /dev/null +++ b/tools/arch/arm64/include/asm/orc_types.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Author: Madhavan T. Venkataraman (madvenka@linux.microsoft.com) + * + * Copyright (C) 2022 Microsoft Corporation + */ + +#ifndef _ORC_TYPES_H +#define _ORC_TYPES_H + +#include +#include +#include + +/* + * The ORC_REG_* registers are base registers which are used to find other + * registers on the stack. + * + * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the + * address of the previous frame: the caller's SP before it called the current + * function. + * + * ORC_REG_UNDEFINED means the corresponding register's value didn't change in + * the current frame. + * + * We only use base registers SP and FP -- which the previous SP is based on -- + * and PREV_SP and UNDEFINED -- which the previous FP is based on. + */ +#define ORC_REG_UNDEFINED 0 +#define ORC_REG_PREV_SP 1 +#define ORC_REG_SP 2 +#define ORC_REG_FP 3 +#define ORC_REG_MAX 4 + +#endif /* _ORC_TYPES_H */ diff --git a/tools/objtool/Build b/tools/objtool/Build index a491f51c40b4..5de79c76f3f0 100644 --- a/tools/objtool/Build +++ b/tools/objtool/Build @@ -19,6 +19,7 @@ objtool-$(SUBCMD_FPV) += fpv.o objtool-$(SUBCMD_FPV) += cfi.o objtool-$(SUBCMD_FPV) += insn.o objtool-$(SUBCMD_FPV) += decode.o +objtool-$(SUBCMD_FPV) += orc_gen.o objtool-y += builtin-check.o objtool-y += builtin-orc.o diff --git a/tools/objtool/arch/arm64/Build b/tools/objtool/arch/arm64/Build index 3ff1f00c6a47..c026f5ddbd03 100644 --- a/tools/objtool/arch/arm64/Build +++ b/tools/objtool/arch/arm64/Build @@ -1 +1,2 @@ objtool-y += decode.o +objtool-$(SUBCMD_FPV) += orc.o diff --git a/tools/objtool/arch/arm64/include/arch/elf.h b/tools/objtool/arch/arm64/include/arch/elf.h new file mode 100644 index 000000000000..4ae6df2bd90c --- /dev/null +++ b/tools/objtool/arch/arm64/include/arch/elf.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ + +#ifndef _OBJTOOL_ARCH_ELF +#define _OBJTOOL_ARCH_ELF + +#define R_NONE R_AARCH64_NONE +#define R_PCREL R_AARCH64_PREL32 + +#endif /* _OBJTOOL_ARCH_ELF */ diff --git a/tools/objtool/arch/arm64/include/arch/endianness.h b/tools/objtool/arch/arm64/include/arch/endianness.h new file mode 100644 index 000000000000..7c362527da20 --- /dev/null +++ b/tools/objtool/arch/arm64/include/arch/endianness.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _ARCH_ENDIANNESS_H +#define _ARCH_ENDIANNESS_H + +#include + +#define __TARGET_BYTE_ORDER __LITTLE_ENDIAN + +#endif /* _ARCH_ENDIANNESS_H */ diff --git a/tools/objtool/arch/arm64/orc.c b/tools/objtool/arch/arm64/orc.c new file mode 100644 index 000000000000..cef14114e1ec --- /dev/null +++ b/tools/objtool/arch/arm64/orc.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Author: Madhavan T. Venkataraman (madvenka@linux.microsoft.com) + * + * Copyright (C) 2022 Microsoft Corporation + */ +#include + +#include + +#include +#include + +int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, + struct instruction *insn) +{ + struct cfi_reg *fp = &cfi->regs[CFI_FP]; + + memset(orc, 0, sizeof(*orc)); + + orc->sp_reg = ORC_REG_SP; + orc->fp_reg = ORC_REG_PREV_SP; + + if (!cfi || cfi->cfa.base == CFI_UNDEFINED || + (cfi->type == UNWIND_HINT_TYPE_CALL && !fp->offset)) { + /* + * The frame pointer has not been set up. This instruction is + * unreliable from an unwind perspective. + */ + return 0; + } + + orc->sp_offset = cfi->cfa.offset; + orc->fp_offset = fp->offset; + orc->type = cfi->type; + orc->end = cfi->end; + + return 0; +} + +static const char *reg_name(unsigned int reg) +{ + switch (reg) { + case ORC_REG_PREV_SP: + return "cfa"; + case ORC_REG_FP: + return "x29"; + case ORC_REG_SP: + return "sp"; + default: + return "?"; + } +} + +const char *orc_type_name(unsigned int type) +{ + switch (type) { + case UNWIND_HINT_TYPE_CALL: + return "call"; + default: + return "?"; + } +} + +void orc_print_reg(unsigned int reg, int offset) +{ + if (reg == ORC_REG_UNDEFINED) + printf("(und)"); + else + printf("%s%+d", reg_name(reg), offset); +} + +void orc_print_sp(void) +{ + printf(" cfa:"); +} + +void orc_print_fp(void) +{ + printf(" x29:"); +} + +bool orc_ignore_section(struct section *sec) +{ + return !strcmp(sec->name, ".head.text"); +} diff --git a/tools/objtool/builtin-fpv.c b/tools/objtool/builtin-fpv.c index ff57dde39587..bc8f88f204b5 100644 --- a/tools/objtool/builtin-fpv.c +++ b/tools/objtool/builtin-fpv.c @@ -59,6 +59,10 @@ int cmd_fpv(int argc, const char **argv) if (list_empty(&file->insn_list)) return 0; + ret = orc_create(file); + if (ret) + return ret; + if (!file->elf->changed) return 0; diff --git a/tools/objtool/include/objtool/insn.h b/tools/objtool/include/objtool/insn.h index 0b5596f8b12f..c555fcd88b43 100644 --- a/tools/objtool/include/objtool/insn.h +++ b/tools/objtool/include/objtool/insn.h @@ -80,12 +80,14 @@ struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruction *next_insn_same_func(struct objtool_file *file, struct instruction *insn); struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn); +bool insn_can_reloc(struct instruction *insn); bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2); bool same_function(struct instruction *insn1, struct instruction *insn2); bool is_first_func_insn(struct instruction *insn); int decode_instructions(struct objtool_file *file); int read_unwind_hints(struct objtool_file *file); + #define for_each_insn(file, insn) \ list_for_each_entry(insn, &file->insn_list, list) diff --git a/tools/objtool/include/objtool/objtool.h b/tools/objtool/include/objtool/objtool.h index e00c8dcc6885..6375566bb78b 100644 --- a/tools/objtool/include/objtool/objtool.h +++ b/tools/objtool/include/objtool/objtool.h @@ -46,5 +46,6 @@ int check(struct objtool_file *file); int orc_dump(const char *objname); int orc_create(struct objtool_file *file); int fpv_decode(struct objtool_file *file); +bool orc_ignore_section(struct section *sec); #endif /* _OBJTOOL_H */ diff --git a/tools/objtool/insn.c b/tools/objtool/insn.c index 669fca9b8e0d..f2a386bbd4b8 100644 --- a/tools/objtool/insn.c +++ b/tools/objtool/insn.c @@ -175,3 +175,23 @@ struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn) return insn->reloc; } + +/* + * This is a hack for Clang. Clang is aggressive about removing section + * symbols and then some. If we cannot find something to relocate an + * instruction against, we must not generate CFI for it or the ORC + * generation will fail later. + */ +bool insn_can_reloc(struct instruction *insn) +{ + struct section *insn_sec = insn->sec; + unsigned long insn_off = insn->offset; + + if (insn_sec->sym || + find_symbol_containing(insn_sec, insn_off) || + find_symbol_containing(insn_sec, insn_off - 1)) { + /* See elf_add_reloc_to_insn(). */ + return true; + } + return false; +} diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c index ea2e361ff7bc..bddf5889466f 100644 --- a/tools/objtool/orc_gen.c +++ b/tools/objtool/orc_gen.c @@ -14,6 +14,11 @@ #include #include +bool __weak orc_ignore_section(struct section *sec) +{ + return false; +} + static int write_orc_entry(struct elf *elf, struct section *orc_sec, struct section *ip_sec, unsigned int idx, struct section *insn_sec, unsigned long insn_off, @@ -87,13 +92,16 @@ int orc_create(struct objtool_file *file) struct instruction *insn; bool empty = true; - if (!sec->text) + if (!sec->text || orc_ignore_section(sec)) continue; sec_for_each_insn(file, sec, insn) { struct alt_group *alt_group = insn->alt_group; int i; + if (!insn_can_reloc(insn)) + continue; + if (!alt_group) { if (init_orc_entry(&orc, insn->cfi, insn)) return -1; @@ -137,7 +145,7 @@ int orc_create(struct objtool_file *file) } /* Add a section terminator */ - if (!empty) { + if (!empty && sec->sym) { orc_list_add(&orc_list, &null, sec, sec->sh.sh_size); nr++; } diff --git a/tools/objtool/sync-check.sh b/tools/objtool/sync-check.sh index ef1acb064605..0d0656f6ce4a 100755 --- a/tools/objtool/sync-check.sh +++ b/tools/objtool/sync-check.sh @@ -29,6 +29,13 @@ arch/x86/lib/insn.c ' fi +if [ "$SRCARCH" = "arm64" ]; then +FILES="$FILES +arch/arm64/include/asm/orc_types.h +include/linux/orc_entry.h +" +fi + check_2 () { file1=$1 file2=$2