diff --git a/compiler/rustc_codegen_ssa/src/back/apple.rs b/compiler/rustc_codegen_ssa/src/back/apple.rs index d9c5c3e5af96d..4b63813eb622e 100644 --- a/compiler/rustc_codegen_ssa/src/back/apple.rs +++ b/compiler/rustc_codegen_ssa/src/back/apple.rs @@ -26,6 +26,87 @@ pub(super) fn macho_platform(target: &Target) -> u32 { } } +/// Add relocation and section data needed for a symbol to be considered +/// undefined by ld64. +/// +/// Inherently very architecture-specific (unfortunately). +/// +/// # New architectures +/// +/// The values here can be found by compiling the following program: +/// +/// ```c +/// void foo(void); +/// +/// void use_foo() { +/// foo(); +/// } +/// ``` +/// +/// With: +/// +/// ```console +/// $ clang -c foo.c -O2 -g0 -fno-exceptions -target $CLANG_TARGET +/// ``` +/// +/// And then inspecting with `objdump -d foo.o` and/or: +/// +/// ```rust,ignore +/// //! ```cargo +/// //! [dependencies] +/// //! object = "0.36" +/// //! ``` +/// +/// use object::read::macho::{MachHeader, MachOFile}; +/// use object::{File, Object, ObjectSection}; +/// +/// fn read(file: MachOFile<'_, impl MachHeader>) { +/// for section in file.sections() { +/// dbg!(section.name().unwrap(), section.data().unwrap(), section.align()); +/// for reloc in section.relocations() { +/// dbg!(reloc); +/// } +/// } +/// } +/// +/// fn main() { +/// match File::parse(&*std::fs::read("foo.o").unwrap()).unwrap() { +/// File::MachO32(file) => read(file), +/// File::MachO64(file) => read(file), +/// _ => unimplemented!(), +/// } +/// } +/// ``` +pub(super) fn add_data_and_relocation( + file: &mut object::write::Object<'_>, + section: object::write::SectionId, + symbol: object::write::SymbolId, + target: &Target, +) -> object::write::Result<()> { + let (data, align, addend, r_type): (&[u8], _, _, _) = match &*target.arch { + "arm" => (&[0xff, 0xf7, 0xfe, 0xbf], 2, 0, object::macho::ARM_THUMB_RELOC_BR22), + "aarch64" => (&[0, 0, 0, 0x14], 4, 0, object::macho::ARM64_RELOC_BRANCH26), + "x86_64" => ( + &[0x55, 0x48, 0x89, 0xe5, 0x5d, 0xe9, 0, 0, 0, 0], + 16, + -4, + object::macho::X86_64_RELOC_BRANCH, + ), + "x86" => (&[0x55, 0x89, 0xe5, 0x5d, 0xe9, 0xf7, 0xff, 0xff, 0xff], 16, -4, 0), + arch => unimplemented!("unsupported Apple architecture {arch:?}"), + }; + + let offset = file.section_mut(section).append_data(data, align); + file.add_relocation(section, object::write::Relocation { + offset, + addend, + symbol, + flags: object::write::RelocationFlags::MachO { r_type, r_pcrel: true, r_length: 2 }, + })?; + + 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 4bc064528f3f0..3ece122e334a3 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2070,8 +2070,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::Text).to_vec(), + "__text".into(), + object::SectionKind::Text, + )) + } 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, @@ -2085,6 +2097,21 @@ 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. + // In particular, it completely ignores undefined symbols by themselves, and only consider + // undefined symbols if they have relocations. + // + // So to make this trick work on ld64, we need to actually insert a relocation. The + // relocation must be valid though, and hence must point to a valid piece of machine code, + // and hence this is all very architecture-specific. + // + // See the following for a few details on the design of ld64: + // https://github.com/apple-oss-distributions/ld64/blob/ld64-951.9/doc/design/linker.html + if let Some(section) = ld64_section_helper { + apple::add_data_and_relocation(&mut file, section, symbol, &sess.target) + .expect("failed adding relocation"); + } } let path = tmpdir.join("symbols.o"); diff --git a/tests/run-make/include-all-symbols-linking/lib.rs b/tests/run-make/include-all-symbols-linking/lib.rs index 99508bcdaf314..73186ee99e3d9 100644 --- a/tests/run-make/include-all-symbols-linking/lib.rs +++ b/tests/run-make/include-all-symbols-linking/lib.rs @@ -1,5 +1,6 @@ mod foo { - #[link_section = ".rodata.STATIC"] + #[cfg_attr(target_os = "linux", link_section = ".rodata.STATIC")] + #[cfg_attr(target_vendor = "apple", link_section = "__DATA,STATIC")] #[used] static STATIC: [u32; 10] = [1; 10]; } diff --git a/tests/run-make/include-all-symbols-linking/rmake.rs b/tests/run-make/include-all-symbols-linking/rmake.rs index 77fd71ab20d21..777c942600966 100644 --- a/tests/run-make/include-all-symbols-linking/rmake.rs +++ b/tests/run-make/include-all-symbols-linking/rmake.rs @@ -7,15 +7,20 @@ // See https://github.com/rust-lang/rust/pull/95604 // See https://github.com/rust-lang/rust/issues/47384 -//@ only-linux -// Reason: differences in object file formats on OSX and Windows -// causes errors in the llvm_objdump step +//@ ignore-windows differences in object file formats causes errors in the llvm_objdump step. +//@ ignore-wasm differences in object file formats causes errors in the llvm_objdump step. -use run_make_support::{dynamic_lib_name, llvm_objdump, llvm_readobj, rustc}; +use run_make_support::{dynamic_lib_name, llvm_objdump, llvm_readobj, rustc, target}; fn main() { rustc().crate_type("lib").input("lib.rs").run(); - rustc().crate_type("cdylib").link_args("-Tlinker.ld").input("main.rs").run(); + let mut main = rustc(); + main.crate_type("cdylib"); + if target().contains("linux") { + main.link_args("-Tlinker.ld"); + } + main.input("main.rs").run(); + // Ensure `#[used]` and `KEEP`-ed section is there llvm_objdump() .arg("--full-contents")