about summary refs log tree commit diff
path: root/compiler/rustc_codegen_ssa
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_ssa')
-rw-r--r--compiler/rustc_codegen_ssa/src/back/apple.rs84
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs59
2 files changed, 140 insertions, 3 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/apple.rs b/compiler/rustc_codegen_ssa/src/back/apple.rs
index d9c5c3e5af9..bfa7635a869 100644
--- a/compiler/rustc_codegen_ssa/src/back/apple.rs
+++ b/compiler/rustc_codegen_ssa/src/back/apple.rs
@@ -2,6 +2,7 @@ use std::env;
 use std::fmt::{Display, from_fn};
 use std::num::ParseIntError;
 
+use rustc_middle::middle::exported_symbols::SymbolExportKind;
 use rustc_session::Session;
 use rustc_target::spec::Target;
 
@@ -26,6 +27,89 @@ pub(super) fn macho_platform(target: &Target) -> u32 {
     }
 }
 
+/// Add relocation and section data needed for a symbol to be considered
+/// undefined by ld64.
+///
+/// The relocation must be valid, and hence must point to a valid piece of
+/// machine code, and hence this is unfortunately very architecture-specific.
+///
+///
+/// # New architectures
+///
+/// The values here are basically the same as emitted by the following program:
+///
+/// ```c
+/// // clang -c foo.c -target $CLANG_TARGET
+/// void foo(void);
+///
+/// extern int bar;
+///
+/// void* foobar[2] = {
+///     (void*)foo,
+///     (void*)&bar,
+///     // ...
+/// };
+/// ```
+///
+/// Can be inspected with:
+/// ```console
+/// objdump --macho --reloc foo.o
+/// objdump --macho --full-contents foo.o
+/// ```
+pub(super) fn add_data_and_relocation(
+    file: &mut object::write::Object<'_>,
+    section: object::write::SectionId,
+    symbol: object::write::SymbolId,
+    target: &Target,
+    kind: SymbolExportKind,
+) -> object::write::Result<()> {
+    let authenticated_pointer =
+        kind == SymbolExportKind::Text && target.llvm_target.starts_with("arm64e");
+
+    let data: &[u8] = match target.pointer_width {
+        _ if authenticated_pointer => &[0, 0, 0, 0, 0, 0, 0, 0x80],
+        32 => &[0; 4],
+        64 => &[0; 8],
+        pointer_width => unimplemented!("unsupported Apple pointer width {pointer_width:?}"),
+    };
+
+    if target.arch == "x86_64" {
+        // Force alignment for the entire section to be 16 on x86_64.
+        file.section_mut(section).append_data(&[], 16);
+    } else {
+        // Elsewhere, the section alignment is the same as the pointer width.
+        file.section_mut(section).append_data(&[], target.pointer_width as u64);
+    }
+
+    let offset = file.section_mut(section).append_data(data, data.len() as u64);
+
+    let flags = if authenticated_pointer {
+        object::write::RelocationFlags::MachO {
+            r_type: object::macho::ARM64_RELOC_AUTHENTICATED_POINTER,
+            r_pcrel: false,
+            r_length: 3,
+        }
+    } else if target.arch == "arm" {
+        // FIXME(madsmtm): Remove once `object` supports 32-bit ARM relocations:
+        // https://github.com/gimli-rs/object/pull/757
+        object::write::RelocationFlags::MachO {
+            r_type: object::macho::ARM_RELOC_VANILLA,
+            r_pcrel: false,
+            r_length: 2,
+        }
+    } else {
+        object::write::RelocationFlags::Generic {
+            kind: object::RelocationKind::Absolute,
+            encoding: object::RelocationEncoding::Generic,
+            size: target.pointer_width as u8,
+        }
+    };
+
+    file.add_relocation(section, object::write::Relocation { offset, addend: 0, symbol, flags })?;
+
+    Ok(())
+}
+
 /// Deployment target or SDK version.
 ///
 /// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`.
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 4c076450269..b090730ac6b 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -2058,8 +2058,8 @@ fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor
 /// linker, and since they never participate in the linking, using `KEEP` in the linker scripts
 /// can't keep them either. This causes #47384.
 ///
-/// To keep them around, we could use `--whole-archive` and equivalents to force rlib to
-/// participate in linking like object files, but this proves to be expensive (#93791). Therefore
+/// To keep them around, we could use `--whole-archive`, `-force_load` and equivalents to force rlib
+/// to participate in linking like object files, but this proves to be expensive (#93791). Therefore
 /// we instead just introduce an undefined reference to them. This could be done by `-u` command
 /// line option to the linker or `EXTERN(...)` in linker scripts, however they does not only
 /// introduce an undefined reference, but also make them the GC roots, preventing `--gc-sections`
@@ -2101,8 +2101,20 @@ fn add_linked_symbol_object(
         file.set_mangling(object::write::Mangling::None);
     }
 
+    // ld64 requires a relocation to load undefined symbols, see below.
+    // Not strictly needed if linking with lld, but might as well do it there too.
+    let ld64_section_helper = if file.format() == object::BinaryFormat::MachO {
+        Some(file.add_section(
+            file.segment_name(object::write::StandardSegment::Data).to_vec(),
+            "__data".into(),
+            object::SectionKind::Data,
+        ))
+    } else {
+        None
+    };
+
     for (sym, kind) in symbols.iter() {
-        file.add_symbol(object::write::Symbol {
+        let symbol = file.add_symbol(object::write::Symbol {
             name: sym.clone().into(),
             value: 0,
             size: 0,
@@ -2116,6 +2128,47 @@ fn add_linked_symbol_object(
             section: object::write::SymbolSection::Undefined,
             flags: object::SymbolFlags::None,
         });
+
+        // The linker shipped with Apple's Xcode, ld64, works a bit differently from other linkers.
+        //
+        // Code-wise, the relevant parts of ld64 are roughly:
+        // 1. Find the `ArchiveLoadMode` based on commandline options, default to `parseObjects`.
+        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/Options.cpp#L924-L932
+        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/Options.h#L55
+        //
+        // 2. Read the archive table of contents (__.SYMDEF file).
+        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/archive_file.cpp#L294-L325
+        //
+        // 3. Begin linking by loading "atoms" from input files.
+        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/doc/design/linker.html
+        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/InputFiles.cpp#L1349
+        //
+        //   a. Directly specified object files (`.o`) are parsed immediately.
+        //      https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/macho_relocatable_file.cpp#L4611-L4627
+        //
+        //     - Undefined symbols are not atoms (`n_value > 0` denotes a common symbol).
+        //       https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/macho_relocatable_file.cpp#L2455-L2468
+        //       https://maskray.me/blog/2022-02-06-all-about-common-symbols
+        //
+        //     - Relocations/fixups are atoms.
+        //       https://github.com/apple-oss-distributions/ld64/blob/ce6341ae966b3451aa54eeb049f2be865afbd578/src/ld/parsers/macho_relocatable_file.cpp#L2088-L2114
+        //
+        //   b. Archives are not parsed yet.
+        //      https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/archive_file.cpp#L467-L577
+        //
+        // 4. When a symbol is needed by an atom, parse the object file that contains the symbol.
+        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/InputFiles.cpp#L1417-L1491
+        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/archive_file.cpp#L579-L597
+        //
+        // All of the steps above are fairly similar to other linkers, except that **it completely
+        // ignores undefined symbols**.
+        //
+        // So to make this trick work on ld64, we need to do something else to load the relevant
+        // object files. We do this by inserting a relocation (fixup) for each symbol.
+        if let Some(section) = ld64_section_helper {
+            apple::add_data_and_relocation(&mut file, section, symbol, &sess.target, *kind)
+                .expect("failed adding relocation");
+        }
     }
 
     let path = tmpdir.join("symbols.o");