about summary refs log tree commit diff
path: root/compiler/rustc_target/src/spec/base
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_target/src/spec/base')
-rw-r--r--compiler/rustc_target/src/spec/base/aix.rs32
-rw-r--r--compiler/rustc_target/src/spec/base/android.rs16
-rw-r--r--compiler/rustc_target/src/spec/base/apple/mod.rs356
-rw-r--r--compiler/rustc_target/src/spec/base/apple/tests.rs38
-rw-r--r--compiler/rustc_target/src/spec/base/avr_gnu.rs368
-rw-r--r--compiler/rustc_target/src/spec/base/bpf.rs29
-rw-r--r--compiler/rustc_target/src/spec/base/dragonfly.rs14
-rw-r--r--compiler/rustc_target/src/spec/base/freebsd.rs15
-rw-r--r--compiler/rustc_target/src/spec/base/fuchsia.rs43
-rw-r--r--compiler/rustc_target/src/spec/base/haiku.rs11
-rw-r--r--compiler/rustc_target/src/spec/base/hermit.rs15
-rw-r--r--compiler/rustc_target/src/spec/base/hurd.rs15
-rw-r--r--compiler/rustc_target/src/spec/base/hurd_gnu.rs5
-rw-r--r--compiler/rustc_target/src/spec/base/illumos.rs59
-rw-r--r--compiler/rustc_target/src/spec/base/l4re.rs14
-rw-r--r--compiler/rustc_target/src/spec/base/linux.rs21
-rw-r--r--compiler/rustc_target/src/spec/base/linux_gnu.rs5
-rw-r--r--compiler/rustc_target/src/spec/base/linux_musl.rs16
-rw-r--r--compiler/rustc_target/src/spec/base/linux_ohos.rs12
-rw-r--r--compiler/rustc_target/src/spec/base/linux_uclibc.rs5
-rw-r--r--compiler/rustc_target/src/spec/base/mod.rs37
-rw-r--r--compiler/rustc_target/src/spec/base/msvc.rs26
-rw-r--r--compiler/rustc_target/src/spec/base/netbsd.rs16
-rw-r--r--compiler/rustc_target/src/spec/base/nto_qnx.rs18
-rw-r--r--compiler/rustc_target/src/spec/base/openbsd.rs16
-rw-r--r--compiler/rustc_target/src/spec/base/redox.rs17
-rw-r--r--compiler/rustc_target/src/spec/base/solaris.rs16
-rw-r--r--compiler/rustc_target/src/spec/base/solid.rs12
-rw-r--r--compiler/rustc_target/src/spec/base/teeos.rs28
-rw-r--r--compiler/rustc_target/src/spec/base/thumb.rs59
-rw-r--r--compiler/rustc_target/src/spec/base/uefi_msvc.rs52
-rw-r--r--compiler/rustc_target/src/spec/base/unikraft_linux_musl.rs15
-rw-r--r--compiler/rustc_target/src/spec/base/vxworks.rs21
-rw-r--r--compiler/rustc_target/src/spec/base/wasm.rs135
-rw-r--r--compiler/rustc_target/src/spec/base/windows_gnu.rs108
-rw-r--r--compiler/rustc_target/src/spec/base/windows_gnullvm.rs47
-rw-r--r--compiler/rustc_target/src/spec/base/windows_msvc.rs34
-rw-r--r--compiler/rustc_target/src/spec/base/windows_uwp_gnu.rs35
-rw-r--r--compiler/rustc_target/src/spec/base/windows_uwp_msvc.rs11
39 files changed, 1792 insertions, 0 deletions
diff --git a/compiler/rustc_target/src/spec/base/aix.rs b/compiler/rustc_target/src/spec/base/aix.rs
new file mode 100644
index 00000000000..c71c4ba2cc9
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/aix.rs
@@ -0,0 +1,32 @@
+use crate::abi::Endian;
+use crate::spec::{crt_objects, cvs, Cc, CodeModel, LinkOutputKind, LinkerFlavor, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        abi: "vec-extabi".into(),
+        code_model: Some(CodeModel::Small),
+        cpu: "pwr7".into(),
+        os: "aix".into(),
+        vendor: "ibm".into(),
+        dynamic_linking: true,
+        endian: Endian::Big,
+        executables: true,
+        archive_format: "aix_big".into(),
+        families: cvs!["unix"],
+        has_rpath: false,
+        has_thread_local: true,
+        crt_static_respected: true,
+        linker_flavor: LinkerFlavor::Unix(Cc::No),
+        linker: Some("ld".into()),
+        eh_frame_header: false,
+        is_like_aix: true,
+        default_dwarf_version: 3,
+        function_sections: true,
+        pre_link_objects: crt_objects::new(&[
+            (LinkOutputKind::DynamicNoPicExe, &["/usr/lib/crt0_64.o", "/usr/lib/crti_64.o"]),
+            (LinkOutputKind::DynamicPicExe, &["/usr/lib/crt0_64.o", "/usr/lib/crti_64.o"]),
+        ]),
+        dll_suffix: ".a".into(),
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/android.rs b/compiler/rustc_target/src/spec/base/android.rs
new file mode 100644
index 00000000000..af15c16a5a9
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/android.rs
@@ -0,0 +1,16 @@
+use crate::spec::{base, SanitizerSet, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    let mut base = base::linux::opts();
+    base.os = "android".into();
+    base.is_like_android = true;
+    base.default_dwarf_version = 2;
+    base.has_thread_local = false;
+    base.supported_sanitizers = SanitizerSet::ADDRESS;
+    // This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867
+    // for context. (At that time, there was no `-C force-unwind-tables`, so the only solution
+    // was to always emit `uwtable`).
+    base.default_uwtable = true;
+    base.crt_static_respected = true;
+    base
+}
diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs
new file mode 100644
index 00000000000..99e64503e25
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/apple/mod.rs
@@ -0,0 +1,356 @@
+use std::{borrow::Cow, env};
+
+use crate::spec::{add_link_args, add_link_args_iter};
+use crate::spec::{cvs, Cc, DebuginfoKind, FramePointer, LinkArgs};
+use crate::spec::{LinkerFlavor, Lld, SplitDebuginfo, StaticCow, Target, TargetOptions};
+
+#[cfg(test)]
+mod tests;
+
+use Arch::*;
+#[allow(non_camel_case_types)]
+#[derive(Copy, Clone)]
+pub enum Arch {
+    Armv7k,
+    Armv7s,
+    Arm64,
+    Arm64_32,
+    I386,
+    I686,
+    X86_64,
+    X86_64h,
+    X86_64_sim,
+    X86_64_macabi,
+    Arm64_macabi,
+    Arm64_sim,
+}
+
+impl Arch {
+    pub fn target_name(self) -> &'static str {
+        match self {
+            Armv7k => "armv7k",
+            Armv7s => "armv7s",
+            Arm64 | Arm64_macabi | Arm64_sim => "arm64",
+            Arm64_32 => "arm64_32",
+            I386 => "i386",
+            I686 => "i686",
+            X86_64 | X86_64_sim | X86_64_macabi => "x86_64",
+            X86_64h => "x86_64h",
+        }
+    }
+
+    pub fn target_arch(self) -> Cow<'static, str> {
+        Cow::Borrowed(match self {
+            Armv7k | Armv7s => "arm",
+            Arm64 | Arm64_32 | Arm64_macabi | Arm64_sim => "aarch64",
+            I386 | I686 => "x86",
+            X86_64 | X86_64_sim | X86_64_macabi | X86_64h => "x86_64",
+        })
+    }
+
+    fn target_abi(self) -> &'static str {
+        match self {
+            Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64h => "",
+            X86_64_macabi | Arm64_macabi => "macabi",
+            // x86_64-apple-ios is a simulator target, even though it isn't
+            // declared that way in the target like the other ones...
+            Arm64_sim | X86_64_sim => "sim",
+        }
+    }
+
+    fn target_cpu(self) -> &'static str {
+        match self {
+            Armv7k => "cortex-a8",
+            Armv7s => "swift", // iOS 10 is only supported on iPhone 5 or higher.
+            Arm64 => "apple-a7",
+            Arm64_32 => "apple-s4",
+            // Only macOS 10.12+ is supported, which means
+            // all x86_64/x86 CPUs must be running at least penryn
+            // https://github.com/llvm/llvm-project/blob/01f924d0e37a5deae51df0d77e10a15b63aa0c0f/clang/lib/Driver/ToolChains/Arch/X86.cpp#L79-L82
+            I386 | I686 => "penryn",
+            X86_64 | X86_64_sim => "penryn",
+            X86_64_macabi => "penryn",
+            // Note: `core-avx2` is slightly more advanced than `x86_64h`, see
+            // comments (and disabled features) in `x86_64h_apple_darwin` for
+            // details. It is a higher baseline then `penryn` however.
+            X86_64h => "core-avx2",
+            Arm64_macabi => "apple-a12",
+            Arm64_sim => "apple-a12",
+        }
+    }
+}
+
+fn pre_link_args(os: &'static str, arch: Arch, abi: &'static str) -> LinkArgs {
+    let platform_name: StaticCow<str> = match abi {
+        "sim" => format!("{os}-simulator").into(),
+        "macabi" => "mac-catalyst".into(),
+        _ => os.into(),
+    };
+
+    let platform_version: StaticCow<str> = match os {
+        "ios" => ios_lld_platform_version(),
+        "tvos" => tvos_lld_platform_version(),
+        "watchos" => watchos_lld_platform_version(),
+        "macos" => macos_lld_platform_version(arch),
+        _ => unreachable!(),
+    }
+    .into();
+
+    let arch = arch.target_name();
+
+    let mut args = TargetOptions::link_args(
+        LinkerFlavor::Darwin(Cc::No, Lld::No),
+        &["-arch", arch, "-platform_version"],
+    );
+    add_link_args_iter(
+        &mut args,
+        LinkerFlavor::Darwin(Cc::No, Lld::No),
+        [platform_name, platform_version.clone(), platform_version].into_iter(),
+    );
+    if abi != "macabi" {
+        add_link_args(&mut args, LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-arch", arch]);
+    }
+
+    args
+}
+
+pub fn opts(os: &'static str, arch: Arch) -> TargetOptions {
+    let abi = arch.target_abi();
+
+    TargetOptions {
+        abi: abi.into(),
+        os: os.into(),
+        cpu: arch.target_cpu().into(),
+        link_env_remove: link_env_remove(arch, os),
+        vendor: "apple".into(),
+        linker_flavor: LinkerFlavor::Darwin(Cc::Yes, Lld::No),
+        // macOS has -dead_strip, which doesn't rely on function_sections
+        function_sections: false,
+        dynamic_linking: true,
+        pre_link_args: pre_link_args(os, arch, abi),
+        families: cvs!["unix"],
+        is_like_osx: true,
+        // LLVM notes that macOS 10.11+ and iOS 9+ default
+        // to v4, so we do the same.
+        // https://github.com/llvm/llvm-project/blob/378778a0d10c2f8d5df8ceff81f95b6002984a4b/clang/lib/Driver/ToolChains/Darwin.cpp#L1203
+        default_dwarf_version: 4,
+        frame_pointer: FramePointer::Always,
+        has_rpath: true,
+        dll_suffix: ".dylib".into(),
+        archive_format: "darwin".into(),
+        // Thread locals became available with iOS 8 and macOS 10.7,
+        // and both are far below our minimum.
+        has_thread_local: true,
+        abi_return_struct_as_int: true,
+        emit_debug_gdb_scripts: false,
+        eh_frame_header: false,
+
+        debuginfo_kind: DebuginfoKind::DwarfDsym,
+        // The historical default for macOS targets is to run `dsymutil` which
+        // generates a packed version of debuginfo split from the main file.
+        split_debuginfo: SplitDebuginfo::Packed,
+        supported_split_debuginfo: Cow::Borrowed(&[
+            SplitDebuginfo::Packed,
+            SplitDebuginfo::Unpacked,
+            SplitDebuginfo::Off,
+        ]),
+
+        // This environment variable is pretty magical but is intended for
+        // producing deterministic builds. This was first discovered to be used
+        // by the `ar` tool as a way to control whether or not mtime entries in
+        // the archive headers were set to zero or not. It appears that
+        // eventually the linker got updated to do the same thing and now reads
+        // this environment variable too in recent versions.
+        //
+        // For some more info see the commentary on #47086
+        link_env: Cow::Borrowed(&[(Cow::Borrowed("ZERO_AR_DATE"), Cow::Borrowed("1"))]),
+
+        ..Default::default()
+    }
+}
+
+pub fn sdk_version(platform: u32) -> Option<(u32, u32)> {
+    // NOTE: These values are from an arbitrary point in time but shouldn't make it into the final
+    // binary since the final link command will have the current SDK version passed to it.
+    match platform {
+        object::macho::PLATFORM_MACOS => Some((13, 1)),
+        object::macho::PLATFORM_IOS
+        | object::macho::PLATFORM_IOSSIMULATOR
+        | object::macho::PLATFORM_TVOS
+        | object::macho::PLATFORM_TVOSSIMULATOR
+        | object::macho::PLATFORM_MACCATALYST => Some((16, 2)),
+        object::macho::PLATFORM_WATCHOS | object::macho::PLATFORM_WATCHOSSIMULATOR => Some((9, 1)),
+        _ => None,
+    }
+}
+
+pub fn platform(target: &Target) -> Option<u32> {
+    Some(match (&*target.os, &*target.abi) {
+        ("macos", _) => object::macho::PLATFORM_MACOS,
+        ("ios", "macabi") => object::macho::PLATFORM_MACCATALYST,
+        ("ios", "sim") => object::macho::PLATFORM_IOSSIMULATOR,
+        ("ios", _) => object::macho::PLATFORM_IOS,
+        ("watchos", "sim") => object::macho::PLATFORM_WATCHOSSIMULATOR,
+        ("watchos", _) => object::macho::PLATFORM_WATCHOS,
+        ("tvos", "sim") => object::macho::PLATFORM_TVOSSIMULATOR,
+        ("tvos", _) => object::macho::PLATFORM_TVOS,
+        _ => return None,
+    })
+}
+
+pub fn deployment_target(target: &Target) -> Option<(u32, u32)> {
+    let (major, minor) = match &*target.os {
+        "macos" => {
+            // This does not need to be specific. It just needs to handle x86 vs M1.
+            let arch = if target.arch == "x86" || target.arch == "x86_64" { X86_64 } else { Arm64 };
+            macos_deployment_target(arch)
+        }
+        "ios" => match &*target.abi {
+            "macabi" => mac_catalyst_deployment_target(),
+            _ => ios_deployment_target(),
+        },
+        "watchos" => watchos_deployment_target(),
+        "tvos" => tvos_deployment_target(),
+        _ => return None,
+    };
+
+    Some((major, minor))
+}
+
+fn from_set_deployment_target(var_name: &str) -> Option<(u32, u32)> {
+    let deployment_target = env::var(var_name).ok()?;
+    let (unparsed_major, unparsed_minor) = deployment_target.split_once('.')?;
+    let (major, minor) = (unparsed_major.parse().ok()?, unparsed_minor.parse().ok()?);
+
+    Some((major, minor))
+}
+
+fn macos_default_deployment_target(arch: Arch) -> (u32, u32) {
+    match arch {
+        // Note: Arm64_sim is not included since macOS has no simulator.
+        Arm64 | Arm64_macabi => (11, 0),
+        _ => (10, 12),
+    }
+}
+
+fn macos_deployment_target(arch: Arch) -> (u32, u32) {
+    // If you are looking for the default deployment target, prefer `rustc --print deployment-target`.
+    from_set_deployment_target("MACOSX_DEPLOYMENT_TARGET")
+        .unwrap_or_else(|| macos_default_deployment_target(arch))
+}
+
+fn macos_lld_platform_version(arch: Arch) -> String {
+    let (major, minor) = macos_deployment_target(arch);
+    format!("{major}.{minor}")
+}
+
+pub fn macos_llvm_target(arch: Arch) -> String {
+    let (major, minor) = macos_deployment_target(arch);
+    format!("{}-apple-macosx{}.{}.0", arch.target_name(), major, minor)
+}
+
+fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]> {
+    // Apple platforms only officially support macOS as a host for any compilation.
+    //
+    // If building for macOS, we go ahead and remove any erroneous environment state
+    // that's only applicable to cross-OS compilation. Always leave anything for the
+    // host OS alone though.
+    if os == "macos" {
+        let mut env_remove = Vec::with_capacity(2);
+        // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
+        // may occur when we're linking a custom build script while targeting iOS for example.
+        if let Ok(sdkroot) = env::var("SDKROOT") {
+            if sdkroot.contains("iPhoneOS.platform")
+                || sdkroot.contains("iPhoneSimulator.platform")
+                || sdkroot.contains("AppleTVOS.platform")
+                || sdkroot.contains("AppleTVSimulator.platform")
+                || sdkroot.contains("WatchOS.platform")
+                || sdkroot.contains("WatchSimulator.platform")
+            {
+                env_remove.push("SDKROOT".into())
+            }
+        }
+        // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
+        // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
+        // although this is apparently ignored when using the linker at "/usr/bin/ld".
+        env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into());
+        env_remove.push("TVOS_DEPLOYMENT_TARGET".into());
+        env_remove.into()
+    } else {
+        // Otherwise if cross-compiling for a different OS/SDK, remove any part
+        // of the linking environment that's wrong and reversed.
+        match arch {
+            Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64_sim | X86_64h
+            | Arm64_sim => {
+                cvs!["MACOSX_DEPLOYMENT_TARGET"]
+            }
+            X86_64_macabi | Arm64_macabi => cvs!["IPHONEOS_DEPLOYMENT_TARGET"],
+        }
+    }
+}
+
+fn ios_deployment_target() -> (u32, u32) {
+    // If you are looking for the default deployment target, prefer `rustc --print deployment-target`.
+    from_set_deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((10, 0))
+}
+
+fn mac_catalyst_deployment_target() -> (u32, u32) {
+    // If you are looking for the default deployment target, prefer `rustc --print deployment-target`.
+    from_set_deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((14, 0))
+}
+
+pub fn ios_llvm_target(arch: Arch) -> String {
+    // Modern iOS tooling extracts information about deployment target
+    // from LC_BUILD_VERSION. This load command will only be emitted when
+    // we build with a version specific `llvm_target`, with the version
+    // set high enough. Luckily one LC_BUILD_VERSION is enough, for Xcode
+    // to pick it up (since std and core are still built with the fallback
+    // of version 7.0 and hence emit the old LC_IPHONE_MIN_VERSION).
+    let (major, minor) = ios_deployment_target();
+    format!("{}-apple-ios{}.{}.0", arch.target_name(), major, minor)
+}
+
+fn ios_lld_platform_version() -> String {
+    let (major, minor) = ios_deployment_target();
+    format!("{major}.{minor}")
+}
+
+pub fn ios_sim_llvm_target(arch: Arch) -> String {
+    let (major, minor) = ios_deployment_target();
+    format!("{}-apple-ios{}.{}.0-simulator", arch.target_name(), major, minor)
+}
+
+fn tvos_deployment_target() -> (u32, u32) {
+    // If you are looking for the default deployment target, prefer `rustc --print deployment-target`.
+    from_set_deployment_target("TVOS_DEPLOYMENT_TARGET").unwrap_or((10, 0))
+}
+
+fn tvos_lld_platform_version() -> String {
+    let (major, minor) = tvos_deployment_target();
+    format!("{major}.{minor}")
+}
+
+pub fn tvos_llvm_target(arch: Arch) -> String {
+    let (major, minor) = tvos_deployment_target();
+    format!("{}-apple-tvos{}.{}.0", arch.target_name(), major, minor)
+}
+
+pub fn tvos_sim_llvm_target(arch: Arch) -> String {
+    let (major, minor) = tvos_deployment_target();
+    format!("{}-apple-tvos{}.{}.0-simulator", arch.target_name(), major, minor)
+}
+
+fn watchos_deployment_target() -> (u32, u32) {
+    // If you are looking for the default deployment target, prefer `rustc --print deployment-target`.
+    from_set_deployment_target("WATCHOS_DEPLOYMENT_TARGET").unwrap_or((5, 0))
+}
+
+fn watchos_lld_platform_version() -> String {
+    let (major, minor) = watchos_deployment_target();
+    format!("{major}.{minor}")
+}
+
+pub fn watchos_sim_llvm_target(arch: Arch) -> String {
+    let (major, minor) = watchos_deployment_target();
+    format!("{}-apple-watchos{}.{}.0-simulator", arch.target_name(), major, minor)
+}
diff --git a/compiler/rustc_target/src/spec/base/apple/tests.rs b/compiler/rustc_target/src/spec/base/apple/tests.rs
new file mode 100644
index 00000000000..3b23ddadcc4
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/apple/tests.rs
@@ -0,0 +1,38 @@
+use crate::spec::{
+    aarch64_apple_darwin, aarch64_apple_ios_sim, aarch64_apple_watchos_sim, i686_apple_darwin,
+    x86_64_apple_darwin, x86_64_apple_ios, x86_64_apple_tvos, x86_64_apple_watchos_sim,
+};
+
+#[test]
+fn simulator_targets_set_abi() {
+    let all_sim_targets = [
+        x86_64_apple_ios::target(),
+        x86_64_apple_tvos::target(),
+        x86_64_apple_watchos_sim::target(),
+        aarch64_apple_ios_sim::target(),
+        // Note: There is currently no ARM64 tvOS simulator target
+        aarch64_apple_watchos_sim::target(),
+    ];
+
+    for target in all_sim_targets {
+        assert_eq!(target.abi, "sim")
+    }
+}
+
+#[test]
+fn macos_link_environment_unmodified() {
+    let all_macos_targets = [
+        aarch64_apple_darwin::target(),
+        i686_apple_darwin::target(),
+        x86_64_apple_darwin::target(),
+    ];
+
+    for target in all_macos_targets {
+        // macOS targets should only remove information for cross-compiling, but never
+        // for the host.
+        assert_eq!(
+            target.link_env_remove,
+            crate::spec::cvs!["IPHONEOS_DEPLOYMENT_TARGET", "TVOS_DEPLOYMENT_TARGET"],
+        );
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/avr_gnu.rs b/compiler/rustc_target/src/spec/base/avr_gnu.rs
new file mode 100644
index 00000000000..cd324c94bbe
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/avr_gnu.rs
@@ -0,0 +1,368 @@
+use crate::spec::{Cc, LinkerFlavor, Lld, RelocModel, Target, TargetOptions};
+use object::elf;
+
+/// A base target for AVR devices using the GNU toolchain.
+///
+/// Requires GNU avr-gcc and avr-binutils on the host system.
+/// FIXME: Remove the second parameter when const string concatenation is possible.
+pub fn target(target_cpu: &'static str, mmcu: &'static str) -> Target {
+    Target {
+        arch: "avr".into(),
+        data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8".into(),
+        llvm_target: "avr-unknown-unknown".into(),
+        pointer_width: 16,
+        options: TargetOptions {
+            c_int_width: "16".into(),
+            cpu: target_cpu.into(),
+            exe_suffix: ".elf".into(),
+
+            linker: Some("avr-gcc".into()),
+            eh_frame_header: false,
+            pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[mmcu]),
+            late_link_args: TargetOptions::link_args(
+                LinkerFlavor::Gnu(Cc::Yes, Lld::No),
+                &["-lgcc"],
+            ),
+            max_atomic_width: Some(16),
+            atomic_cas: false,
+            relocation_model: RelocModel::Static,
+            ..TargetOptions::default()
+        },
+    }
+}
+
+/// Resolve the value of the EF_AVR_ARCH field for AVR ELF files, given the
+/// name of the target CPU / MCU.
+///
+/// In ELF files using the AVR architecture, the lower 7 bits of the e_flags
+/// field is a code that identifies the "ISA revision" of the object code.
+///
+/// This flag is generally set by AVR compilers in their output ELF files,
+/// and linkers like avr-ld check this flag in all of their input files to
+/// make sure they are compiled with the same ISA revision.
+pub fn ef_avr_arch(target_cpu: &str) -> u32 {
+    // Adapted from llvm-project/llvm/lib/target/AVR/AVRDevices.td
+    match target_cpu {
+        // Generic MCUs
+        "avr1" => elf::EF_AVR_ARCH_AVR1,
+        "avr2" => elf::EF_AVR_ARCH_AVR2,
+        "avr25" => elf::EF_AVR_ARCH_AVR25,
+        "avr3" => elf::EF_AVR_ARCH_AVR3,
+        "avr31" => elf::EF_AVR_ARCH_AVR31,
+        "avr35" => elf::EF_AVR_ARCH_AVR35,
+        "avr4" => elf::EF_AVR_ARCH_AVR4,
+        "avr5" => elf::EF_AVR_ARCH_AVR5,
+        "avr51" => elf::EF_AVR_ARCH_AVR51,
+        "avr6" => elf::EF_AVR_ARCH_AVR6,
+        "avrxmega1" => elf::EF_AVR_ARCH_XMEGA1,
+        "avrxmega2" => elf::EF_AVR_ARCH_XMEGA2,
+        "avrxmega3" => elf::EF_AVR_ARCH_XMEGA3,
+        "avrxmega4" => elf::EF_AVR_ARCH_XMEGA4,
+        "avrxmega5" => elf::EF_AVR_ARCH_XMEGA5,
+        "avrxmega6" => elf::EF_AVR_ARCH_XMEGA6,
+        "avrxmega7" => elf::EF_AVR_ARCH_XMEGA7,
+        "avrtiny" => elf::EF_AVR_ARCH_AVRTINY,
+
+        // Specific MCUs
+        "at90s1200" => elf::EF_AVR_ARCH_AVR1,
+        "attiny11" => elf::EF_AVR_ARCH_AVR1,
+        "attiny12" => elf::EF_AVR_ARCH_AVR1,
+        "attiny15" => elf::EF_AVR_ARCH_AVR1,
+        "attiny28" => elf::EF_AVR_ARCH_AVR1,
+        "at90s2313" => elf::EF_AVR_ARCH_AVR2,
+        "at90s2323" => elf::EF_AVR_ARCH_AVR2,
+        "at90s2333" => elf::EF_AVR_ARCH_AVR2,
+        "at90s2343" => elf::EF_AVR_ARCH_AVR2,
+        "attiny22" => elf::EF_AVR_ARCH_AVR2,
+        "attiny26" => elf::EF_AVR_ARCH_AVR2,
+        "at86rf401" => elf::EF_AVR_ARCH_AVR25,
+        "at90s4414" => elf::EF_AVR_ARCH_AVR2,
+        "at90s4433" => elf::EF_AVR_ARCH_AVR2,
+        "at90s4434" => elf::EF_AVR_ARCH_AVR2,
+        "at90s8515" => elf::EF_AVR_ARCH_AVR2,
+        "at90c8534" => elf::EF_AVR_ARCH_AVR2,
+        "at90s8535" => elf::EF_AVR_ARCH_AVR2,
+        "ata5272" => elf::EF_AVR_ARCH_AVR25,
+        "ata6616c" => elf::EF_AVR_ARCH_AVR25,
+        "attiny13" => elf::EF_AVR_ARCH_AVR25,
+        "attiny13a" => elf::EF_AVR_ARCH_AVR25,
+        "attiny2313" => elf::EF_AVR_ARCH_AVR25,
+        "attiny2313a" => elf::EF_AVR_ARCH_AVR25,
+        "attiny24" => elf::EF_AVR_ARCH_AVR25,
+        "attiny24a" => elf::EF_AVR_ARCH_AVR25,
+        "attiny4313" => elf::EF_AVR_ARCH_AVR25,
+        "attiny44" => elf::EF_AVR_ARCH_AVR25,
+        "attiny44a" => elf::EF_AVR_ARCH_AVR25,
+        "attiny84" => elf::EF_AVR_ARCH_AVR25,
+        "attiny84a" => elf::EF_AVR_ARCH_AVR25,
+        "attiny25" => elf::EF_AVR_ARCH_AVR25,
+        "attiny45" => elf::EF_AVR_ARCH_AVR25,
+        "attiny85" => elf::EF_AVR_ARCH_AVR25,
+        "attiny261" => elf::EF_AVR_ARCH_AVR25,
+        "attiny261a" => elf::EF_AVR_ARCH_AVR25,
+        "attiny441" => elf::EF_AVR_ARCH_AVR25,
+        "attiny461" => elf::EF_AVR_ARCH_AVR25,
+        "attiny461a" => elf::EF_AVR_ARCH_AVR25,
+        "attiny841" => elf::EF_AVR_ARCH_AVR25,
+        "attiny861" => elf::EF_AVR_ARCH_AVR25,
+        "attiny861a" => elf::EF_AVR_ARCH_AVR25,
+        "attiny87" => elf::EF_AVR_ARCH_AVR25,
+        "attiny43u" => elf::EF_AVR_ARCH_AVR25,
+        "attiny48" => elf::EF_AVR_ARCH_AVR25,
+        "attiny88" => elf::EF_AVR_ARCH_AVR25,
+        "attiny828" => elf::EF_AVR_ARCH_AVR25,
+        "at43usb355" => elf::EF_AVR_ARCH_AVR3,
+        "at76c711" => elf::EF_AVR_ARCH_AVR3,
+        "atmega103" => elf::EF_AVR_ARCH_AVR31,
+        "at43usb320" => elf::EF_AVR_ARCH_AVR31,
+        "attiny167" => elf::EF_AVR_ARCH_AVR35,
+        "at90usb82" => elf::EF_AVR_ARCH_AVR35,
+        "at90usb162" => elf::EF_AVR_ARCH_AVR35,
+        "ata5505" => elf::EF_AVR_ARCH_AVR35,
+        "ata6617c" => elf::EF_AVR_ARCH_AVR35,
+        "ata664251" => elf::EF_AVR_ARCH_AVR35,
+        "atmega8u2" => elf::EF_AVR_ARCH_AVR35,
+        "atmega16u2" => elf::EF_AVR_ARCH_AVR35,
+        "atmega32u2" => elf::EF_AVR_ARCH_AVR35,
+        "attiny1634" => elf::EF_AVR_ARCH_AVR35,
+        "atmega8" => elf::EF_AVR_ARCH_AVR4,
+        "ata6289" => elf::EF_AVR_ARCH_AVR4,
+        "atmega8a" => elf::EF_AVR_ARCH_AVR4,
+        "ata6285" => elf::EF_AVR_ARCH_AVR4,
+        "ata6286" => elf::EF_AVR_ARCH_AVR4,
+        "ata6612c" => elf::EF_AVR_ARCH_AVR4,
+        "atmega48" => elf::EF_AVR_ARCH_AVR4,
+        "atmega48a" => elf::EF_AVR_ARCH_AVR4,
+        "atmega48pa" => elf::EF_AVR_ARCH_AVR4,
+        "atmega48pb" => elf::EF_AVR_ARCH_AVR4,
+        "atmega48p" => elf::EF_AVR_ARCH_AVR4,
+        "atmega88" => elf::EF_AVR_ARCH_AVR4,
+        "atmega88a" => elf::EF_AVR_ARCH_AVR4,
+        "atmega88p" => elf::EF_AVR_ARCH_AVR4,
+        "atmega88pa" => elf::EF_AVR_ARCH_AVR4,
+        "atmega88pb" => elf::EF_AVR_ARCH_AVR4,
+        "atmega8515" => elf::EF_AVR_ARCH_AVR4,
+        "atmega8535" => elf::EF_AVR_ARCH_AVR4,
+        "atmega8hva" => elf::EF_AVR_ARCH_AVR4,
+        "at90pwm1" => elf::EF_AVR_ARCH_AVR4,
+        "at90pwm2" => elf::EF_AVR_ARCH_AVR4,
+        "at90pwm2b" => elf::EF_AVR_ARCH_AVR4,
+        "at90pwm3" => elf::EF_AVR_ARCH_AVR4,
+        "at90pwm3b" => elf::EF_AVR_ARCH_AVR4,
+        "at90pwm81" => elf::EF_AVR_ARCH_AVR4,
+        "ata5702m322" => elf::EF_AVR_ARCH_AVR5,
+        "ata5782" => elf::EF_AVR_ARCH_AVR5,
+        "ata5790" => elf::EF_AVR_ARCH_AVR5,
+        "ata5790n" => elf::EF_AVR_ARCH_AVR5,
+        "ata5791" => elf::EF_AVR_ARCH_AVR5,
+        "ata5795" => elf::EF_AVR_ARCH_AVR5,
+        "ata5831" => elf::EF_AVR_ARCH_AVR5,
+        "ata6613c" => elf::EF_AVR_ARCH_AVR5,
+        "ata6614q" => elf::EF_AVR_ARCH_AVR5,
+        "ata8210" => elf::EF_AVR_ARCH_AVR5,
+        "ata8510" => elf::EF_AVR_ARCH_AVR5,
+        "atmega16" => elf::EF_AVR_ARCH_AVR5,
+        "atmega16a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega161" => elf::EF_AVR_ARCH_AVR5,
+        "atmega162" => elf::EF_AVR_ARCH_AVR5,
+        "atmega163" => elf::EF_AVR_ARCH_AVR5,
+        "atmega164a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega164p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega164pa" => elf::EF_AVR_ARCH_AVR5,
+        "atmega165" => elf::EF_AVR_ARCH_AVR5,
+        "atmega165a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega165p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega165pa" => elf::EF_AVR_ARCH_AVR5,
+        "atmega168" => elf::EF_AVR_ARCH_AVR5,
+        "atmega168a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega168p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega168pa" => elf::EF_AVR_ARCH_AVR5,
+        "atmega168pb" => elf::EF_AVR_ARCH_AVR5,
+        "atmega169" => elf::EF_AVR_ARCH_AVR5,
+        "atmega169a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega169p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega169pa" => elf::EF_AVR_ARCH_AVR5,
+        "atmega32" => elf::EF_AVR_ARCH_AVR5,
+        "atmega32a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega323" => elf::EF_AVR_ARCH_AVR5,
+        "atmega324a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega324p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega324pa" => elf::EF_AVR_ARCH_AVR5,
+        "atmega324pb" => elf::EF_AVR_ARCH_AVR5,
+        "atmega325" => elf::EF_AVR_ARCH_AVR5,
+        "atmega325a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega325p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega325pa" => elf::EF_AVR_ARCH_AVR5,
+        "atmega3250" => elf::EF_AVR_ARCH_AVR5,
+        "atmega3250a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega3250p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega3250pa" => elf::EF_AVR_ARCH_AVR5,
+        "atmega328" => elf::EF_AVR_ARCH_AVR5,
+        "atmega328p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega328pb" => elf::EF_AVR_ARCH_AVR5,
+        "atmega329" => elf::EF_AVR_ARCH_AVR5,
+        "atmega329a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega329p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega329pa" => elf::EF_AVR_ARCH_AVR5,
+        "atmega3290" => elf::EF_AVR_ARCH_AVR5,
+        "atmega3290a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega3290p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega3290pa" => elf::EF_AVR_ARCH_AVR5,
+        "atmega406" => elf::EF_AVR_ARCH_AVR5,
+        "atmega64" => elf::EF_AVR_ARCH_AVR5,
+        "atmega64a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega640" => elf::EF_AVR_ARCH_AVR5,
+        "atmega644" => elf::EF_AVR_ARCH_AVR5,
+        "atmega644a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega644p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega644pa" => elf::EF_AVR_ARCH_AVR5,
+        "atmega645" => elf::EF_AVR_ARCH_AVR5,
+        "atmega645a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega645p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega649" => elf::EF_AVR_ARCH_AVR5,
+        "atmega649a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega649p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega6450" => elf::EF_AVR_ARCH_AVR5,
+        "atmega6450a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega6450p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega6490" => elf::EF_AVR_ARCH_AVR5,
+        "atmega6490a" => elf::EF_AVR_ARCH_AVR5,
+        "atmega6490p" => elf::EF_AVR_ARCH_AVR5,
+        "atmega64rfr2" => elf::EF_AVR_ARCH_AVR5,
+        "atmega644rfr2" => elf::EF_AVR_ARCH_AVR5,
+        "atmega16hva" => elf::EF_AVR_ARCH_AVR5,
+        "atmega16hva2" => elf::EF_AVR_ARCH_AVR5,
+        "atmega16hvb" => elf::EF_AVR_ARCH_AVR5,
+        "atmega16hvbrevb" => elf::EF_AVR_ARCH_AVR5,
+        "atmega32hvb" => elf::EF_AVR_ARCH_AVR5,
+        "atmega32hvbrevb" => elf::EF_AVR_ARCH_AVR5,
+        "atmega64hve" => elf::EF_AVR_ARCH_AVR5,
+        "atmega64hve2" => elf::EF_AVR_ARCH_AVR5,
+        "at90can32" => elf::EF_AVR_ARCH_AVR5,
+        "at90can64" => elf::EF_AVR_ARCH_AVR5,
+        "at90pwm161" => elf::EF_AVR_ARCH_AVR5,
+        "at90pwm216" => elf::EF_AVR_ARCH_AVR5,
+        "at90pwm316" => elf::EF_AVR_ARCH_AVR5,
+        "atmega32c1" => elf::EF_AVR_ARCH_AVR5,
+        "atmega64c1" => elf::EF_AVR_ARCH_AVR5,
+        "atmega16m1" => elf::EF_AVR_ARCH_AVR5,
+        "atmega32m1" => elf::EF_AVR_ARCH_AVR5,
+        "atmega64m1" => elf::EF_AVR_ARCH_AVR5,
+        "atmega16u4" => elf::EF_AVR_ARCH_AVR5,
+        "atmega32u4" => elf::EF_AVR_ARCH_AVR5,
+        "atmega32u6" => elf::EF_AVR_ARCH_AVR5,
+        "at90usb646" => elf::EF_AVR_ARCH_AVR5,
+        "at90usb647" => elf::EF_AVR_ARCH_AVR5,
+        "at90scr100" => elf::EF_AVR_ARCH_AVR5,
+        "at94k" => elf::EF_AVR_ARCH_AVR5,
+        "m3000" => elf::EF_AVR_ARCH_AVR5,
+        "atmega128" => elf::EF_AVR_ARCH_AVR51,
+        "atmega128a" => elf::EF_AVR_ARCH_AVR51,
+        "atmega1280" => elf::EF_AVR_ARCH_AVR51,
+        "atmega1281" => elf::EF_AVR_ARCH_AVR51,
+        "atmega1284" => elf::EF_AVR_ARCH_AVR51,
+        "atmega1284p" => elf::EF_AVR_ARCH_AVR51,
+        "atmega128rfa1" => elf::EF_AVR_ARCH_AVR51,
+        "atmega128rfr2" => elf::EF_AVR_ARCH_AVR51,
+        "atmega1284rfr2" => elf::EF_AVR_ARCH_AVR51,
+        "at90can128" => elf::EF_AVR_ARCH_AVR51,
+        "at90usb1286" => elf::EF_AVR_ARCH_AVR51,
+        "at90usb1287" => elf::EF_AVR_ARCH_AVR51,
+        "atmega2560" => elf::EF_AVR_ARCH_AVR6,
+        "atmega2561" => elf::EF_AVR_ARCH_AVR6,
+        "atmega256rfr2" => elf::EF_AVR_ARCH_AVR6,
+        "atmega2564rfr2" => elf::EF_AVR_ARCH_AVR6,
+        "atxmega16a4" => elf::EF_AVR_ARCH_XMEGA2,
+        "atxmega16a4u" => elf::EF_AVR_ARCH_XMEGA2,
+        "atxmega16c4" => elf::EF_AVR_ARCH_XMEGA2,
+        "atxmega16d4" => elf::EF_AVR_ARCH_XMEGA2,
+        "atxmega32a4" => elf::EF_AVR_ARCH_XMEGA2,
+        "atxmega32a4u" => elf::EF_AVR_ARCH_XMEGA2,
+        "atxmega32c3" => elf::EF_AVR_ARCH_XMEGA2,
+        "atxmega32c4" => elf::EF_AVR_ARCH_XMEGA2,
+        "atxmega32d3" => elf::EF_AVR_ARCH_XMEGA2,
+        "atxmega32d4" => elf::EF_AVR_ARCH_XMEGA2,
+        "atxmega32e5" => elf::EF_AVR_ARCH_XMEGA2,
+        "atxmega16e5" => elf::EF_AVR_ARCH_XMEGA2,
+        "atxmega8e5" => elf::EF_AVR_ARCH_XMEGA2,
+        "atxmega64a3" => elf::EF_AVR_ARCH_XMEGA4,
+        "atxmega64a3u" => elf::EF_AVR_ARCH_XMEGA4,
+        "atxmega64a4u" => elf::EF_AVR_ARCH_XMEGA4,
+        "atxmega64b1" => elf::EF_AVR_ARCH_XMEGA4,
+        "atxmega64b3" => elf::EF_AVR_ARCH_XMEGA4,
+        "atxmega64c3" => elf::EF_AVR_ARCH_XMEGA4,
+        "atxmega64d3" => elf::EF_AVR_ARCH_XMEGA4,
+        "atxmega64d4" => elf::EF_AVR_ARCH_XMEGA4,
+        "atxmega64a1" => elf::EF_AVR_ARCH_XMEGA5,
+        "atxmega64a1u" => elf::EF_AVR_ARCH_XMEGA5,
+        "atxmega128a3" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega128a3u" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega128b1" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega128b3" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega128c3" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega128d3" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega128d4" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega192a3" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega192a3u" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega192c3" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega192d3" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega256a3" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega256a3u" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega256a3b" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega256a3bu" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega256c3" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega256d3" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega384c3" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega384d3" => elf::EF_AVR_ARCH_XMEGA6,
+        "atxmega128a1" => elf::EF_AVR_ARCH_XMEGA7,
+        "atxmega128a1u" => elf::EF_AVR_ARCH_XMEGA7,
+        "atxmega128a4u" => elf::EF_AVR_ARCH_XMEGA7,
+        "attiny4" => elf::EF_AVR_ARCH_AVRTINY,
+        "attiny5" => elf::EF_AVR_ARCH_AVRTINY,
+        "attiny9" => elf::EF_AVR_ARCH_AVRTINY,
+        "attiny10" => elf::EF_AVR_ARCH_AVRTINY,
+        "attiny20" => elf::EF_AVR_ARCH_AVRTINY,
+        "attiny40" => elf::EF_AVR_ARCH_AVRTINY,
+        "attiny102" => elf::EF_AVR_ARCH_AVRTINY,
+        "attiny104" => elf::EF_AVR_ARCH_AVRTINY,
+        "attiny202" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny402" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny204" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny404" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny804" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny1604" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny406" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny806" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny1606" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny807" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny1607" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny212" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny412" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny214" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny414" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny814" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny1614" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny416" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny816" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny1616" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny3216" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny417" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny817" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny1617" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny3217" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny1624" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny1626" => elf::EF_AVR_ARCH_XMEGA3,
+        "attiny1627" => elf::EF_AVR_ARCH_XMEGA3,
+        "atmega808" => elf::EF_AVR_ARCH_XMEGA3,
+        "atmega809" => elf::EF_AVR_ARCH_XMEGA3,
+        "atmega1608" => elf::EF_AVR_ARCH_XMEGA3,
+        "atmega1609" => elf::EF_AVR_ARCH_XMEGA3,
+        "atmega3208" => elf::EF_AVR_ARCH_XMEGA3,
+        "atmega3209" => elf::EF_AVR_ARCH_XMEGA3,
+        "atmega4808" => elf::EF_AVR_ARCH_XMEGA3,
+        "atmega4809" => elf::EF_AVR_ARCH_XMEGA3,
+
+        // Unknown target CPU => Unspecified/generic code
+        _ => 0,
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/bpf.rs b/compiler/rustc_target/src/spec/base/bpf.rs
new file mode 100644
index 00000000000..4d03747d016
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/bpf.rs
@@ -0,0 +1,29 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, TargetOptions};
+
+pub fn opts(endian: Endian) -> TargetOptions {
+    TargetOptions {
+        allow_asm: true,
+        endian,
+        linker_flavor: LinkerFlavor::Bpf,
+        atomic_cas: false,
+        dynamic_linking: true,
+        no_builtins: true,
+        panic_strategy: PanicStrategy::Abort,
+        position_independent_executables: true,
+        // Disable MergeFunctions since:
+        // - older kernels don't support bpf-to-bpf calls
+        // - on newer kernels, userspace still needs to relocate before calling
+        //   BPF_PROG_LOAD and not all BPF libraries do that yet
+        merge_functions: MergeFunctions::Disabled,
+        obj_is_bitcode: true,
+        requires_lto: false,
+        singlethread: true,
+        // When targeting the `v3` cpu in llvm, 32-bit atomics are also supported.
+        // But making this value change based on the target cpu can be mostly confusing
+        // and would require a bit of a refactor.
+        min_atomic_width: Some(64),
+        max_atomic_width: Some(64),
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/dragonfly.rs b/compiler/rustc_target/src/spec/base/dragonfly.rs
new file mode 100644
index 00000000000..de2be781796
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/dragonfly.rs
@@ -0,0 +1,14 @@
+use crate::spec::{cvs, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "dragonfly".into(),
+        dynamic_linking: true,
+        families: cvs!["unix"],
+        has_rpath: true,
+        position_independent_executables: true,
+        relro_level: RelroLevel::Full,
+        default_dwarf_version: 2,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/freebsd.rs b/compiler/rustc_target/src/spec/base/freebsd.rs
new file mode 100644
index 00000000000..8c141aaaec3
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/freebsd.rs
@@ -0,0 +1,15 @@
+use crate::spec::{cvs, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "freebsd".into(),
+        dynamic_linking: true,
+        families: cvs!["unix"],
+        has_rpath: true,
+        position_independent_executables: true,
+        relro_level: RelroLevel::Full,
+        abi_return_struct_as_int: true,
+        default_dwarf_version: 2,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/fuchsia.rs b/compiler/rustc_target/src/spec/base/fuchsia.rs
new file mode 100644
index 00000000000..4c2775850d1
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/fuchsia.rs
@@ -0,0 +1,43 @@
+use crate::spec::{crt_objects, cvs, Cc, LinkOutputKind, LinkerFlavor, Lld, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    // This mirrors the linker options provided by clang. We presume lld for
+    // now. When using clang as the linker it will supply these options for us,
+    // so we only list them for ld/lld.
+    //
+    // https://github.com/llvm/llvm-project/blob/db9322b2066c55254e7691efeab863f43bfcc084/clang/lib/Driver/ToolChains/Fuchsia.cpp#L31
+    let pre_link_args = TargetOptions::link_args(
+        LinkerFlavor::Gnu(Cc::No, Lld::No),
+        &[
+            "--build-id",
+            "--hash-style=gnu",
+            "-z",
+            "max-page-size=4096",
+            "-z",
+            "now",
+            "-z",
+            "rodynamic",
+            "-z",
+            "separate-loadable-segments",
+            "--pack-dyn-relocs=relr",
+        ],
+    );
+
+    TargetOptions {
+        os: "fuchsia".into(),
+        linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
+        linker: Some("rust-lld".into()),
+        dynamic_linking: true,
+        families: cvs!["unix"],
+        pre_link_args,
+        pre_link_objects: crt_objects::new(&[
+            (LinkOutputKind::DynamicNoPicExe, &["Scrt1.o"]),
+            (LinkOutputKind::DynamicPicExe, &["Scrt1.o"]),
+            (LinkOutputKind::StaticNoPicExe, &["Scrt1.o"]),
+            (LinkOutputKind::StaticPicExe, &["Scrt1.o"]),
+        ]),
+        position_independent_executables: true,
+        has_thread_local: true,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/haiku.rs b/compiler/rustc_target/src/spec/base/haiku.rs
new file mode 100644
index 00000000000..8ab874410aa
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/haiku.rs
@@ -0,0 +1,11 @@
+use crate::spec::{cvs, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "haiku".into(),
+        dynamic_linking: true,
+        families: cvs!["unix"],
+        relro_level: RelroLevel::Full,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/hermit.rs b/compiler/rustc_target/src/spec/base/hermit.rs
new file mode 100644
index 00000000000..c6e98fc1a11
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/hermit.rs
@@ -0,0 +1,15 @@
+use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, TargetOptions, TlsModel};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "hermit".into(),
+        linker: Some("rust-lld".into()),
+        linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
+        tls_model: TlsModel::InitialExec,
+        position_independent_executables: true,
+        static_position_independent_executables: true,
+        has_thread_local: true,
+        panic_strategy: PanicStrategy::Abort,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/hurd.rs b/compiler/rustc_target/src/spec/base/hurd.rs
new file mode 100644
index 00000000000..76f8223c0e4
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/hurd.rs
@@ -0,0 +1,15 @@
+use crate::spec::{cvs, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "hurd".into(),
+        dynamic_linking: true,
+        families: cvs!["unix"],
+        has_rpath: true,
+        position_independent_executables: true,
+        relro_level: RelroLevel::Full,
+        has_thread_local: true,
+        crt_static_respected: true,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/hurd_gnu.rs b/compiler/rustc_target/src/spec/base/hurd_gnu.rs
new file mode 100644
index 00000000000..3501dbdea1b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/hurd_gnu.rs
@@ -0,0 +1,5 @@
+use crate::spec::{base, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions { env: "gnu".into(), ..base::hurd::opts() }
+}
diff --git a/compiler/rustc_target/src/spec/base/illumos.rs b/compiler/rustc_target/src/spec/base/illumos.rs
new file mode 100644
index 00000000000..e63e789752b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/illumos.rs
@@ -0,0 +1,59 @@
+use crate::spec::{cvs, Cc, FramePointer, LinkerFlavor, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    let late_link_args = TargetOptions::link_args(
+        LinkerFlavor::Unix(Cc::Yes),
+        &[
+            // The illumos libc contains a stack unwinding implementation, as
+            // does libgcc_s. The latter implementation includes several
+            // additional symbols that are not always in base libc. To force
+            // the consistent use of just one unwinder, we ensure libc appears
+            // after libgcc_s in the NEEDED list for the resultant binary by
+            // ignoring any attempts to add it as a dynamic dependency until the
+            // very end.
+            // FIXME: This should be replaced by a more complete and generic
+            // mechanism for controlling the order of library arguments passed
+            // to the linker.
+            "-lc",
+            // LLVM will insert calls to the stack protector functions
+            // "__stack_chk_fail" and "__stack_chk_guard" into code in native
+            // object files. Some platforms include these symbols directly in
+            // libc, but at least historically these have been provided in
+            // libssp.so on illumos and Solaris systems.
+            "-lssp",
+        ],
+    );
+
+    TargetOptions {
+        os: "illumos".into(),
+        dynamic_linking: true,
+        has_rpath: true,
+        families: cvs!["unix"],
+        is_like_solaris: true,
+        linker_flavor: LinkerFlavor::Unix(Cc::Yes),
+        limit_rdylib_exports: false, // Linker doesn't support this
+        frame_pointer: FramePointer::Always,
+        eh_frame_header: false,
+        late_link_args,
+
+        // While we support ELF TLS, rust requires a way to register
+        // cleanup handlers (in C, this would be something along the lines of:
+        // void register_callback(void (*fn)(void *), void *arg);
+        // (see src/libstd/sys/unix/fast_thread_local.rs) that is currently
+        // missing in illumos. For now at least, we must fallback to using
+        // pthread_{get,set}specific.
+        //has_thread_local: true,
+
+        // FIXME: Currently, rust is invoking cc to link, which ends up
+        // causing these to get included twice. We should eventually transition
+        // to having rustc invoke ld directly, in which case these will need to
+        // be uncommented.
+        //
+        // We want XPG6 behavior from libc and libm. See standards(5)
+        //pre_link_objects_exe: vec![
+        //    "/usr/lib/amd64/values-Xc.o".into(),
+        //    "/usr/lib/amd64/values-xpg6.o".into(),
+        //],
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/l4re.rs b/compiler/rustc_target/src/spec/base/l4re.rs
new file mode 100644
index 00000000000..3a4d83fad17
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/l4re.rs
@@ -0,0 +1,14 @@
+use crate::spec::{cvs, Cc, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "l4re".into(),
+        env: "uclibc".into(),
+        linker_flavor: LinkerFlavor::Unix(Cc::No),
+        panic_strategy: PanicStrategy::Abort,
+        linker: Some("l4-bender".into()),
+        families: cvs!["unix"],
+        relocation_model: RelocModel::Static,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/linux.rs b/compiler/rustc_target/src/spec/base/linux.rs
new file mode 100644
index 00000000000..df8e848124a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/linux.rs
@@ -0,0 +1,21 @@
+use crate::spec::{cvs, RelroLevel, SplitDebuginfo, TargetOptions};
+use std::borrow::Cow;
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "linux".into(),
+        dynamic_linking: true,
+        families: cvs!["unix"],
+        has_rpath: true,
+        position_independent_executables: true,
+        relro_level: RelroLevel::Full,
+        has_thread_local: true,
+        crt_static_respected: true,
+        supported_split_debuginfo: Cow::Borrowed(&[
+            SplitDebuginfo::Packed,
+            SplitDebuginfo::Unpacked,
+            SplitDebuginfo::Off,
+        ]),
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/linux_gnu.rs b/compiler/rustc_target/src/spec/base/linux_gnu.rs
new file mode 100644
index 00000000000..3491277c440
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/linux_gnu.rs
@@ -0,0 +1,5 @@
+use crate::spec::{base, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions { env: "gnu".into(), ..base::linux::opts() }
+}
diff --git a/compiler/rustc_target/src/spec/base/linux_musl.rs b/compiler/rustc_target/src/spec/base/linux_musl.rs
new file mode 100644
index 00000000000..5117cadbee0
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/linux_musl.rs
@@ -0,0 +1,16 @@
+use crate::spec::crt_objects;
+use crate::spec::{base, LinkSelfContainedDefault, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    let mut base = base::linux::opts();
+
+    base.env = "musl".into();
+    base.pre_link_objects_self_contained = crt_objects::pre_musl_self_contained();
+    base.post_link_objects_self_contained = crt_objects::post_musl_self_contained();
+    base.link_self_contained = LinkSelfContainedDefault::InferredForMusl;
+
+    // These targets statically link libc by default
+    base.crt_static_default = true;
+
+    base
+}
diff --git a/compiler/rustc_target/src/spec/base/linux_ohos.rs b/compiler/rustc_target/src/spec/base/linux_ohos.rs
new file mode 100644
index 00000000000..273e6a98dd4
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/linux_ohos.rs
@@ -0,0 +1,12 @@
+use crate::spec::{base, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    let mut base = base::linux::opts();
+
+    base.env = "ohos".into();
+    base.crt_static_default = false;
+    base.force_emulated_tls = true;
+    base.has_thread_local = false;
+
+    base
+}
diff --git a/compiler/rustc_target/src/spec/base/linux_uclibc.rs b/compiler/rustc_target/src/spec/base/linux_uclibc.rs
new file mode 100644
index 00000000000..540344e9e94
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/linux_uclibc.rs
@@ -0,0 +1,5 @@
+use crate::spec::{base, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions { env: "uclibc".into(), ..base::linux::opts() }
+}
diff --git a/compiler/rustc_target/src/spec/base/mod.rs b/compiler/rustc_target/src/spec/base/mod.rs
new file mode 100644
index 00000000000..d137aaa5358
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/mod.rs
@@ -0,0 +1,37 @@
+pub(crate) mod aix;
+pub(crate) mod android;
+pub(crate) mod apple;
+pub(crate) mod avr_gnu;
+pub(crate) mod bpf;
+pub(crate) mod dragonfly;
+pub(crate) mod freebsd;
+pub(crate) mod fuchsia;
+pub(crate) mod haiku;
+pub(crate) mod hermit;
+pub(crate) mod hurd;
+pub(crate) mod hurd_gnu;
+pub(crate) mod illumos;
+pub(crate) mod l4re;
+pub(crate) mod linux;
+pub(crate) mod linux_gnu;
+pub(crate) mod linux_musl;
+pub(crate) mod linux_ohos;
+pub(crate) mod linux_uclibc;
+pub(crate) mod msvc;
+pub(crate) mod netbsd;
+pub(crate) mod nto_qnx;
+pub(crate) mod openbsd;
+pub(crate) mod redox;
+pub(crate) mod solaris;
+pub(crate) mod solid;
+pub(crate) mod teeos;
+pub(crate) mod thumb;
+pub(crate) mod uefi_msvc;
+pub(crate) mod unikraft_linux_musl;
+pub(crate) mod vxworks;
+pub(crate) mod wasm;
+pub(crate) mod windows_gnu;
+pub(crate) mod windows_gnullvm;
+pub(crate) mod windows_msvc;
+pub(crate) mod windows_uwp_gnu;
+pub(crate) mod windows_uwp_msvc;
diff --git a/compiler/rustc_target/src/spec/base/msvc.rs b/compiler/rustc_target/src/spec/base/msvc.rs
new file mode 100644
index 00000000000..efe949a4e90
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/msvc.rs
@@ -0,0 +1,26 @@
+use crate::spec::{DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions};
+use std::borrow::Cow;
+
+pub fn opts() -> TargetOptions {
+    // Suppress the verbose logo and authorship debugging output, which would needlessly
+    // clog any log files.
+    let pre_link_args = TargetOptions::link_args(LinkerFlavor::Msvc(Lld::No), &["/NOLOGO"]);
+
+    TargetOptions {
+        linker_flavor: LinkerFlavor::Msvc(Lld::No),
+        dll_tls_export: false,
+        is_like_windows: true,
+        is_like_msvc: true,
+        pre_link_args,
+        abi_return_struct_as_int: true,
+        emit_debug_gdb_scripts: false,
+
+        // Currently this is the only supported method of debuginfo on MSVC
+        // where `*.pdb` files show up next to the final artifact.
+        split_debuginfo: SplitDebuginfo::Packed,
+        supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Packed]),
+        debuginfo_kind: DebuginfoKind::Pdb,
+
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/netbsd.rs b/compiler/rustc_target/src/spec/base/netbsd.rs
new file mode 100644
index 00000000000..be94ea23465
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/netbsd.rs
@@ -0,0 +1,16 @@
+use crate::spec::{cvs, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "netbsd".into(),
+        dynamic_linking: true,
+        families: cvs!["unix"],
+        no_default_libraries: false,
+        has_rpath: true,
+        position_independent_executables: true,
+        relro_level: RelroLevel::Full,
+        use_ctors_section: true,
+        default_dwarf_version: 2,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/nto_qnx.rs b/compiler/rustc_target/src/spec/base/nto_qnx.rs
new file mode 100644
index 00000000000..f1405e9b446
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/nto_qnx.rs
@@ -0,0 +1,18 @@
+use crate::spec::{cvs, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        crt_static_respected: true,
+        dynamic_linking: true,
+        executables: true,
+        families: cvs!["unix"],
+        has_rpath: true,
+        has_thread_local: false,
+        linker: Some("qcc".into()),
+        os: "nto".into(),
+        position_independent_executables: true,
+        static_position_independent_executables: true,
+        relro_level: RelroLevel::Full,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/openbsd.rs b/compiler/rustc_target/src/spec/base/openbsd.rs
new file mode 100644
index 00000000000..e7db14e05a4
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/openbsd.rs
@@ -0,0 +1,16 @@
+use crate::spec::{cvs, FramePointer, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "openbsd".into(),
+        dynamic_linking: true,
+        families: cvs!["unix"],
+        has_rpath: true,
+        abi_return_struct_as_int: true,
+        position_independent_executables: true,
+        frame_pointer: FramePointer::Always, // FIXME 43575: should be MayOmit...
+        relro_level: RelroLevel::Full,
+        default_dwarf_version: 2,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/redox.rs b/compiler/rustc_target/src/spec/base/redox.rs
new file mode 100644
index 00000000000..468fe478549
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/redox.rs
@@ -0,0 +1,17 @@
+use crate::spec::{cvs, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "redox".into(),
+        env: "relibc".into(),
+        dynamic_linking: true,
+        families: cvs!["unix"],
+        has_rpath: true,
+        position_independent_executables: true,
+        relro_level: RelroLevel::Full,
+        has_thread_local: true,
+        crt_static_default: true,
+        crt_static_respected: true,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/solaris.rs b/compiler/rustc_target/src/spec/base/solaris.rs
new file mode 100644
index 00000000000..f97cdb4fb28
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/solaris.rs
@@ -0,0 +1,16 @@
+use crate::spec::{cvs, Cc, LinkerFlavor, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "solaris".into(),
+        dynamic_linking: true,
+        has_rpath: true,
+        families: cvs!["unix"],
+        is_like_solaris: true,
+        linker_flavor: LinkerFlavor::Unix(Cc::Yes),
+        limit_rdylib_exports: false, // Linker doesn't support this
+        eh_frame_header: false,
+
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/solid.rs b/compiler/rustc_target/src/spec/base/solid.rs
new file mode 100644
index 00000000000..ce4a8869001
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/solid.rs
@@ -0,0 +1,12 @@
+use crate::spec::{FramePointer, TargetOptions};
+
+pub fn opts(kernel: &str) -> TargetOptions {
+    TargetOptions {
+        os: format!("solid_{kernel}").into(),
+        vendor: "kmc".into(),
+        executables: false,
+        frame_pointer: FramePointer::NonLeaf,
+        has_thread_local: true,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/teeos.rs b/compiler/rustc_target/src/spec/base/teeos.rs
new file mode 100644
index 00000000000..38d0a6d7314
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/teeos.rs
@@ -0,0 +1,28 @@
+use crate::spec::{add_link_args, Cc, LinkerFlavor, Lld, PanicStrategy, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    let lld_args = &["-zmax-page-size=4096", "-znow", "-ztext", "--execute-only"];
+    let cc_args = &["-Wl,-zmax-page-size=4096", "-Wl,-znow", "-Wl,-ztext", "-mexecute-only"];
+
+    let mut pre_link_args = TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), lld_args);
+    add_link_args(&mut pre_link_args, LinkerFlavor::Gnu(Cc::Yes, Lld::No), cc_args);
+
+    TargetOptions {
+        os: "teeos".into(),
+        vendor: "unknown".into(),
+        dynamic_linking: true,
+        linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No),
+        // rpath hardcodes -Wl, so it can't be used together with ld.lld.
+        // C TAs also don't support rpath, so this is fine.
+        has_rpath: false,
+        // Note: Setting has_thread_local to true causes an error when
+        // loading / dyn-linking the TA
+        has_thread_local: false,
+        position_independent_executables: true,
+        relro_level: RelroLevel::Full,
+        crt_static_respected: true,
+        pre_link_args,
+        panic_strategy: PanicStrategy::Abort,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/thumb.rs b/compiler/rustc_target/src/spec/base/thumb.rs
new file mode 100644
index 00000000000..0decfecb4cd
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/thumb.rs
@@ -0,0 +1,59 @@
+// These `thumbv*` targets cover the ARM Cortex-M family of processors which are widely used in
+// microcontrollers. Namely, all these processors:
+//
+// - Cortex-M0
+// - Cortex-M0+
+// - Cortex-M1
+// - Cortex-M3
+// - Cortex-M4(F)
+// - Cortex-M7(F)
+// - Cortex-M23
+// - Cortex-M33
+//
+// We have opted for these instead of one target per processor (e.g., `cortex-m0`, `cortex-m3`,
+// etc) because the differences between some processors like the cortex-m0 and cortex-m1 are almost
+// nonexistent from the POV of codegen so it doesn't make sense to have separate targets for them.
+// And if differences exist between two processors under the same target, rustc flags can be used to
+// optimize for one processor or the other.
+//
+// Also, we have not chosen a single target (`arm-none-eabi`) like GCC does because this makes
+// difficult to integrate Rust code and C code. Targeting the Cortex-M4 requires different gcc flags
+// than the ones you would use for the Cortex-M0 and with a single target it'd be impossible to
+// differentiate one processor from the other.
+//
+// About arm vs thumb in the name. The Cortex-M devices only support the Thumb instruction set,
+// which is more compact (higher code density), and not the ARM instruction set. That's why LLVM
+// triples use thumb instead of arm. We follow suit because having thumb in the name let us
+// differentiate these targets from our other `arm(v7)-*-*-gnueabi(hf)` targets in the context of
+// build scripts / gcc flags.
+
+use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, PanicStrategy, RelocModel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    // See rust-lang/rfcs#1645 for a discussion about these defaults
+    TargetOptions {
+        linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
+        // In most cases, LLD is good enough
+        linker: Some("rust-lld".into()),
+        // Because these devices have very little resources having an unwinder is too onerous so we
+        // default to "abort" because the "unwind" strategy is very rare.
+        panic_strategy: PanicStrategy::Abort,
+        // Similarly, one almost always never wants to use relocatable code because of the extra
+        // costs it involves.
+        relocation_model: RelocModel::Static,
+        // When this section is added a volatile load to its start address is also generated. This
+        // volatile load is a footgun as it can end up loading an invalid memory address, depending
+        // on how the user set up their linker scripts. This section adds pretty printer for stuff
+        // like std::Vec, which is not that used in no-std context, so it's best to left it out
+        // until we figure a way to add the pretty printers without requiring a volatile load cf.
+        // rust-lang/rust#44993.
+        emit_debug_gdb_scripts: false,
+        // LLVM is eager to trash the link register when calling `noreturn` functions, which
+        // breaks debugging. Preserve LR by default to prevent that from happening.
+        frame_pointer: FramePointer::Always,
+        // ARM supports multiple ABIs for enums, the linux one matches the default of 32 here
+        // but any arm-none or thumb-none target will be defaulted to 8 on GCC.
+        c_enum_min_bits: Some(8),
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/uefi_msvc.rs b/compiler/rustc_target/src/spec/base/uefi_msvc.rs
new file mode 100644
index 00000000000..e8acd6078e2
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/uefi_msvc.rs
@@ -0,0 +1,52 @@
+// 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::{base, LinkerFlavor, Lld, PanicStrategy, StackProbeType, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    let mut base = base::msvc::opts();
+
+    base.add_pre_link_args(
+        LinkerFlavor::Msvc(Lld::No),
+        &[
+            // 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",
+            // 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",
+        ],
+    );
+
+    TargetOptions {
+        os: "uefi".into(),
+        linker_flavor: LinkerFlavor::Msvc(Lld::Yes),
+        disable_redzone: true,
+        exe_suffix: ".efi".into(),
+        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".into()),
+        entry_name: "efi_main".into(),
+        ..base
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/unikraft_linux_musl.rs b/compiler/rustc_target/src/spec/base/unikraft_linux_musl.rs
new file mode 100644
index 00000000000..9ccd0a1e7ca
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/unikraft_linux_musl.rs
@@ -0,0 +1,15 @@
+use crate::spec::{cvs, PanicStrategy, RelocModel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "linux".into(),
+        env: "musl".into(),
+        vendor: "unikraft".into(),
+        linker: Some("kraftld".into()),
+        relocation_model: RelocModel::Static,
+        families: cvs!["unix"],
+        has_thread_local: true,
+        panic_strategy: PanicStrategy::Abort,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/vxworks.rs b/compiler/rustc_target/src/spec/base/vxworks.rs
new file mode 100644
index 00000000000..aa4784b63e7
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/vxworks.rs
@@ -0,0 +1,21 @@
+use crate::spec::{cvs, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "vxworks".into(),
+        env: "gnu".into(),
+        vendor: "wrs".into(),
+        linker: Some("wr-c++".into()),
+        exe_suffix: ".vxe".into(),
+        dynamic_linking: true,
+        families: cvs!["unix"],
+        has_rpath: true,
+        has_thread_local: true,
+        crt_static_default: true,
+        crt_static_respected: true,
+        crt_static_allows_dylibs: true,
+        // VxWorks needs to implement this to support profiling
+        mcount: "_mcount".into(),
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/wasm.rs b/compiler/rustc_target/src/spec/base/wasm.rs
new file mode 100644
index 00000000000..87ade9e58cf
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/wasm.rs
@@ -0,0 +1,135 @@
+use crate::spec::{
+    add_link_args, cvs, Cc, LinkSelfContainedDefault, LinkerFlavor, PanicStrategy, RelocModel,
+    TargetOptions, TlsModel,
+};
+
+pub fn options() -> TargetOptions {
+    macro_rules! args {
+        ($prefix:literal) => {
+            &[
+                // By default LLD only gives us one page of stack (64k) which is a
+                // little small. Default to a larger stack closer to other PC platforms
+                // (1MB) and users can always inject their own link-args to override this.
+                concat!($prefix, "-z"),
+                concat!($prefix, "stack-size=1048576"),
+                // By default LLD's memory layout is:
+                //
+                // 1. First, a blank page
+                // 2. Next, all static data
+                // 3. Finally, the main stack (which grows down)
+                //
+                // This has the unfortunate consequence that on stack overflows you
+                // corrupt static data and can cause some exceedingly weird bugs. To
+                // help detect this a little sooner we instead request that the stack is
+                // placed before static data.
+                //
+                // This means that we'll generate slightly larger binaries as references
+                // to static data will take more bytes in the ULEB128 encoding, but
+                // stack overflow will be guaranteed to trap as it underflows instead of
+                // corrupting static data.
+                concat!($prefix, "--stack-first"),
+                // FIXME we probably shouldn't pass this but instead pass an explicit list
+                // of symbols we'll allow to be undefined. We don't currently have a
+                // mechanism of knowing, however, which symbols are intended to be imported
+                // from the environment and which are intended to be imported from other
+                // objects linked elsewhere. This is a coarse approximation but is sure to
+                // hide some bugs and frustrate someone at some point, so we should ideally
+                // work towards a world where we can explicitly list symbols that are
+                // supposed to be imported and have all other symbols generate errors if
+                // they remain undefined.
+                concat!($prefix, "--allow-undefined"),
+                // Rust code should never have warnings, and warnings are often
+                // indicative of bugs, let's prevent them.
+                concat!($prefix, "--fatal-warnings"),
+                // LLD only implements C++-like demangling, which doesn't match our own
+                // mangling scheme. Tell LLD to not demangle anything and leave it up to
+                // us to demangle these symbols later. Currently rustc does not perform
+                // further demangling, but tools like twiggy and wasm-bindgen are intended
+                // to do so.
+                concat!($prefix, "--no-demangle"),
+            ]
+        };
+    }
+
+    let mut pre_link_args = TargetOptions::link_args(LinkerFlavor::WasmLld(Cc::No), args!(""));
+    add_link_args(&mut pre_link_args, LinkerFlavor::WasmLld(Cc::Yes), args!("-Wl,"));
+
+    TargetOptions {
+        is_like_wasm: true,
+        families: cvs!["wasm"],
+
+        // we allow dynamic linking, but only cdylibs. Basically we allow a
+        // final library artifact that exports some symbols (a wasm module) but
+        // we don't allow intermediate `dylib` crate types
+        dynamic_linking: true,
+        only_cdylib: true,
+
+        // relatively self-explanatory!
+        exe_suffix: ".wasm".into(),
+        dll_prefix: "".into(),
+        dll_suffix: ".wasm".into(),
+        eh_frame_header: false,
+
+        max_atomic_width: Some(64),
+
+        // Unwinding doesn't work right now, so the whole target unconditionally
+        // defaults to panic=abort. Note that this is guaranteed to change in
+        // the future once unwinding is implemented. Don't rely on this as we're
+        // basically guaranteed to change it once WebAssembly supports
+        // exceptions.
+        panic_strategy: PanicStrategy::Abort,
+
+        // Wasm doesn't have atomics yet, so tell LLVM that we're in a single
+        // threaded model which will legalize atomics to normal operations.
+        singlethread: true,
+
+        // no dynamic linking, no need for default visibility!
+        default_hidden_visibility: true,
+
+        // Symbol visibility takes care of this for the WebAssembly.
+        // Additionally the only known linker, LLD, doesn't support the script
+        // arguments just yet
+        limit_rdylib_exports: false,
+
+        // we use the LLD shipped with the Rust toolchain by default
+        linker: Some("rust-lld".into()),
+        linker_flavor: LinkerFlavor::WasmLld(Cc::No),
+
+        pre_link_args,
+
+        // FIXME: Figure out cases in which WASM needs to link with a native toolchain.
+        //
+        // rust-lang/rust#104137: cannot blindly remove this without putting in
+        // some other way to compensate for lack of `-nostartfiles` in linker
+        // invocation.
+        link_self_contained: LinkSelfContainedDefault::True,
+
+        // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when
+        // PIC code is implemented this has quite a drastic effect if it stays
+        // at the default, `pic`. In an effort to keep wasm binaries as minimal
+        // as possible we're defaulting to `static` for now, but the hope is
+        // that eventually we can ship a `pic`-compatible standard library which
+        // works with `static` as well (or works with some method of generating
+        // non-relative calls and such later on).
+        relocation_model: RelocModel::Static,
+
+        // When the atomics feature is activated then these two keys matter,
+        // otherwise they're basically ignored by the standard library. In this
+        // mode, however, the `#[thread_local]` attribute works (i.e.
+        // `has_thread_local`) and we need to get it to work by specifying
+        // `local-exec` as that's all that's implemented in LLVM today for wasm.
+        has_thread_local: true,
+        tls_model: TlsModel::LocalExec,
+
+        // gdb scripts don't work on wasm blobs
+        emit_debug_gdb_scripts: false,
+
+        // There's more discussion of this at
+        // https://bugs.llvm.org/show_bug.cgi?id=52442 but the general result is
+        // that this isn't useful for wasm and has tricky issues with
+        // representation, so this is disabled.
+        generate_arange_section: false,
+
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/windows_gnu.rs b/compiler/rustc_target/src/spec/base/windows_gnu.rs
new file mode 100644
index 00000000000..25f02dc1451
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/windows_gnu.rs
@@ -0,0 +1,108 @@
+use crate::spec::LinkSelfContainedDefault;
+use crate::spec::{add_link_args, crt_objects};
+use crate::spec::{cvs, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions};
+use std::borrow::Cow;
+
+pub fn opts() -> TargetOptions {
+    let mut pre_link_args = TargetOptions::link_args(
+        LinkerFlavor::Gnu(Cc::No, Lld::No),
+        &[
+            // Enable ASLR
+            "--dynamicbase",
+            // ASLR will rebase it anyway so leaving that option enabled only leads to confusion
+            "--disable-auto-image-base",
+        ],
+    );
+    add_link_args(
+        &mut pre_link_args,
+        LinkerFlavor::Gnu(Cc::Yes, Lld::No),
+        &[
+            // Tell GCC to avoid linker plugins, because we are not bundling
+            // them with Windows installer, and Rust does its own LTO anyways.
+            "-fno-use-linker-plugin",
+            "-Wl,--dynamicbase",
+            "-Wl,--disable-auto-image-base",
+        ],
+    );
+
+    // Order of `late_link_args*` was found through trial and error to work with various
+    // mingw-w64 versions (not tested on the CI). It's expected to change from time to time.
+    let mingw_libs = &[
+        "-lmsvcrt",
+        "-lmingwex",
+        "-lmingw32",
+        "-lgcc", // alas, mingw* libraries above depend on libgcc
+        // mingw's msvcrt is a weird hybrid import library and static library.
+        // And it seems that the linker fails to use import symbols from msvcrt
+        // that are required from functions in msvcrt in certain cases. For example
+        // `_fmode` that is used by an implementation of `__p__fmode` in x86_64.
+        // The library is purposely listed twice to fix that.
+        //
+        // See https://github.com/rust-lang/rust/pull/47483 for some more details.
+        "-lmsvcrt",
+        "-luser32",
+        "-lkernel32",
+    ];
+    let mut late_link_args =
+        TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), mingw_libs);
+    add_link_args(&mut late_link_args, LinkerFlavor::Gnu(Cc::Yes, Lld::No), mingw_libs);
+    // If any of our crates are dynamically linked then we need to use
+    // the shared libgcc_s-dw2-1.dll. This is required to support
+    // unwinding across DLL boundaries.
+    let dynamic_unwind_libs = &["-lgcc_s"];
+    let mut late_link_args_dynamic =
+        TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), dynamic_unwind_libs);
+    add_link_args(
+        &mut late_link_args_dynamic,
+        LinkerFlavor::Gnu(Cc::Yes, Lld::No),
+        dynamic_unwind_libs,
+    );
+    // If all of our crates are statically linked then we can get away
+    // with statically linking the libgcc unwinding code. This allows
+    // binaries to be redistributed without the libgcc_s-dw2-1.dll
+    // dependency, but unfortunately break unwinding across DLL
+    // boundaries when unwinding across FFI boundaries.
+    let static_unwind_libs = &["-lgcc_eh", "-l:libpthread.a"];
+    let mut late_link_args_static =
+        TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), static_unwind_libs);
+    add_link_args(
+        &mut late_link_args_static,
+        LinkerFlavor::Gnu(Cc::Yes, Lld::No),
+        static_unwind_libs,
+    );
+
+    TargetOptions {
+        os: "windows".into(),
+        env: "gnu".into(),
+        vendor: "pc".into(),
+        // FIXME(#13846) this should be enabled for windows
+        function_sections: false,
+        linker: Some("gcc".into()),
+        dynamic_linking: true,
+        dll_tls_export: false,
+        dll_prefix: "".into(),
+        dll_suffix: ".dll".into(),
+        exe_suffix: ".exe".into(),
+        families: cvs!["windows"],
+        is_like_windows: true,
+        allows_weak_linkage: false,
+        pre_link_args,
+        pre_link_objects: crt_objects::pre_mingw(),
+        post_link_objects: crt_objects::post_mingw(),
+        pre_link_objects_self_contained: crt_objects::pre_mingw_self_contained(),
+        post_link_objects_self_contained: crt_objects::post_mingw_self_contained(),
+        link_self_contained: LinkSelfContainedDefault::InferredForMingw,
+        late_link_args,
+        late_link_args_dynamic,
+        late_link_args_static,
+        abi_return_struct_as_int: true,
+        emit_debug_gdb_scripts: false,
+        requires_uwtable: true,
+        eh_frame_header: false,
+        // FIXME(davidtwco): Support Split DWARF on Windows GNU - may require LLVM changes to
+        // output DWO, despite using DWARF, doesn't use ELF..
+        debuginfo_kind: DebuginfoKind::Pdb,
+        supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/windows_gnullvm.rs b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs
new file mode 100644
index 00000000000..b1d8e2be5a6
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs
@@ -0,0 +1,47 @@
+use crate::spec::{cvs, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions};
+use std::borrow::Cow;
+
+pub fn opts() -> TargetOptions {
+    // We cannot use `-nodefaultlibs` because compiler-rt has to be passed
+    // as a path since it's not added to linker search path by the default.
+    // There were attempts to make it behave like libgcc (so one can just use -l<name>)
+    // but LLVM maintainers rejected it: https://reviews.llvm.org/D51440
+    let pre_link_args = TargetOptions::link_args(
+        LinkerFlavor::Gnu(Cc::Yes, Lld::No),
+        &["-nolibc", "--unwindlib=none"],
+    );
+    // Order of `late_link_args*` does not matter with LLD.
+    let late_link_args = TargetOptions::link_args(
+        LinkerFlavor::Gnu(Cc::Yes, Lld::No),
+        &["-lmingw32", "-lmingwex", "-lmsvcrt", "-lkernel32", "-luser32"],
+    );
+
+    TargetOptions {
+        os: "windows".into(),
+        env: "gnu".into(),
+        vendor: "pc".into(),
+        abi: "llvm".into(),
+        linker: Some("clang".into()),
+        dynamic_linking: true,
+        dll_tls_export: false,
+        dll_prefix: "".into(),
+        dll_suffix: ".dll".into(),
+        exe_suffix: ".exe".into(),
+        families: cvs!["windows"],
+        is_like_windows: true,
+        allows_weak_linkage: false,
+        pre_link_args,
+        late_link_args,
+        abi_return_struct_as_int: true,
+        emit_debug_gdb_scripts: false,
+        requires_uwtable: true,
+        eh_frame_header: false,
+        no_default_libraries: false,
+        has_thread_local: true,
+        // FIXME(davidtwco): Support Split DWARF on Windows GNU - may require LLVM changes to
+        // output DWO, despite using DWARF, doesn't use ELF..
+        debuginfo_kind: DebuginfoKind::Pdb,
+        supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/windows_msvc.rs b/compiler/rustc_target/src/spec/base/windows_msvc.rs
new file mode 100644
index 00000000000..e3cf9757219
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/windows_msvc.rs
@@ -0,0 +1,34 @@
+use crate::spec::{base, cvs, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    let base = base::msvc::opts();
+
+    TargetOptions {
+        os: "windows".into(),
+        env: "msvc".into(),
+        vendor: "pc".into(),
+        dynamic_linking: true,
+        dll_prefix: "".into(),
+        dll_suffix: ".dll".into(),
+        exe_suffix: ".exe".into(),
+        staticlib_prefix: "".into(),
+        staticlib_suffix: ".lib".into(),
+        families: cvs!["windows"],
+        crt_static_allows_dylibs: true,
+        crt_static_respected: true,
+        requires_uwtable: true,
+        // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC
+        // as there's been trouble in the past of linking the C++ standard
+        // library required by LLVM. This likely needs to happen one day, but
+        // in general Windows is also a more controlled environment than
+        // Unix, so it's not necessarily as critical that this be implemented.
+        //
+        // Note that there are also some licensing worries about statically
+        // linking some libraries which require a specific agreement, so it may
+        // not ever be possible for us to pass this flag.
+        no_default_libraries: false,
+        has_thread_local: true,
+
+        ..base
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/windows_uwp_gnu.rs b/compiler/rustc_target/src/spec/base/windows_uwp_gnu.rs
new file mode 100644
index 00000000000..17256e18e24
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/windows_uwp_gnu.rs
@@ -0,0 +1,35 @@
+use crate::spec::{add_link_args, base, Cc, LinkArgs, LinkerFlavor, Lld, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    let base = base::windows_gnu::opts();
+
+    // FIXME: This should be updated for the exception machinery changes from #67502
+    // and inherit from `windows_gnu_base`, at least partially.
+    let mingw_libs = &[
+        "-lwinstorecompat",
+        "-lruntimeobject",
+        "-lsynchronization",
+        "-lvcruntime140_app",
+        "-lucrt",
+        "-lwindowsapp",
+        "-lmingwex",
+        "-lmingw32",
+    ];
+    let mut late_link_args =
+        TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), mingw_libs);
+    add_link_args(&mut late_link_args, LinkerFlavor::Gnu(Cc::Yes, Lld::No), mingw_libs);
+    // Reset the flags back to empty until the FIXME above is addressed.
+    let late_link_args_dynamic = LinkArgs::new();
+    let late_link_args_static = LinkArgs::new();
+
+    TargetOptions {
+        abi: "uwp".into(),
+        vendor: "uwp".into(),
+        limit_rdylib_exports: false,
+        late_link_args,
+        late_link_args_dynamic,
+        late_link_args_static,
+
+        ..base
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/windows_uwp_msvc.rs b/compiler/rustc_target/src/spec/base/windows_uwp_msvc.rs
new file mode 100644
index 00000000000..59a76167125
--- /dev/null
+++ b/compiler/rustc_target/src/spec/base/windows_uwp_msvc.rs
@@ -0,0 +1,11 @@
+use crate::spec::{base, LinkerFlavor, Lld, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    let mut opts = base::windows_msvc::opts();
+
+    opts.abi = "uwp".into();
+    opts.vendor = "uwp".into();
+    opts.add_pre_link_args(LinkerFlavor::Msvc(Lld::No), &["/APPCONTAINER", "mincore.lib"]);
+
+    opts
+}