Skip to content

Commit 0653e4c

Browse files
committed
Make #[used] work when linking with ld64
1 parent 2061630 commit 0653e4c

File tree

4 files changed

+121
-7
lines changed

4 files changed

+121
-7
lines changed

Diff for: compiler/rustc_codegen_ssa/src/back/apple.rs

+81
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,87 @@ pub(super) fn macho_platform(target: &Target) -> u32 {
2626
}
2727
}
2828

29+
/// Add relocation and section data needed for a symbol to be considered
30+
/// undefined by ld64.
31+
///
32+
/// Inherently very architecture-specific (unfortunately).
33+
///
34+
/// # New architectures
35+
///
36+
/// The values here can be found by compiling the following program:
37+
///
38+
/// ```c
39+
/// void foo(void);
40+
///
41+
/// void use_foo() {
42+
/// foo();
43+
/// }
44+
/// ```
45+
///
46+
/// With:
47+
///
48+
/// ```console
49+
/// $ clang -c foo.c -O2 -g0 -fno-exceptions -target $CLANG_TARGET
50+
/// ```
51+
///
52+
/// And then inspecting with `objdump -d foo.o` and/or:
53+
///
54+
/// ```rust,ignore
55+
/// //! ```cargo
56+
/// //! [dependencies]
57+
/// //! object = "0.36"
58+
/// //! ```
59+
///
60+
/// use object::read::macho::{MachHeader, MachOFile};
61+
/// use object::{File, Object, ObjectSection};
62+
///
63+
/// fn read(file: MachOFile<'_, impl MachHeader>) {
64+
/// for section in file.sections() {
65+
/// dbg!(section.name().unwrap(), section.data().unwrap(), section.align());
66+
/// for reloc in section.relocations() {
67+
/// dbg!(reloc);
68+
/// }
69+
/// }
70+
/// }
71+
///
72+
/// fn main() {
73+
/// match File::parse(&*std::fs::read("foo.o").unwrap()).unwrap() {
74+
/// File::MachO32(file) => read(file),
75+
/// File::MachO64(file) => read(file),
76+
/// _ => unimplemented!(),
77+
/// }
78+
/// }
79+
/// ```
80+
pub(super) fn add_data_and_relocation(
81+
file: &mut object::write::Object<'_>,
82+
section: object::write::SectionId,
83+
symbol: object::write::SymbolId,
84+
target: &Target,
85+
) -> object::write::Result<()> {
86+
let (data, align, addend, r_type): (&[u8], _, _, _) = match &*target.arch {
87+
"arm" => (&[0xff, 0xf7, 0xfe, 0xbf], 2, 0, object::macho::ARM_THUMB_RELOC_BR22),
88+
"aarch64" => (&[0, 0, 0, 0x14], 4, 0, object::macho::ARM64_RELOC_BRANCH26),
89+
"x86_64" => (
90+
&[0x55, 0x48, 0x89, 0xe5, 0x5d, 0xe9, 0, 0, 0, 0],
91+
16,
92+
-4,
93+
object::macho::X86_64_RELOC_BRANCH,
94+
),
95+
"x86" => (&[0x55, 0x89, 0xe5, 0x5d, 0xe9, 0xf7, 0xff, 0xff, 0xff], 16, -4, 0),
96+
arch => unimplemented!("unsupported Apple architecture {arch:?}"),
97+
};
98+
99+
let offset = file.section_mut(section).append_data(data, align);
100+
file.add_relocation(section, object::write::Relocation {
101+
offset,
102+
addend,
103+
symbol,
104+
flags: object::write::RelocationFlags::MachO { r_type, r_pcrel: true, r_length: 2 },
105+
})?;
106+
107+
Ok(())
108+
}
109+
29110
/// Deployment target or SDK version.
30111
///
31112
/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`.

Diff for: compiler/rustc_codegen_ssa/src/back/link.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -2070,8 +2070,20 @@ fn add_linked_symbol_object(
20702070
file.set_mangling(object::write::Mangling::None);
20712071
}
20722072

2073+
// ld64 requires a relocation to load undefined symbols, see below.
2074+
// Not strictly needed if linking with lld, but might as well do it there too.
2075+
let ld64_section_helper = if file.format() == object::BinaryFormat::MachO {
2076+
Some(file.add_section(
2077+
file.segment_name(object::write::StandardSegment::Text).to_vec(),
2078+
"__text".into(),
2079+
object::SectionKind::Text,
2080+
))
2081+
} else {
2082+
None
2083+
};
2084+
20732085
for (sym, kind) in symbols.iter() {
2074-
file.add_symbol(object::write::Symbol {
2086+
let symbol = file.add_symbol(object::write::Symbol {
20752087
name: sym.clone().into(),
20762088
value: 0,
20772089
size: 0,
@@ -2085,6 +2097,21 @@ fn add_linked_symbol_object(
20852097
section: object::write::SymbolSection::Undefined,
20862098
flags: object::SymbolFlags::None,
20872099
});
2100+
2101+
// The linker shipped with Apple's Xcode, ld64, works a bit differently from other linkers.
2102+
// In particular, it completely ignores undefined symbols by themselves, and only consider
2103+
// undefined symbols if they have relocations.
2104+
//
2105+
// So to make this trick work on ld64, we need to actually insert a relocation. The
2106+
// relocation must be valid though, and hence must point to a valid piece of machine code,
2107+
// and hence this is all very architecture-specific.
2108+
//
2109+
// See the following for a few details on the design of ld64:
2110+
// https://github.com/apple-oss-distributions/ld64/blob/ld64-951.9/doc/design/linker.html
2111+
if let Some(section) = ld64_section_helper {
2112+
apple::add_data_and_relocation(&mut file, section, symbol, &sess.target)
2113+
.expect("failed adding relocation");
2114+
}
20882115
}
20892116

20902117
let path = tmpdir.join("symbols.o");

Diff for: tests/run-make/include-all-symbols-linking/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod foo {
2-
#[link_section = ".rodata.STATIC"]
2+
#[cfg_attr(target_os = "linux", link_section = ".rodata.STATIC")]
3+
#[cfg_attr(target_vendor = "apple", link_section = "__DATA,STATIC")]
34
#[used]
45
static STATIC: [u32; 10] = [1; 10];
56
}

Diff for: tests/run-make/include-all-symbols-linking/rmake.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,20 @@
77
// See https://github.com/rust-lang/rust/pull/95604
88
// See https://github.com/rust-lang/rust/issues/47384
99

10-
//@ only-linux
11-
// Reason: differences in object file formats on OSX and Windows
12-
// causes errors in the llvm_objdump step
10+
//@ ignore-windows differences in object file formats causes errors in the llvm_objdump step.
11+
//@ ignore-wasm differences in object file formats causes errors in the llvm_objdump step.
1312

14-
use run_make_support::{dynamic_lib_name, llvm_objdump, llvm_readobj, rustc};
13+
use run_make_support::{dynamic_lib_name, llvm_objdump, llvm_readobj, rustc, target};
1514

1615
fn main() {
1716
rustc().crate_type("lib").input("lib.rs").run();
18-
rustc().crate_type("cdylib").link_args("-Tlinker.ld").input("main.rs").run();
17+
let mut main = rustc();
18+
main.crate_type("cdylib");
19+
if target().contains("linux") {
20+
main.link_args("-Tlinker.ld");
21+
}
22+
main.input("main.rs").run();
23+
1924
// Ensure `#[used]` and `KEEP`-ed section is there
2025
llvm_objdump()
2126
.arg("--full-contents")

0 commit comments

Comments
 (0)