Message ID | 20230804090621.400-1-elver@google.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:44a:b0:3f2:4152:657d with SMTP id ez10csp177880vqb; Fri, 4 Aug 2023 04:01:12 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFeqxn95IZeI2u6+eOWUh80JDnv44m8BVniVoIaZuVJxNmxG8ep3Ltn6tTuoOLP1z3Vpk5O X-Received: by 2002:a17:906:3082:b0:99c:55c5:1c6e with SMTP id 2-20020a170906308200b0099c55c51c6emr1693001ejv.8.1691146872754; Fri, 04 Aug 2023 04:01:12 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1691146872; cv=none; d=google.com; s=arc-20160816; b=IESu3phzta20bkaIZeeSxY+vjDzUMTuPDeQozwj8BdMCFshTsCMLmkVuHRrYShs90u uZFVf9csdArWtaN2dYtMZecSAM2mxWRDcN5L1s742lT9y8GCnH3Y1HPwEYySh+gT+JIG 8LNi4HSEn4xo4g4acjFUvpy4LS6Lw4yIXOJJpmeDuitUwaH3DuJWZZCdP9m4BQR2MifW ralXR2b9HDoYOyFWcqN2vHNPUYZ+wI4FTI+FRYtg2UOXnL/+aeFSnYxPsc87wamEdgsR sslk4Qv8owRYKYyZ8DkUOz6m4IYqUx34WwefNshcadZ7Id84lQKWr0I8kPAjr6HWU4II mcRA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:from:subject:message-id:mime-version:date :dkim-signature; bh=mbDuRBO1V21kY8Ln+XMGj8tah/dM0ZngOK9DCSez6Ds=; fh=J68g6MH/W7wOt124Z8Lp4h64rxBVPLWDnNTMQSSfdjs=; b=BFFP/X2FhUUIVxMA6Mbkh0vqxvL+23uJRkpP70GU0Z81FKBz7Fv+hWbFYUmOSR/U5b /4qQXbvQNKs39DcXxuehQRhVNxmBnDtsd3XTz6+KvJH8s7853nDvf51egShWw1T+Lf5v dk8/w67hZF7s11TaqBy7iVT/pmsOj7O9BC2+h4udenldjnsuvxrhCp5EDJk5vGUMLfz3 xn6kdnpLIMUCU23RUqvAC8ggFl859+h+wt66jQCv4LbmdaYtl3z13lScvv2J5KZDPaw8 m1XxQZCTXtlOKXv5OetbW2fq4YqYQI+4EYZzepDpaQAljb6i68YXV20FhMXgQxyIoSpI Ui0Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20221208 header.b=5+NdHYLi; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id o15-20020a170906358f00b0099bd4f74cbesi1496770ejb.615.2023.08.04.04.00.49; Fri, 04 Aug 2023 04:01:12 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20221208 header.b=5+NdHYLi; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229732AbjHDJKI (ORCPT <rfc822;wenzhi022@gmail.com> + 99 others); Fri, 4 Aug 2023 05:10:08 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57890 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229692AbjHDJJh (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Fri, 4 Aug 2023 05:09:37 -0400 Received: from mail-wr1-x449.google.com (mail-wr1-x449.google.com [IPv6:2a00:1450:4864:20::449]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0E4746587 for <linux-kernel@vger.kernel.org>; Fri, 4 Aug 2023 02:06:51 -0700 (PDT) Received: by mail-wr1-x449.google.com with SMTP id ffacd0b85a97d-31797adfe97so1022703f8f.3 for <linux-kernel@vger.kernel.org>; Fri, 04 Aug 2023 02:06:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1691140010; x=1691744810; h=cc:to:from:subject:message-id:mime-version:date:from:to:cc:subject :date:message-id:reply-to; bh=mbDuRBO1V21kY8Ln+XMGj8tah/dM0ZngOK9DCSez6Ds=; b=5+NdHYLigl8F6LR7td6fJwq4mY9o+g31biS6M/x67uOl9aEZNMw4T8nENGruiRPiRB +Gw5NEcsPnW4zysL4GKnQBPK9MO8u0Fsh1G5fWlgkmiENC1pguO3W0zZ0X/9KVUs9XYc Tobr1N4uOqLruYOpHjKAtJJ2mDS90eZzDY6tlw9DthEFZ7ugqCsD5LBgD4MRn1hh6V3W c6lem7rrbgF4amzGiob51SzurdoFT2U3i2F0AvVFAwCHWg57HOfRNXyQL4/kaXiOsTGY Q4BSyO6jSC8eDLVGZOooU1CA1YZfiIB3t/WuaZY5AlYApLPEZnbtXoouz1qw8nB4BnEV 78og== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1691140010; x=1691744810; h=cc:to:from:subject:message-id:mime-version:date:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=mbDuRBO1V21kY8Ln+XMGj8tah/dM0ZngOK9DCSez6Ds=; b=WHFc5rwDiGqUO04zOvfdqfhSPUDeKbIoVEyu9gP8p3Yfw8EOjXW8Bj8LTnFL3gmTEX KmJmyYn0GsGb5TrkXPCDB69fCOf899LwjjxkvjY5wTlkgSVLieFLfA+ENO2wfha+v7Se tBLYZQ32D4r8BfqsfV9VlXxPUCa3mYN6HfBJ3bRAzV0HAusKXEty66XooMGVjfQgXnR5 /CMoaKoY3yJDS/vqUZPIqW9nk7kqEcNO4/EenEB+nsj9k06l7HyW/+3Hz5iCnKnDwkC/ EQxrnQAKw8ZPjyrosdRH7r/4b0uNXnGHeQh4a2JtgXNBJa8h6+bmw1CzuJ+2juiZ6+7K L4Xw== X-Gm-Message-State: AOJu0Yy4HAHGRuYHIeLDftN+CfhZ/RGr4Zz4S6W5rjG1ffyv0q3SLWo8 SRE05Ag2oW7x2pxI6e0zXA+qMLnRZA== X-Received: from elver.muc.corp.google.com ([2a00:79e0:9c:201:2ebf:f3ea:4841:53b6]) (user=elver job=sendgmr) by 2002:adf:f587:0:b0:313:e68e:885d with SMTP id f7-20020adff587000000b00313e68e885dmr6006wro.13.1691140009920; Fri, 04 Aug 2023 02:06:49 -0700 (PDT) Date: Fri, 4 Aug 2023 11:02:56 +0200 Mime-Version: 1.0 X-Mailer: git-send-email 2.41.0.640.ga95def55d0-goog Message-ID: <20230804090621.400-1-elver@google.com> Subject: [PATCH v2 1/3] compiler_types: Introduce the Clang __preserve_most function attribute From: Marco Elver <elver@google.com> To: elver@google.com, Andrew Morton <akpm@linux-foundation.org>, Kees Cook <keescook@chromium.org> Cc: Guenter Roeck <linux@roeck-us.net>, Peter Zijlstra <peterz@infradead.org>, Mark Rutland <mark.rutland@arm.com>, Steven Rostedt <rostedt@goodmis.org>, Marc Zyngier <maz@kernel.org>, Oliver Upton <oliver.upton@linux.dev>, James Morse <james.morse@arm.com>, Suzuki K Poulose <suzuki.poulose@arm.com>, Zenghui Yu <yuzenghui@huawei.com>, Catalin Marinas <catalin.marinas@arm.com>, Will Deacon <will@kernel.org>, Nathan Chancellor <nathan@kernel.org>, Nick Desaulniers <ndesaulniers@google.com>, Tom Rix <trix@redhat.com>, Miguel Ojeda <ojeda@kernel.org>, linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-kernel@vger.kernel.org, llvm@lists.linux.dev, Dmitry Vyukov <dvyukov@google.com>, Alexander Potapenko <glider@google.com>, kasan-dev@googlegroups.com, linux-toolchains@vger.kernel.org Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-9.6 required=5.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1773296022803751579 X-GMAIL-MSGID: 1773296022803751579 |
Series |
[v2,1/3] compiler_types: Introduce the Clang __preserve_most function attribute
|
|
Commit Message
Marco Elver
Aug. 4, 2023, 9:02 a.m. UTC
[1]: "On X86-64 and AArch64 targets, this attribute changes the calling
convention of a function. The preserve_most calling convention attempts
to make the code in the caller as unintrusive as possible. This
convention behaves identically to the C calling convention on how
arguments and return values are passed, but it uses a different set of
caller/callee-saved registers. This alleviates the burden of saving and
recovering a large register set before and after the call in the
caller."
[1] https://clang.llvm.org/docs/AttributeReference.html#preserve-most
Introduce the attribute to compiler_types.h as __preserve_most.
Use of this attribute results in better code generation for calls to
very rarely called functions, such as error-reporting functions, or
rarely executed slow paths.
Beware that the attribute conflicts with instrumentation calls inserted
on function entry which do not use __preserve_most themselves. Notably,
function tracing which assumes the normal C calling convention for the
given architecture. Where the attribute is supported, __preserve_most
will imply notrace. It is recommended to restrict use of the attribute
to functions that should or already disable tracing.
Signed-off-by: Marco Elver <elver@google.com>
---
v2:
* Imply notrace, to avoid any conflicts with tracing which is inserted
on function entry. See added comments.
---
include/linux/compiler_types.h | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
Comments
On Fri, 4 Aug 2023 11:02:56 +0200 Marco Elver <elver@google.com> wrote: > [1]: "On X86-64 and AArch64 targets, this attribute changes the calling > convention of a function. The preserve_most calling convention attempts > to make the code in the caller as unintrusive as possible. This > convention behaves identically to the C calling convention on how > arguments and return values are passed, but it uses a different set of > caller/callee-saved registers. This alleviates the burden of saving and > recovering a large register set before and after the call in the > caller." > > [1] https://clang.llvm.org/docs/AttributeReference.html#preserve-most > > Introduce the attribute to compiler_types.h as __preserve_most. > > Use of this attribute results in better code generation for calls to > very rarely called functions, such as error-reporting functions, or > rarely executed slow paths. > > Beware that the attribute conflicts with instrumentation calls inserted > on function entry which do not use __preserve_most themselves. Notably, > function tracing which assumes the normal C calling convention for the > given architecture. Where the attribute is supported, __preserve_most > will imply notrace. It is recommended to restrict use of the attribute > to functions that should or already disable tracing. > > Signed-off-by: Marco Elver <elver@google.com> > --- > v2: > * Imply notrace, to avoid any conflicts with tracing which is inserted > on function entry. See added comments. Acked-by: Steven Rostedt (Google) <rostedt@goodmis.org> -- Steve > --- > include/linux/compiler_types.h | 27 +++++++++++++++++++++++++++ > 1 file changed, 27 insertions(+) > > diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h > index 547ea1ff806e..12c4540335b7 100644 > --- a/include/linux/compiler_types.h > +++ b/include/linux/compiler_types.h > @@ -106,6 +106,33 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { } > #define __cold > #endif > > +/* > + * On x86-64 and arm64 targets, __preserve_most changes the calling convention > + * of a function to make the code in the caller as unintrusive as possible. This > + * convention behaves identically to the C calling convention on how arguments > + * and return values are passed, but uses a different set of caller- and callee- > + * saved registers. > + * > + * The purpose is to alleviates the burden of saving and recovering a large > + * register set before and after the call in the caller. This is beneficial for > + * rarely taken slow paths, such as error-reporting functions that may be called > + * from hot paths. > + * > + * Note: This may conflict with instrumentation inserted on function entry which > + * does not use __preserve_most or equivalent convention (if in assembly). Since > + * function tracing assumes the normal C calling convention, where the attribute > + * is supported, __preserve_most implies notrace. > + * > + * Optional: not supported by gcc. > + * > + * clang: https://clang.llvm.org/docs/AttributeReference.html#preserve-most > + */ > +#if __has_attribute(__preserve_most__) > +# define __preserve_most notrace __attribute__((__preserve_most__)) > +#else > +# define __preserve_most > +#endif > + > /* Builtins */ > > /*
On Fri, Aug 04, 2023 at 11:02:56AM +0200, Marco Elver wrote: > diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h > index 547ea1ff806e..12c4540335b7 100644 > --- a/include/linux/compiler_types.h > +++ b/include/linux/compiler_types.h > @@ -106,6 +106,33 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { } > #define __cold > #endif > > +/* > + * On x86-64 and arm64 targets, __preserve_most changes the calling convention > + * of a function to make the code in the caller as unintrusive as possible. This > + * convention behaves identically to the C calling convention on how arguments > + * and return values are passed, but uses a different set of caller- and callee- > + * saved registers. > + * > + * The purpose is to alleviates the burden of saving and recovering a large > + * register set before and after the call in the caller. This is beneficial for > + * rarely taken slow paths, such as error-reporting functions that may be called > + * from hot paths. > + * > + * Note: This may conflict with instrumentation inserted on function entry which > + * does not use __preserve_most or equivalent convention (if in assembly). Since > + * function tracing assumes the normal C calling convention, where the attribute > + * is supported, __preserve_most implies notrace. > + * > + * Optional: not supported by gcc. > + * > + * clang: https://clang.llvm.org/docs/AttributeReference.html#preserve-most > + */ > +#if __has_attribute(__preserve_most__) > +# define __preserve_most notrace __attribute__((__preserve_most__)) > +#else > +# define __preserve_most > +#endif This seems to shrink the ARM64 vmlinux just a little and mirrors what we do on x86 in asm. I'll leave it to the arm64 people to judge if this is worth the hassle. Index: linux-2.6/arch/arm64/include/asm/preempt.h =================================================================== --- linux-2.6.orig/arch/arm64/include/asm/preempt.h +++ linux-2.6/arch/arm64/include/asm/preempt.h @@ -88,15 +88,18 @@ void preempt_schedule_notrace(void); #ifdef CONFIG_PREEMPT_DYNAMIC DECLARE_STATIC_KEY_TRUE(sk_dynamic_irqentry_exit_cond_resched); -void dynamic_preempt_schedule(void); +void __preserve_most dynamic_preempt_schedule(void); #define __preempt_schedule() dynamic_preempt_schedule() -void dynamic_preempt_schedule_notrace(void); +void __preserve_most dynamic_preempt_schedule_notrace(void); #define __preempt_schedule_notrace() dynamic_preempt_schedule_notrace() #else /* CONFIG_PREEMPT_DYNAMIC */ -#define __preempt_schedule() preempt_schedule() -#define __preempt_schedule_notrace() preempt_schedule_notrace() +void __preserve_most preserve_preempt_schedule(void); +void __preserve_most preserve_preempt_schedule_notrace(void); + +#define __preempt_schedule() preserve_preempt_schedule() +#define __preempt_schedule_notrace() preserve_preempt_schedule_notrace() #endif /* CONFIG_PREEMPT_DYNAMIC */ #endif /* CONFIG_PREEMPTION */ Index: linux-2.6/kernel/sched/core.c =================================================================== --- linux-2.6.orig/kernel/sched/core.c +++ linux-2.6/kernel/sched/core.c @@ -6915,7 +6915,7 @@ DEFINE_STATIC_CALL(preempt_schedule, pre EXPORT_STATIC_CALL_TRAMP(preempt_schedule); #elif defined(CONFIG_HAVE_PREEMPT_DYNAMIC_KEY) static DEFINE_STATIC_KEY_TRUE(sk_dynamic_preempt_schedule); -void __sched notrace dynamic_preempt_schedule(void) +void __sched __preserve_most dynamic_preempt_schedule(void) { if (!static_branch_unlikely(&sk_dynamic_preempt_schedule)) return; @@ -6924,6 +6924,11 @@ void __sched notrace dynamic_preempt_sch NOKPROBE_SYMBOL(dynamic_preempt_schedule); EXPORT_SYMBOL(dynamic_preempt_schedule); #endif +#else +void __sched __preserve_most preserve_preempt_schedule(void) +{ + preempt_schedule(); +} #endif /** @@ -6988,7 +6993,7 @@ DEFINE_STATIC_CALL(preempt_schedule_notr EXPORT_STATIC_CALL_TRAMP(preempt_schedule_notrace); #elif defined(CONFIG_HAVE_PREEMPT_DYNAMIC_KEY) static DEFINE_STATIC_KEY_TRUE(sk_dynamic_preempt_schedule_notrace); -void __sched notrace dynamic_preempt_schedule_notrace(void) +void __sched __preserve_most dynamic_preempt_schedule_notrace(void) { if (!static_branch_unlikely(&sk_dynamic_preempt_schedule_notrace)) return; @@ -6997,6 +7002,11 @@ void __sched notrace dynamic_preempt_sch NOKPROBE_SYMBOL(dynamic_preempt_schedule_notrace); EXPORT_SYMBOL(dynamic_preempt_schedule_notrace); #endif +#else +void __sched __preserve_most preserve_preempt_schedule_notrace(void) +{ + preempt_schedule_notrace(); +} #endif #endif /* CONFIG_PREEMPTION */
On Fri, Aug 4, 2023 at 11:06 AM Marco Elver <elver@google.com> wrote: > > will imply notrace. It is recommended to restrict use of the attribute > to functions that should or already disable tracing. Should the last sentence here be added into the code comment? Apart from that this looks fine from a `compiler_attributes.h` perspective (even if we are not there anymore): Reviewed-by: Miguel Ojeda <ojeda@kernel.org> Cheers, Miguel
* Marco Elver: > [1]: "On X86-64 and AArch64 targets, this attribute changes the calling > convention of a function. The preserve_most calling convention attempts > to make the code in the caller as unintrusive as possible. This > convention behaves identically to the C calling convention on how > arguments and return values are passed, but it uses a different set of > caller/callee-saved registers. This alleviates the burden of saving and > recovering a large register set before and after the call in the > caller." > > [1] https://clang.llvm.org/docs/AttributeReference.html#preserve-most You dropped the interesting part: | If the arguments are passed in callee-saved registers, then they will | be preserved by the callee across the call. This doesn’t apply for | values returned in callee-saved registers. | | · On X86-64 the callee preserves all general purpose registers, except | for R11. R11 can be used as a scratch register. Floating-point | registers (XMMs/YMMs) are not preserved and need to be saved by the | caller. | | · On AArch64 the callee preserve all general purpose registers, except | X0-X8 and X16-X18. Ideally, this would be documented in the respective psABI supplement. I filled in some gaps and filed: Document the ABI for __preserve_most__ function calls <https://gitlab.com/x86-psABIs/x86-64-ABI/-/merge_requests/45> Doesn't this change impact the kernel module ABI? I would really expect a check here > +#if __has_attribute(__preserve_most__) > +# define __preserve_most notrace __attribute__((__preserve_most__)) > +#else > +# define __preserve_most > +#endif that this is not a compilation for a module. Otherwise modules built with a compiler with __preserve_most__ attribute support are incompatible with kernels built with a compiler without that attribute. Thanks, Florian
On Mon, 7 Aug 2023 at 13:41, Florian Weimer <fweimer@redhat.com> wrote: > > * Marco Elver: > > > [1]: "On X86-64 and AArch64 targets, this attribute changes the calling > > convention of a function. The preserve_most calling convention attempts > > to make the code in the caller as unintrusive as possible. This > > convention behaves identically to the C calling convention on how > > arguments and return values are passed, but it uses a different set of > > caller/callee-saved registers. This alleviates the burden of saving and > > recovering a large register set before and after the call in the > > caller." > > > > [1] https://clang.llvm.org/docs/AttributeReference.html#preserve-most > > You dropped the interesting part: I will add it back for the kernel documentation. > | If the arguments are passed in callee-saved registers, then they will > | be preserved by the callee across the call. This doesn’t apply for > | values returned in callee-saved registers. > | > | · On X86-64 the callee preserves all general purpose registers, except > | for R11. R11 can be used as a scratch register. Floating-point > | registers (XMMs/YMMs) are not preserved and need to be saved by the > | caller. > | > | · On AArch64 the callee preserve all general purpose registers, except > | X0-X8 and X16-X18. > > Ideally, this would be documented in the respective psABI supplement. > I filled in some gaps and filed: > > Document the ABI for __preserve_most__ function calls > <https://gitlab.com/x86-psABIs/x86-64-ABI/-/merge_requests/45> Good idea. I had already created https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110899, and we need better spec to proceed for GCC anyway. > Doesn't this change impact the kernel module ABI? > > I would really expect a check here > > > +#if __has_attribute(__preserve_most__) > > +# define __preserve_most notrace __attribute__((__preserve_most__)) > > +#else > > +# define __preserve_most > > +#endif > > that this is not a compilation for a module. Otherwise modules built > with a compiler with __preserve_most__ attribute support are > incompatible with kernels built with a compiler without that attribute. That's true, but is it a real problem? Isn't it known that trying to make kernel modules built for a kernel with a different config (incl. compiler) is not guaranteed to work? See IBT, CFI schemes, kernel sanitizers, etc? If we were to start trying to introduce some kind of minimal kernel to module ABI so that modules and kernels built with different toolchains keep working together, we'd need a mechanism to guarantee this minimal ABI or prohibit incompatible modules and kernels somehow. Is there a precedence for this somewhere?
On Mon, Aug 07, 2023 at 01:41:07PM +0200, Florian Weimer wrote: > * Marco Elver: > > > [1]: "On X86-64 and AArch64 targets, this attribute changes the calling > > convention of a function. The preserve_most calling convention attempts > > to make the code in the caller as unintrusive as possible. This > > convention behaves identically to the C calling convention on how > > arguments and return values are passed, but it uses a different set of > > caller/callee-saved registers. This alleviates the burden of saving and > > recovering a large register set before and after the call in the > > caller." > > > > [1] https://clang.llvm.org/docs/AttributeReference.html#preserve-most > > You dropped the interesting part: > > | If the arguments are passed in callee-saved registers, then they will > | be preserved by the callee across the call. This doesn’t apply for > | values returned in callee-saved registers. > | > | · On X86-64 the callee preserves all general purpose registers, except > | for R11. R11 can be used as a scratch register. Floating-point > | registers (XMMs/YMMs) are not preserved and need to be saved by the > | caller. > | > | · On AArch64 the callee preserve all general purpose registers, except > | X0-X8 and X16-X18. > > Ideally, this would be documented in the respective psABI supplement. > I filled in some gaps and filed: > > Document the ABI for __preserve_most__ function calls > <https://gitlab.com/x86-psABIs/x86-64-ABI/-/merge_requests/45> > > Doesn't this change impact the kernel module ABI? > > I would really expect a check here > > > +#if __has_attribute(__preserve_most__) > > +# define __preserve_most notrace __attribute__((__preserve_most__)) > > +#else > > +# define __preserve_most > > +#endif > > that this is not a compilation for a module. Otherwise modules built > with a compiler with __preserve_most__ attribute support are > incompatible with kernels built with a compiler without that attribute. We have a metric ton of options that can break module ABI. If you're daft enough to not build with the exact same compiler and .config you get to keep the pieces.
* Marco Elver: > Good idea. I had already created > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110899, and we need > better spec to proceed for GCC anyway. Thanks for the reference. >> Doesn't this change impact the kernel module ABI? >> >> I would really expect a check here >> >> > +#if __has_attribute(__preserve_most__) >> > +# define __preserve_most notrace __attribute__((__preserve_most__)) >> > +#else >> > +# define __preserve_most >> > +#endif >> >> that this is not a compilation for a module. Otherwise modules built >> with a compiler with __preserve_most__ attribute support are >> incompatible with kernels built with a compiler without that attribute. > > That's true, but is it a real problem? Isn't it known that trying to > make kernel modules built for a kernel with a different config (incl. > compiler) is not guaranteed to work? See IBT, CFI schemes, kernel > sanitizers, etc? > > If we were to start trying to introduce some kind of minimal kernel to > module ABI so that modules and kernels built with different toolchains > keep working together, we'd need a mechanism to guarantee this minimal > ABI or prohibit incompatible modules and kernels somehow. Is there a > precedence for this somewhere? I think the GCC vs Clang thing is expected to work today, isn't it? Using the Clang-based BPF tools with a GCC-compiled kernel requires a matching ABI. The other things you listed result in fairly obvious breakage, sometimes even module loading failures. Unconditional crashes are possible as well. With __preserve_most__, the issues are much more subtle and may only appear for some kernel/module compielr combinations and optimization settings. The impact of incorrectly clobbered registers tends to be like that. Thanks, Florian
On Mon, Aug 07, 2023 at 02:24:26PM +0200, Marco Elver wrote: > > | If the arguments are passed in callee-saved registers, then they will > > | be preserved by the callee across the call. This doesn’t apply for > > | values returned in callee-saved registers. > > | > > | · On X86-64 the callee preserves all general purpose registers, except > > | for R11. R11 can be used as a scratch register. Floating-point > > | registers (XMMs/YMMs) are not preserved and need to be saved by the > > | caller. > > | > > | · On AArch64 the callee preserve all general purpose registers, except > > | X0-X8 and X16-X18. > > > > Ideally, this would be documented in the respective psABI supplement. > > I filled in some gaps and filed: > > > > Document the ABI for __preserve_most__ function calls > > <https://gitlab.com/x86-psABIs/x86-64-ABI/-/merge_requests/45> > > Good idea. I had already created > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110899, and we need > better spec to proceed for GCC anyway. "Registers used for passing arguments are preserved by the called function, but registers used for returning results are not." You mean just GPRs or also vector SSE or MMX registers? Because if some of those are to be preserved by callee, there is an issue that they need to be e.g. handled during unwinding, with all the consequences. It is hard to impossible to guess what size needs to be saved/restored, both normally or during unwinding. As caller could be say -mavx512f and expect preservation of all 512 bits and callee -msse2 or -mavx{,2}, or caller -mavx{,2} and expect preservation of all 256 bits and callee -msse2 etc. MSABI "solves" that by making just the low 128 bits preserved and upper bits clobbered. Jakub
* Jakub Jelinek: > On Mon, Aug 07, 2023 at 02:24:26PM +0200, Marco Elver wrote: >> > | If the arguments are passed in callee-saved registers, then they will >> > | be preserved by the callee across the call. This doesn’t apply for >> > | values returned in callee-saved registers. >> > | >> > | · On X86-64 the callee preserves all general purpose registers, except >> > | for R11. R11 can be used as a scratch register. Floating-point >> > | registers (XMMs/YMMs) are not preserved and need to be saved by the >> > | caller. >> > | >> > | · On AArch64 the callee preserve all general purpose registers, except >> > | X0-X8 and X16-X18. >> > >> > Ideally, this would be documented in the respective psABI supplement. >> > I filled in some gaps and filed: >> > >> > Document the ABI for __preserve_most__ function calls >> > <https://gitlab.com/x86-psABIs/x86-64-ABI/-/merge_requests/45> >> >> Good idea. I had already created >> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110899, and we need >> better spec to proceed for GCC anyway. > > "Registers used for passing arguments > are preserved by the called function, but registers used for > returning results are not." > > You mean just GPRs or also vector SSE or MMX registers? I think this is pretty clear for x86-64: | Floating-point registers (XMMs/YMMs) are not preserved and need to be | saved by the caller. The issue is more with future GPR extensions like APX. Thanks, Florian
On Mon, Aug 07, 2023 at 02:43:39PM +0200, Florian Weimer wrote: > > On Mon, Aug 07, 2023 at 02:24:26PM +0200, Marco Elver wrote: > >> > | If the arguments are passed in callee-saved registers, then they will > >> > | be preserved by the callee across the call. This doesn’t apply for > >> > | values returned in callee-saved registers. > >> > | > >> > | · On X86-64 the callee preserves all general purpose registers, except > >> > | for R11. R11 can be used as a scratch register. Floating-point > >> > | registers (XMMs/YMMs) are not preserved and need to be saved by the > >> > | caller. > >> > | > >> > | · On AArch64 the callee preserve all general purpose registers, except > >> > | X0-X8 and X16-X18. > >> > > >> > Ideally, this would be documented in the respective psABI supplement. > >> > I filled in some gaps and filed: > >> > > >> > Document the ABI for __preserve_most__ function calls > >> > <https://gitlab.com/x86-psABIs/x86-64-ABI/-/merge_requests/45> > >> > >> Good idea. I had already created > >> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110899, and we need > >> better spec to proceed for GCC anyway. > > > > "Registers used for passing arguments > > are preserved by the called function, but registers used for > > returning results are not." > > > > You mean just GPRs or also vector SSE or MMX registers? > > I think this is pretty clear for x86-64: > > | Floating-point registers (XMMs/YMMs) are not preserved and need to be > | saved by the caller. The above wording conflicts with that, so it should be clarified. Jakub
On Mon, 7 Aug 2023 at 14:37, Florian Weimer <fweimer@redhat.com> wrote: > > * Marco Elver: > > > Good idea. I had already created > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110899, and we need > > better spec to proceed for GCC anyway. > > Thanks for the reference. > > >> Doesn't this change impact the kernel module ABI? > >> > >> I would really expect a check here > >> > >> > +#if __has_attribute(__preserve_most__) > >> > +# define __preserve_most notrace __attribute__((__preserve_most__)) > >> > +#else > >> > +# define __preserve_most > >> > +#endif > >> > >> that this is not a compilation for a module. Otherwise modules built > >> with a compiler with __preserve_most__ attribute support are > >> incompatible with kernels built with a compiler without that attribute. > > > > That's true, but is it a real problem? Isn't it known that trying to > > make kernel modules built for a kernel with a different config (incl. > > compiler) is not guaranteed to work? See IBT, CFI schemes, kernel > > sanitizers, etc? > > > > If we were to start trying to introduce some kind of minimal kernel to > > module ABI so that modules and kernels built with different toolchains > > keep working together, we'd need a mechanism to guarantee this minimal > > ABI or prohibit incompatible modules and kernels somehow. Is there a > > precedence for this somewhere? > > I think the GCC vs Clang thing is expected to work today, isn't it? I, personally, wouldn't bet on it. It very much depends on the kernel config used. > Using the Clang-based BPF tools with a GCC-compiled kernel requires a > matching ABI. BPF is a different story altogether, and falls more into the category of user space to kernel ABI, which the kernel has strong guarantees on. > The other things you listed result in fairly obvious breakage, sometimes > even module loading failures. Unconditional crashes are possible as > well. With __preserve_most__, the issues are much more subtle and may > only appear for some kernel/module compielr combinations and > optimization settings. The impact of incorrectly clobbered registers > tends to be like that. One way around this would be to make the availability of the attribute a Kconfig variable. Then externally compiled kernel modules should do the right thing, since they ought to use the right .config when being built. I can make that change for v3.
On Mon, Aug 07, 2023 at 02:36:53PM +0200, Florian Weimer wrote: > I think the GCC vs Clang thing is expected to work today, isn't it? > Using the Clang-based BPF tools with a GCC-compiled kernel requires a > matching ABI. Nope, all bets are off. There is no module ABI, in the widest possible sense. There's all sorts of subtle breakage, I don't remember the exact details, but IIRC building the kernel with a compiler that has asm-goto-output and modules with a compiler that doesn't have it gets you fireworks. We absolutely do even bother tracking any of this. There's also things like GCC plugins, they can randomly (literally in the case of struct randomization) change things around that isn't compatible with what the other compiler does -- or perhaps even a later version of the same compiler.
On Mon, Aug 7, 2023 at 4:41 AM Florian Weimer <fweimer@redhat.com> wrote: > > * Marco Elver: > > > [1]: "On X86-64 and AArch64 targets, this attribute changes the calling > > convention of a function. The preserve_most calling convention attempts > > to make the code in the caller as unintrusive as possible. This > > convention behaves identically to the C calling convention on how > > arguments and return values are passed, but it uses a different set of > > caller/callee-saved registers. This alleviates the burden of saving and > > recovering a large register set before and after the call in the > > caller." > > > > [1] https://clang.llvm.org/docs/AttributeReference.html#preserve-most > > You dropped the interesting part: > > | If the arguments are passed in callee-saved registers, then they will > | be preserved by the callee across the call. This doesn’t apply for > | values returned in callee-saved registers. > | > | · On X86-64 the callee preserves all general purpose registers, except > | for R11. R11 can be used as a scratch register. Floating-point > | registers (XMMs/YMMs) are not preserved and need to be saved by the > | caller. > | > | · On AArch64 the callee preserve all general purpose registers, except > | X0-X8 and X16-X18. > > Ideally, this would be documented in the respective psABI supplement. > I filled in some gaps and filed: > > Document the ABI for __preserve_most__ function calls > <https://gitlab.com/x86-psABIs/x86-64-ABI/-/merge_requests/45> > > Doesn't this change impact the kernel module ABI? > > I would really expect a check here > > > +#if __has_attribute(__preserve_most__) > > +# define __preserve_most notrace __attribute__((__preserve_most__)) > > +#else > > +# define __preserve_most > > +#endif > > that this is not a compilation for a module. Otherwise modules built > with a compiler with __preserve_most__ attribute support are > incompatible with kernels built with a compiler without that attribute. Surely the Linux kernel has a stable ABI for modules right? Nah. https://www.kernel.org/doc/Documentation/process/stable-api-nonsense.rst > > Thanks, > Florian >
On Mon, 7 Aug 2023 14:31:37 +0200 Peter Zijlstra <peterz@infradead.org> wrote: > > that this is not a compilation for a module. Otherwise modules built > > with a compiler with __preserve_most__ attribute support are > > incompatible with kernels built with a compiler without that attribute. > > We have a metric ton of options that can break module ABI. If you're > daft enough to not build with the exact same compiler and .config you > get to keep the pieces. I believe there's enough checks for various compiler options in order to enable features during the build that trying to load a module built with another compiler is pretty much guaranteed to fail today. -- Steve
On Mon, Aug 07, 2023 at 01:41:07PM +0200, Florian Weimer wrote: > * Marco Elver: > > > [1]: "On X86-64 and AArch64 targets, this attribute changes the calling > > convention of a function. The preserve_most calling convention attempts > > to make the code in the caller as unintrusive as possible. This > > convention behaves identically to the C calling convention on how > > arguments and return values are passed, but it uses a different set of > > caller/callee-saved registers. This alleviates the burden of saving and > > recovering a large register set before and after the call in the > > caller." > > > > [1] https://clang.llvm.org/docs/AttributeReference.html#preserve-most > > You dropped the interesting part: > > | If the arguments are passed in callee-saved registers, then they will > | be preserved by the callee across the call. This doesn’t apply for > | values returned in callee-saved registers. > | > | · On X86-64 the callee preserves all general purpose registers, except > | for R11. R11 can be used as a scratch register. Floating-point > | registers (XMMs/YMMs) are not preserved and need to be saved by the > | caller. > | > | · On AArch64 the callee preserve all general purpose registers, except > | X0-X8 and X16-X18. > > Ideally, this would be documented in the respective psABI supplement. > I filled in some gaps and filed: > > Document the ABI for __preserve_most__ function calls > <https://gitlab.com/x86-psABIs/x86-64-ABI/-/merge_requests/45> > > Doesn't this change impact the kernel module ABI? > > I would really expect a check here So in the GCC bugzilla you raise the point of unwinding. So in arch/x86 we've beeing doing what this attribute proposes (except without that weird r11 exception) for a long while. We simply do: asm("call foo_thunk"); instead of a C call, and then have the 'thunk' thing PUSH/POP all the registers around a regular C function. Paravirt does quite a lot of that as well. In a very few cases we implement a function specifically to avoid all the PUSH/POP nonsense because it's so small the stack overhead kills it. For unwinding this means that the 'thunk' becomes invisible when IP is not inside it. But since the thunk is purely 'uninteresting' PUSH/POP around a real C call, this is not an issue. [[ tail calls leaving their sibling invisible is a far more annoying issue ]] If the function is one of those special things, then it will be a leaf function and we get to see it throught he IP. Now, the problem with __preserve_most is that it makes it really easy to deviate from this pattern, you can trivially write a function that is not a trivial wrapper and then does not show up on unwind. This might indeed be a problem. Ofc. the kernel doesn't longjmp much, and we also don't throw many exxceptions, but the live-patch people might care (although ORC unwinder should be able to deal with all this perfectly fine). In userspace, would not .eh_frame still be able to unwind this?
* Peter Zijlstra: > Now, the problem with __preserve_most is that it makes it really easy to > deviate from this pattern, you can trivially write a function that is > not a trivial wrapper and then does not show up on unwind. This might > indeed be a problem. Backtrace generation shouldn't be impacted by a compiler implementation of __preserve_most__. If unwinding implies restoring register contents, the question becomes whether the unwinder can be taught to do this natively. For .eh_frame/PT_GNU_EH_FRAME-based unwinders and __preserve_most__, I think that's true because they already support custom ABIs (and GCC uses them for local functions). In other cases, if the unwinder does not support the extra registers, then it might still be possible to compensate for that via code generation (e.g., setjmp won't be __preserve_most__, so the compiler would have to preserve register contents by other means, also accounting for the returns-twice nature, likewise for exception handling landing pads). But __preserve_all__ is a completely different beast. I *think* it is possible to do this with helpers (state size, state save, state restore) and strategically placed restores after returns-twice functions and the like, but people disagree. This has come up before in the context of the s390x vector ABI and the desire to add new callee-saved registers. We just couldn't make that work at the time. On the other hand, __preserve_all__ goes into the other direction (opt-in of extra saves), so it may be conceptually easier. Thanks, Florian
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index 547ea1ff806e..12c4540335b7 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -106,6 +106,33 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { } #define __cold #endif +/* + * On x86-64 and arm64 targets, __preserve_most changes the calling convention + * of a function to make the code in the caller as unintrusive as possible. This + * convention behaves identically to the C calling convention on how arguments + * and return values are passed, but uses a different set of caller- and callee- + * saved registers. + * + * The purpose is to alleviates the burden of saving and recovering a large + * register set before and after the call in the caller. This is beneficial for + * rarely taken slow paths, such as error-reporting functions that may be called + * from hot paths. + * + * Note: This may conflict with instrumentation inserted on function entry which + * does not use __preserve_most or equivalent convention (if in assembly). Since + * function tracing assumes the normal C calling convention, where the attribute + * is supported, __preserve_most implies notrace. + * + * Optional: not supported by gcc. + * + * clang: https://clang.llvm.org/docs/AttributeReference.html#preserve-most + */ +#if __has_attribute(__preserve_most__) +# define __preserve_most notrace __attribute__((__preserve_most__)) +#else +# define __preserve_most +#endif + /* Builtins */ /*