Skip to content

Commit

Permalink
Rollup merge of #58420 - dvdhrm:target-uefi-comments, r=nagisa
Browse files Browse the repository at this point in the history
target/uefi: clarify documentation

This clarifies why FP-units are disabled on UEFI targets, as well as
why we must opt into the NXCOMPAT feature.

I did find some time to investigate why GRUB and friends disable FP on
UEFI. The specification explicitly allows using MMX/SSE/AVX, but as it
turns out it does not mandate enabling the instruction sets explicitly.
Hence, any use of these instructions will trigger CPU exceptions,
unless an application explicitly enables them (which is not an option,
as these are global flags that better be controlled by the
kernel/firmware).

Furthermore, UEFI systems are allowed to mark any non-code page as
non-executable. Hence, we must make sure to never place code on the
stack or heap. So we better pass /NXCOMPAT to the linker for it to
complain if it ever places code in non-code pages.

Lastly, this fixes some typos in related comments.

r? @alexcrichton
  • Loading branch information
Centril authored Feb 13, 2019
2 parents 0178f31 + 15e4bd3 commit 05cfcb5
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 12 deletions.
7 changes: 5 additions & 2 deletions src/librustc_target/spec/uefi_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// UEFI uses COFF/PE32+ format for binaries. All binaries must be statically linked. No dynamic
// linker is supported. As native to COFF, binaries are position-dependent, but will be relocated
// by the loader if the pre-chosen memory location is already in use.
// UEFI forbids running code on anything but the boot-CPU. Not interrupts are allowed other than
// UEFI forbids running code on anything but the boot-CPU. No interrupts are allowed other than
// the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all
// code runs in the same environment, no process separation is supported.

Expand All @@ -21,7 +21,10 @@ pub fn opts() -> TargetOptions {
"/NOLOGO".to_string(),

// UEFI is fully compatible to non-executable data pages. Tell the compiler that
// non-code sections can be marked as non-executable, including stack pages.
// non-code sections can be marked as non-executable, including stack pages. In fact,
// firmware might enforce this, so we better let the linker know about this, so it
// will fail if the compiler ever tries placing code on the stack (e.g., trampoline
// constructs and alike).
"/NXCOMPAT".to_string(),

// There is no runtime for UEFI targets, prevent them from being linked. UEFI targets
Expand Down
22 changes: 12 additions & 10 deletions src/librustc_target/spec/x86_64_unknown_uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,25 @@ pub fn target() -> TargetResult {
base.cpu = "x86-64".to_string();
base.max_atomic_width = Some(64);

// We disable MMX and SSE for now. UEFI does not prevent these from being used, but there have
// been reports to GRUB that some firmware does not initialize the FP exception handlers
// properly. Therefore, using FP coprocessors will end you up at random memory locations when
// you throw FP exceptions.
// To be safe, we disable them for now and force soft-float. This can be revisited when we
// have more test coverage. Disabling FP served GRUB well so far, so it should be good for us
// as well.
// We disable MMX and SSE for now, even though UEFI allows using them. Problem is, you have to
// enable these CPU features explicitly before their first use, otherwise their instructions
// will trigger an exception. Rust does not inject any code that enables AVX/MMX/SSE
// instruction sets, so this must be done by the firmware. However, existing firmware is known
// to leave these uninitialized, thus triggering exceptions if we make use of them. Which is
// why we avoid them and instead use soft-floats. This is also what GRUB and friends did so
// far.
// If you initialize FP units yourself, you can override these flags with custom linker
// arguments, thus giving you access to full MMX/SSE acceleration.
base.features = "-mmx,-sse,+soft-float".to_string();

// UEFI systems run without a host OS, hence we cannot assume any code locality. We must tell
// LLVM to expect code to reference any address in the address-space. The "large" code-model
// places no locality-restrictions, so it fits well here.
base.code_model = Some("large".to_string());

// UEFI mostly mirrors the calling-conventions used on windows. In case of x86-64 this means
// small structs will be returned as int. This shouldn't matter much, since the restrictions
// placed by the UEFI specifications forbid any ABI to return structures.
// UEFI mirrors the calling-conventions used on windows. In case of x86-64 this means small
// structs will be returned as int. This shouldn't matter much, since the restrictions placed
// by the UEFI specifications forbid any ABI to return structures.
base.abi_return_struct_as_int = true;

Ok(Target {
Expand Down

0 comments on commit 05cfcb5

Please sign in to comment.