From nobody Thu Feb 12 04:51:48 2026 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 324BAC77B60 for ; Fri, 28 Apr 2023 10:02:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345811AbjD1KCQ (ORCPT ); Fri, 28 Apr 2023 06:02:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44570 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230341AbjD1KBv (ORCPT ); Fri, 28 Apr 2023 06:01:51 -0400 Received: from out0-214.mail.aliyun.com (out0-214.mail.aliyun.com [140.205.0.214]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 84B1061BE; Fri, 28 Apr 2023 03:01:27 -0700 (PDT) X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R171e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=ay29a033018047187;MF=houwenlong.hwl@antgroup.com;NM=1;PH=DS;RN=17;SR=0;TI=SMTPD_---.STCEPeI_1682675581; Received: from localhost(mailfrom:houwenlong.hwl@antgroup.com fp:SMTPD_---.STCEPeI_1682675581) by smtp.aliyun-inc.com; Fri, 28 Apr 2023 17:53:01 +0800 From: "Hou Wenlong" To: linux-kernel@vger.kernel.org Cc: "Thomas Garnier" , "Lai Jiangshan" , "Kees Cook" , "Hou Wenlong" , "Steven Rostedt" , "Masami Hiramatsu" , "Mark Rutland" , "Thomas Gleixner" , "Ingo Molnar" , "Borislav Petkov" , "Dave Hansen" , , "H. Peter Anvin" , "Huacai Chen" , "Qing Zhang" , Subject: [PATCH RFC 22/43] x86/ftrace: Adapt ftrace nop patching for PIE support Date: Fri, 28 Apr 2023 17:51:02 +0800 Message-Id: <38a5029cd2590e04209117740f8912db36eff58f.1682673543.git.houwenlong.hwl@antgroup.com> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Thomas Garnier From: Thomas Garnier When using PIE with function tracing, the compiler generates a call through the GOT (call *__fentry__@GOTPCREL). This instruction takes 6-bytes instead of 5-bytes with a relative call. And -mnop-mcount option is not implemented for -fPIE now. If PIE is enabled, replace the 6th byte of the GOT call by a 1-byte nop so ftrace can handle the previous 5-bytes as before. [Hou Wenlong: Adapt code change and fix wrong offset calculation in make_nop_x86()] Signed-off-by: Thomas Garnier Co-developed-by: Hou Wenlong Signed-off-by: Hou Wenlong Cc: Lai Jiangshan Cc: Kees Cook --- arch/x86/kernel/ftrace.c | 46 ++++++++++++++++++++++- scripts/recordmcount.c | 81 ++++++++++++++++++++++++++-------------- 2 files changed, 98 insertions(+), 29 deletions(-) diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 5e7ead52cfdb..b795f9dde561 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -124,6 +124,50 @@ ftrace_modify_code_direct(unsigned long ip, const char= *old_code, return 0; } =20 +/* Bytes before call GOT offset */ +static const unsigned char got_call_preinsn[] =3D { 0xff, 0x15 }; + +static int __ref +ftrace_modify_initial_code(unsigned long ip, unsigned const char *old_code, + unsigned const char *new_code) +{ + unsigned char replaced[MCOUNT_INSN_SIZE + 1]; + + /* + * If PIE is not enabled default to the original approach to code + * modification. + */ + if (!IS_ENABLED(CONFIG_X86_PIE)) + return ftrace_modify_code_direct(ip, old_code, new_code); + + ftrace_expected =3D old_code; + + /* Ensure the instructions point to a call to the GOT */ + if (copy_from_kernel_nofault(replaced, (void *)ip, sizeof(replaced))) { + WARN_ONCE(1, "invalid function"); + return -EFAULT; + } + + if (memcmp(replaced, got_call_preinsn, sizeof(got_call_preinsn))) { + WARN_ONCE(1, "invalid function call"); + return -EINVAL; + } + + /* + * Build a nop slide with a 5-byte nop and 1-byte nop to keep the ftrace + * hooking algorithm working with the expected 5 bytes instruction. + */ + memset(replaced, x86_nops[1][0], sizeof(replaced)); + memcpy(replaced, new_code, MCOUNT_INSN_SIZE); + + /* replace the text with the new text */ + if (ftrace_poke_late) + text_poke_queue((void *)ip, replaced, MCOUNT_INSN_SIZE + 1, NULL); + else + text_poke_early((void *)ip, replaced, MCOUNT_INSN_SIZE + 1); + return 0; +} + int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned l= ong addr) { unsigned long ip =3D rec->ip; @@ -141,7 +185,7 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftra= ce *rec, unsigned long ad * just modify the code directly. */ if (addr =3D=3D MCOUNT_ADDR) - return ftrace_modify_code_direct(ip, old, new); + return ftrace_modify_initial_code(ip, old, new); =20 /* * x86 overrides ftrace_replace_code -- this function will never be used diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index e30216525325..02783a29d428 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -218,36 +218,10 @@ static void *mmap_file(char const *fname) return file_map; } =20 - -static unsigned char ideal_nop5_x86_64[5] =3D { 0x0f, 0x1f, 0x44, 0x00, 0x= 00 }; -static unsigned char ideal_nop5_x86_32[5] =3D { 0x3e, 0x8d, 0x74, 0x26, 0x= 00 }; -static unsigned char *ideal_nop; - static char rel_type_nop; - static int (*make_nop)(void *map, size_t const offset); =20 -static int make_nop_x86(void *map, size_t const offset) -{ - uint32_t *ptr; - unsigned char *op; - - /* Confirm we have 0xe8 0x0 0x0 0x0 0x0 */ - ptr =3D map + offset; - if (*ptr !=3D 0) - return -1; - - op =3D map + offset - 1; - if (*op !=3D 0xe8) - return -1; - - /* convert to nop */ - if (ulseek(offset - 1, SEEK_SET) < 0) - return -1; - if (uwrite(ideal_nop, 5) < 0) - return -1; - return 0; -} +static unsigned char *ideal_nop; =20 static unsigned char ideal_nop4_arm_le[4] =3D { 0x00, 0x00, 0xa0, 0xe1 }; = /* mov r0, r0 */ static unsigned char ideal_nop4_arm_be[4] =3D { 0xe1, 0xa0, 0x00, 0x00 }; = /* mov r0, r0 */ @@ -504,6 +478,50 @@ static void MIPS64_r_info(Elf64_Rel *const rp, unsigne= d sym, unsigned type) }).r_info; } =20 +static unsigned char ideal_nop5_x86_64[5] =3D { 0x0f, 0x1f, 0x44, 0x00, 0x= 00 }; +static unsigned char ideal_nop6_x86_64[6] =3D { 0x66, 0x0f, 0x1f, 0x44, 0x= 00, 0x00 }; +static unsigned char ideal_nop5_x86_32[5] =3D { 0x3e, 0x8d, 0x74, 0x26, 0x= 00 }; +static size_t ideal_nop_x86_size; + +static unsigned char stub_default_x86[2] =3D { 0xe8, 0x00 }; /* call rel= ative */ +static unsigned char stub_got_x86[3] =3D { 0xff, 0x15, 0x00 }; /* call .go= t */ +static unsigned char *stub_x86; +static size_t stub_x86_size; + +static int make_nop_x86(void *map, size_t const offset) +{ + uint32_t *ptr; + size_t stub_offset =3D offset + 1 - stub_x86_size; + + /* Confirm we have the expected stub */ + ptr =3D map + stub_offset; + if (memcmp(ptr, stub_x86, stub_x86_size)) + return -1; + + /* convert to nop */ + if (ulseek(stub_offset, SEEK_SET) < 0) + return -1; + if (uwrite(ideal_nop, ideal_nop_x86_size) < 0) + return -1; + return 0; +} + +/* Swap the stub and nop for a got call if the binary is built with PIE */ +static int is_fake_mcount_x86_x64(Elf64_Rel const *rp) +{ + if (ELF64_R_TYPE(rp->r_info) =3D=3D R_X86_64_GOTPCREL) { + ideal_nop =3D ideal_nop6_x86_64; + ideal_nop_x86_size =3D sizeof(ideal_nop6_x86_64); + stub_x86 =3D stub_got_x86; + stub_x86_size =3D sizeof(stub_got_x86); + mcount_adjust_64 =3D 1 - stub_x86_size; + } + + /* Once the relocation was checked, rollback to default */ + is_fake_mcount64 =3D fn_is_fake_mcount64; + return is_fake_mcount64(rp); +} + static int do_file(char const *const fname) { unsigned int reltype =3D 0; @@ -568,6 +586,9 @@ static int do_file(char const *const fname) rel_type_nop =3D R_386_NONE; make_nop =3D make_nop_x86; ideal_nop =3D ideal_nop5_x86_32; + ideal_nop_x86_size =3D sizeof(ideal_nop5_x86_32); + stub_x86 =3D stub_default_x86; + stub_x86_size =3D sizeof(stub_default_x86); mcount_adjust_32 =3D -1; gpfx =3D 0; break; @@ -597,9 +618,13 @@ static int do_file(char const *const fname) case EM_X86_64: make_nop =3D make_nop_x86; ideal_nop =3D ideal_nop5_x86_64; + ideal_nop_x86_size =3D sizeof(ideal_nop5_x86_64); + stub_x86 =3D stub_default_x86; + stub_x86_size =3D sizeof(stub_default_x86); reltype =3D R_X86_64_64; rel_type_nop =3D R_X86_64_NONE; - mcount_adjust_64 =3D -1; + is_fake_mcount64 =3D is_fake_mcount_x86_x64; + mcount_adjust_64 =3D 1 - stub_x86_size; gpfx =3D 0; break; } /* end switch */ --=20 2.31.1