1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// This defines a base target-configuration for native UEFI systems. The UEFI specification has
// quite detailed sections on the ABI of all the supported target architectures. In almost all
// cases it simply follows what Microsoft Windows does. Hence, whenever in doubt, see the MSDN
// documentation.
// 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. 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.

use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, StackProbeType, TargetOptions};

pub fn opts() -> TargetOptions {
    let mut base = super::msvc_base::opts();

    let pre_link_args_msvc = vec![
        // Non-standard subsystems have no default entry-point in PE+ files. We have to define
        // one. "efi_main" seems to be a common choice amongst other implementations and the
        // spec.
        "/entry:efi_main".to_string(),
        // COFF images have a "Subsystem" field in their header, which defines what kind of
        // program it is. UEFI has 3 fields reserved, which are EFI_APPLICATION,
        // EFI_BOOT_SERVICE_DRIVER, and EFI_RUNTIME_DRIVER. We default to EFI_APPLICATION,
        // which is very likely the most common option. Individual projects can override this
        // with custom linker flags.
        // The subsystem-type only has minor effects on the application. It defines the memory
        // regions the application is loaded into (runtime-drivers need to be put into
        // reserved areas), as well as whether a return from the entry-point is treated as
        // exit (default for applications).
        "/subsystem:efi_application".to_string(),
    ];
    base.pre_link_args.entry(LinkerFlavor::Msvc).or_default().extend(pre_link_args_msvc.clone());
    base.pre_link_args
        .entry(LinkerFlavor::Lld(LldFlavor::Link))
        .or_default()
        .extend(pre_link_args_msvc);

    TargetOptions {
        os: "uefi".to_string(),
        linker_flavor: LinkerFlavor::Lld(LldFlavor::Link),
        disable_redzone: true,
        exe_suffix: ".efi".to_string(),
        allows_weak_linkage: false,
        panic_strategy: PanicStrategy::Abort,
        // LLVM does not emit inline assembly because the LLVM target does not get considered as…
        // "Windows".
        stack_probes: StackProbeType::Call,
        singlethread: true,
        linker: Some("rust-lld".to_string()),
        ..base
    }
}