-
Notifications
You must be signed in to change notification settings - Fork 13.7k
Stabilize -Cmin-function-alignment
#142824
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Stabilize -Cmin-function-alignment
#142824
Conversation
Some changes occurred to the CTFE machinery
Some changes occurred to the CTFE / Miri interpreter
cc @rust-lang/miri
Some changes occurred in compiler/rustc_codegen_cranelift
cc @bjorn3
Some changes occurred in compiler/rustc_codegen_ssa
The Miri subtree was changed
cc @rust-lang/miri
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should it be limited on all platforms? Can't we error when the alignment exceeds the maximum that the actual target we are compiling for supports? Maybe someone genuinely needs to align to 16k on ELF for whatever reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, so as we've discovered, alignment is just a mess between clang and llvm. For instance -falign-function
only goes up to 1 << 16
, although you can manually align a function to a much higher alignment.
For 8192, we know it'll work everywhere, and the logic for when to accept/reject an alignment value is clear.
This flag aligns all functions to the minimum, so I have a hard time seeing a realistic scenario where aligning all functions to 16k is a reasonable thing to do (the limit on individual functions will be handled separately).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I think the options are:
- Limit to
8192
on all platforms, it's consistent and unlikely to cause issues. The limit can be safely raised in the future if a need arises - Follow
clang
and allow1 << 16
, except when the target object format is COFF, then the limit is8192
. - Accept what llvm accepts: allow
1 << 29
, except when the target object format is COFF, then the limit is8192
.
I've picked the most conservative one (again, with the option to relax the limits if the need ever arises), but if there is consensus now on one of the other options that's also fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is my understanding that on Linux kernels we still nominally support, overaligning beyond the page size, typically 4096, is not going to work properly, so it is arguable that even 8192 is too high.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO for the long term, hard-coding to 8192
across all platforms seem like a mistake -- especially because then, does this become part of the language or even more problematic, become part of (compiler/language) stability guarantees?
EDIT: or rather, what is a suitable upper bound in this case in terms of stability guarantees? As in, if we stabilize n=8192
as a universal upper bound, if we find out some platform cannot handle that in the future?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(orthogonal to this PR)
it is not great that the logic for merging the per-fn alignment and the global alignment needs to be repeated in each backend.
Maybe codegen_fn_attrs
should just take min_function_alignment
into account?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact, using sess.opts
in the interpreter is at best iffy since it means the interpreter can behave differently in different crates in the same crate graph which can cause unsoundness...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that function pointers can be odd even when -Cmin-function-alignment
is 2 (see here), I don't think it makes sense for the interpreter to do anything with min_function_alignment
.
@rustbot labels +T-lang
If the idea with this is that passing -Cmin-function-alignment
represents a language-level guarantee, e.g. that a naked function could validly rely on this minimum alignment holding when this flag is passed to prove the soundness of its operations -- that from a language spec standpoint, this is equivalent to marking each function with #[align(..)]
-- then I'd suggest that a dual FCP with lang here is proper.
cc @rust-lang/lang
@rust-lang/spec: What thoughts do we have about how to handle compiler flags that affect the language definition?
cc @RalfJung
Here's a question. Since this does have language-level effect, might this be better expressed as a crate-level attribute?
If there's a good reason, given the use case, for this to be a compiler flag instead, probably it'd be good to describe that in some detail.
If it can then be applied via --crate-attr
similarly, then that should be fine for our use case.
...-alignment, r=workingjubilee centralize `-Zmin-function-alignment` logic tracking issue: rust-lang#82232 discussed in: rust-lang#142824 (comment) Apply the `-Zmin-function-alignment` value to the alignment field of the function attributes when those are created, so that individual backends don't need to consider it. The one exception right now is cranelift, because it can't yet set the alignment for individual functions, but it can (and does) set the global minimum function alignment. cc `@RalfJung` I think this is an improvement regardless, is there anything else that should be done for miri?
...-alignment, r=workingjubilee centralize `-Zmin-function-alignment` logic tracking issue: rust-lang#82232 discussed in: rust-lang#142824 (comment) Apply the `-Zmin-function-alignment` value to the alignment field of the function attributes when those are created, so that individual backends don't need to consider it. The one exception right now is cranelift, because it can't yet set the alignment for individual functions, but it can (and does) set the global minimum function alignment. cc ``@RalfJung`` I think this is an improvement regardless, is there anything else that should be done for miri?
Huh. That could be... interesting to work with, since it would logically apply only to entities that originate from that crate, right?
@folkertdev Do any of our tests verify that if you
- use this option on the functions described in a crate
- one is generic or from a trait or otherwise non-instantiated
- you instantiate that function in another crate without this flag
Then the resulting function is instantiated with the correct alignment? Where "correct" is... well, pick an answer, I guess?
Rollup merge of #142854 - folkertdev:centralize-min-function-alignment, r=workingjubilee centralize `-Zmin-function-alignment` logic tracking issue: #82232 discussed in: #142824 (comment) Apply the `-Zmin-function-alignment` value to the alignment field of the function attributes when those are created, so that individual backends don't need to consider it. The one exception right now is cranelift, because it can't yet set the alignment for individual functions, but it can (and does) set the global minimum function alignment. cc ``@RalfJung`` I think this is an improvement regardless, is there anything else that should be done for miri?
☔ The latest upstream changes (presumably #142901) made this pull request unmergeable. Please resolve the merge conflicts.
@workingjubilee we don't, currently. I think the expected answer is clear though, because -Zmin-function-alignment
is applied to all compiled crates, not just the top-level one?
I'm not sure how that would work with #![min_function_alignment = N]
, because then library crates could set the value as well? So far I've been thinking of this option as similar to -Ctarget-cpu
or -Ctarget-feature
: they are not properties of a crate but of the compilation as a whole.
What thoughts do we have about how to handle compiler flags that affect the language definition?
Is setting and then relying on the alignment fundamentally different from -Ctarget-feature=+avx2
and then asserting in the program that avx2
is available?
Is setting and then relying on the alignment fundamentally different from
-Ctarget-feature=+avx2
and then asserting in the program thatavx2
is available?
It's about whether we like this as a safety argument:
/// SAFETY: The pointee must be a 32-byte aligned function. It's OK /// for the low-order bits of the function pointer itself to be /// non-zero, e.g., if they're used for controlling the processor /// mode. unsafe fn f(x: fn()) { todo!() } fn g() { let p = g as fn(); // SAFETY: We compiled with `-Cmin-function-alignment=32`. unsafe { f(p) }; }
For avx2
, I'd expect that the safety argument would be made by use of cfg(target_feature = "..")
, if is_x86_feature_detected!("avx2") { .. }
, etc., rather than by using the argument that it was compiled with -Ctarget-feature=+avx2
in the proof.
they are not properties of a crate but of the compilation as a whole.
Wait so, does this need to be handled like Target Modifiers?
Higher alignments are not supported in COFF
Yes, unexpected, but here we are. There are a bunch of threads here, and I guess we don't have a great way of reaching consensus, so I'm trying to figure out what to do next.
Alignment bounds
I'm assuming the question of what to do with the alignment bounds will be handled by T-lang in RFC 3806. Currently it states that
The maximum alignment is the same as
#[repr(align(...))]
: 229. That
being said, some targets may not be able to support very high alignments in all
contexts. In such cases, the compiler must impose a lower limit for those
specific contexts on those specific targets. The compiler may choose to emit a
lint warning for high, non-portable alignment specifiers.
which I think in practice means
- for PE/COFF, 8192 is the hard limit
- for elf, 4096 is the soft limit, and we warn on anything higher while rust still supports kernel versions that use that upper bound (rust supports kernel version v3.2, the issue is fixed in v5.10, see Statics don't support alignments larger than the page size #70022 (comment) ) (we could error too, but there has been resistance to that in this thread; I'm also equating elf and linux, I'm assuming e.g. the BSDs behave like linux here, but have no way of verifying that)
- otherwise, we defer to the max LLVM value 229
Language guarantee
I believe that what RfL wants is a practical way to set the minimum alignment across the compilation. As far as I can tell now there is no need for a language guarantee on the alignment value (i.e. the alignment is not used in safety comments). That -Cmin-function-align
is implemented as a #[align]
on each function can remain an implementation detail, and we'll need to be careful to not claim that they are equivalent.
Target modifier
I've been trying to tease out what a target modifier is and what it is not. It looks like there is support for broadening the definition of a target modifier to something like
A target modifier is a flag where it is invalid if you link together two compilation units that disagree on the flag.
Where invalid includes, but is not limited to, the result being unsound.
Making -Cmin-function-align
a target modifier sidesteps a bunch of questions around how crates interact, and practically speaking, both for the RfL tooling and performance, having the alignment value be consistent across the crate graph is probably the desired behavior anyway.
Unless someone speaks up about this I'll make the change in a couple of days.
Do we have precedent for such a "weak" target modifier? I know @Darksonn wanted to use target modifiers for some cases where it is not soundness-critical but otherwise still potentially bad to accidentally mix things, like sanitizer settings. However, I don't know if this alignment question falls into that scope.
Specifically, it contains a section that attempts to define what alignment values are accepted.
#[align]
and-Cmin-function-alignment
should accept/reject the same values.
FWIW, as the author of that RFC, I think starting off with a blanket limit of 8192 for this flag is fine for an MVP of this flag. As others have said, it seems really unrealistic that a user would want to align all functions to a higher value. The limit should be changed to match the RFC once it is implemented, but that shouldn’t block stabilization of the flag.
Making
-Cmin-function-align
a target modifier sidesteps a bunch of questions around how crates interact, and practically speaking, both for the RfL tooling and performance, having the alignment value be consistent across the crate graph is probably the desired behavior anyway.
That and your plan on this sounds reasonable to me.
One note on this part though:
Language guarantee... I believe that what RfL wants is a practical way to set the minimum alignment across the compilation. As far as I can tell now there is no need for a language guarantee on the alignment value (i.e. the alignment is not used in safety comments).
While they're maybe not writing safety comments, I nonetheless read the original ask as for a guarantee:
Does rustc allow one to specify the alignment of a fuction starting address like
-fmin-function-alignment
or-falign-functions
?Asked because in Linux kernel, to enable dynamic function trace, on ARM64 the function addresses are required to be 8-byte aligned.
At least, it's not obvious to me how ftrace
could be used here soundly without such a guarantee.
I asked for some clarification at #t-compiler/help > ✔ Alignment for function addresses @ 💬, and the RfL developer confirmed that, as far as they are aware, no UB can result from misapplying the alignment:
if the function address is not aligned, function trace point will just ignore this function.
Hence, at the moment, I'm not aware of any concrete need for a language requirement. We can still make it one though, and at least I believe that we're keeping the door open to making it a language guarantee in the future.
if the function address is not aligned, function trace point will just ignore this function.
Ah, good to know, thanks for that.
We can still make it one though, and at least I believe that we're keeping the door open to making it a language guarantee in the future.
Yes, and making this a target modifier, as you're planning, probably makes this more palatable in any case.
Hm reading this again, I think we'll want to be careful/explicit in at least two aspects:
I believe that what RfL wants is a practical way to set the minimum alignment across the compilation. As far as I can tell now there is no need for a language guarantee on the alignment value (i.e. the alignment is not used in safety comments).
(The first point is already mentioned earlier, but summarizing again)
- Clarify whether this flag is intended as a guarantee for a whole compilation graph, or merely a hint. In relation to this, can this flag be relied upon for (transitive) soundness requirements? I'm assuming that it cannot (AFAICT, we haven't fully determined this), in which case we should probably explicitly state that, because it forms part of the usage contract of this flag between the compiler and the user.
- What stability guarantees do we have or not have for the "interface" of the flag and the behavior of the flag?
- "Interface" stability guarantees: from a compiler stability perspective, if we hard error on say max alignment
8192
now, can we or can we not lower that upper bound if we find out new information (per target/platform or for other reasons) that indicate that we should've imposed a lower limit for $target/$platform?
- "Interface" stability guarantees: from a compiler stability perspective, if we hard error on say max alignment
We can still make it one though, and at least I believe that we're keeping the door open to making it a language guarantee in the future.
This might not necessarily be fully open. From a language perspective -- perhaps. But from an implementation (compiler/toolchain) perspective, this is not necessarily so. As mentioned above, precompiled standard library could potentially be of a different min alignment. So if we want to upgrade this to be a guaranteed min alignment across the whole compilation graph, that might be a spanner in the works.
I suppose in theory if -Zbuild-std
becomes stabilized this might be less of an issue.
I also have another question: would -Cmin-function-alignment
(whether hint form, or in some theoretical guarantee form) apply to extern functions? I guess why I'm asking is more for the "if this is intended to become a language guarantee", that can code also assume extern functions follow this min alignment?
(This is a less of a concern, but more of a question about what is the expectation on interactions.)
@rustbot concern interaction-with-extern-functions
Actually, let my try to use rustbot's concern functionality to make the existing discussions/concerns more explicit.
@rustbot concern per-target-vs-universal-max-alignment
Why should it be limited on all platforms? Can't we error when the alignment exceeds the maximum that the actual target we are compiling for supports? Maybe someone genuinely needs to align to 16k on ELF for whatever reason?
@rustbot concern hint-vs-language-guarantee-can-be-relied-upon-for-soundness
We should figure out if this is a hint or a language guarantee -- can this be relied upon for soundness?
@rustbot concern applicability-current-crate-or-whole-crate-graph
Looks like the current direction is "for the whole crate graph" (potentially "enforced" via Target Modifiers mechanism), but just noting a concern to make sure this is what we explicitly decide to go with.
@rustbot concern carve-out-interface-and-behavioral-stability-guarantees
I suppose in theory if
-Zbuild-std
becomes stabilized this might be less of an issue.
My understanding is that target modifiers effectively require -Zbuild-std
. From the RFC:
https://github.com/rust-lang/rfcs/blob/master/text/3716-target-modifiers.md#guide-level-explanation
The requirement that all CUs agree includes stdlib crates (core, alloc, std), so using these flags usually requires that you compile your own standard library with
-Zbuild-std
or by directly invokingrustc
. That said, some flags (e.g.,-Cpanic
) have mechanisms to avoid this requirement.
I also have another question: would
-Cmin-function-alignment
(whether hint form, or in some theoretical guarantee form) apply to extern functions? I guess why I'm asking is more for the "if this is intended to become a language guarantee", that can code also assume extern functions follow this min alignment?
You mean foreign functions here right (so, declared in an extern "abi" {}
block)? Making that assumption would mean that linking to any foreign function that is not appropriately aligned would be UB.
Such a guarantee/requirement would be really restrictive, especially for the performance use-case. I guess RfL does control it's build process sufficiently to guarantee that external C code also follows the alignment, but in general that seems unlikely?
On the other hand, from the outside it's not really clear whether a symbol is a foreign function, or a vanilla rust function.
Yeah, I'm assuming that this flag wouldn't apply to extern functions as a hint or guarantee (I don't really see how it meaningfully can). Or rather, if for whatever reason the intended use case is a language guarantee, then in any case reasoning about min alignment of extern functions seems like "well, the burden of ensuring that said extern fns do have the min alignment falls upon the user, if the user wishes to rely upon that for soundness". I'm asking in case there are... interesting or really weird interactions.
@rustbot resolve interaction-with-extern-functions
(Ignore this, this is purely my skill issue)
@rustbot resolve resolve interaction-with-extern-functions
Well an implication is that you can't, for an arbitrary function pointer, assume that it is well-aligned, because it may be an extern function
the PR for making -Zmin-function-alignment
a target modifier is up at #143323
As discovered in #143206 (comment), it looks like LLVM completely ignores function alignment specifications on WASM.
As discovered in #143206 (comment), it looks like LLVM completely ignores function alignment specifications on WASM.
That's tracked at #143368 now and yeah I assume it also affects this flag.
@rustbot concern wasm
@rustbot concern resolve-interaction-with-fn-ptrs
It seems like this does not actually interact with function pointers in any reasonable way, and in particular does not guarantee anything about the integral value of function pointers (see here).
That means this is entirely unobservable in the language itself, i.e. in pure Rust programs. It can only possibly be exploited by inline asm code that observes the layout of the generated machine code. We should make sure this is suitably documented as it is extremely surprising.
☔ The latest upstream changes (presumably #143556) made this pull request unmergeable. Please resolve the merge conflicts.
Uh oh!
There was an error while loading. Please reload this page.
tracking issue: #82232
split out from: #140261
Request for Stabilization
Summary
The
-Cmin-function-alignment=<align>
flag specifies the minimum alignment of functions for which code is generated.The
align
value must be a power of 2, other values are rejected.Note that
-Zbuild-std
(or similar) is required to apply this minimum alignment to standard library functions.By default, these functions come precompiled and their alignments won't respect the
min-function-alignment
flag.This flag is equivalent to:
-fmin-function-alignment
for GCC-falign-functions
for ClangThe specified alignment is a minimum. A higher alignment can be specified for specific functions by annotating the function with a
#[align(<align>)]
attribute.The attribute's value is ignored when it is lower than the value passed to
min-function-alignment
.There are two additional edge cases for this flag:
A
min-function-alignment
value lower than the target's minimum has no effect.8192
. Trying to set a higher value results in an error.Testing
History
-Zmin-function-alignment
#134030The -Zmin-function-alignment flag was requested by rust-for-linux #128830. It will be used soon (see #t-compiler/help > ✔ Alignment for function addresses).
Miri supports function alignment since #140072. In const-eval there is no way to observe the address of a function pointer, so no special attention is needed there (see #t-compiler/const-eval > function address alignment).
Originally, the maximum allowed alignment was
1 << 29
, because this is the highest value the LLVM API accepts. However, on COFF the highest supported alignment is only 8192 (see #142638). Practically speaking, that seems more than sufficient for all known use cases. So for simplicity, for now, we limit the alignment to 8192. The value can be increased on platforms that support it if the need arises.r? @workingjubilee
the first commit can be split out if that is more convenient.
Caution
Concerns (6 active)
(削除) interaction-with-extern-functions (削除ここまで)resolved in this comment(削除) resolve interaction-with-extern-functions (削除ここまで)resolved in this commentManaged by
@rustbot
—see help for details.