-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
macOS: Crate symbols get discarded when crate appears unused #133491
Comments
hey @madsmtm What do you think we have wrought upon ourselves today? |
Fair chance this is because of linkers only pulling in object files from archives when a symbol in them is referenced unless |
I'd be surprised if |
It appears that |
Does |
|
I think the issue is that the If I use |
And I'll see if I can figure out a way to make the |
I've run out of time for now, but might continue work on this in perhaps a few days, perhaps weeks. Noting down my findings in the meantime: This can be reproduced with just a library crate Running // dep.rs
#![crate_type = "rlib"]
#![no_std]
extern "C" {
fn printf(format: *const core::ffi::c_char, ...) -> i32;
}
#[used]
#[link_section = "__DATA,__mod_init_func,mod_init_funcs"]
#[no_mangle]
pub static INIT: extern "C" fn() = init;
pub extern "C" fn init() {
unsafe { printf(b"inside initializer\n\0".as_ptr().cast()) };
}
#[no_mangle]
pub fn foo() {} // main.rs
#![no_std]
#![feature(start)]
extern crate dep;
#[link(name = "System")]
extern "C" {}
extern "Rust" {
fn foo();
}
#[panic_handler]
fn handle(_: &core::panic::PanicInfo<'_>) -> ! {
loop {}
}
extern "C" {
fn printf(format: *const core::ffi::c_char, ...) -> i32;
}
#[start]
fn main(a: isize, l: *const *const u8) -> isize {
// dep::foo();
// foo();
unsafe { printf(b"in main\n\0".as_ptr().cast()) };
return 0;
} Adding I've managed to produce a // helper.rs
#![crate_type = "lib"]
#![no_std]
extern "Rust" {
fn foo();
}
pub fn use_foo() {
unsafe { foo() };
} The difference between that and Hypotheses:
|
I managed to (basically) re-create the I have put up #133832, with that PR, simply doing |
Make `#[used]` work when linking with `ld64` To make `#[used]` work in static libraries, we use the `symbols.o` trick introduced in rust-lang#95604. However, the linker shipped with Xcode, ld64, works a bit differently from other linkers; in particular, [it completely ignores undefined symbols by themselves](https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/macho_relocatable_file.cpp#L2455-L2468), and only consider them if they have relocations (something something atoms something fixups, I don't know the details). So to make the `symbols.o` file work on ld64, we need to actually insert a relocation. That's kinda cumbersome to do though, since the relocation must be valid, and hence must point to a valid piece of machine code, and is hence very architecture-specific. Fixes rust-lang#133491, see that for investigation. --- Another option would be to pass `-u _foo` to the final linker invocation. This has the problem that `-u` causes the linker to not be able to dead-strip the symbol, which is undesirable. (If we did this, we would possibly also want to do it by putting the arguments in a file by itself, and passing that file via ``@`,` e.g. ``@undefined_symbols.txt`,` similar to rust-lang#52699, though that [is only supported since Xcode 12](https://developer.apple.com/documentation/xcode-release-notes/xcode-12-release-notes#Linking), and I'm not sure we wanna bump that). Various other options that are probably all undesirable as they affect link time performance: - Pass `-all_load` to the linker. - Pass `-ObjC` to the linker (the Objective-C support in the linker has different code paths that load more of the binary), and instrument the binaries that contain `#[used]` symbols. - Pass `-force_load` to libraries that contain `#[used]` symbols. Failed attempt: Embed `-u _foo` in the object file with `LC_LINKER_OPTION`, akin to rust-lang#121293. Doesn't work, both because `ld64` doesn't read that from archive members unless it already has a reason to load the member (which is what this PR is trying to make it do), and because `ld64` only support the `-l`, `-needed-l`, `-framework` and `-needed_framework` flags in there. --- TODO: - [x] Support all Apple architectures. - [x] Ensure that this works regardless of the actual type of the symbol. - [x] Write up more docs. - [x] Wire up a few proper tests. `@rustbot` label O-apple
Make `#[used]` work when linking with `ld64` To make `#[used]` work in static libraries, we use the `symbols.o` trick introduced in rust-lang#95604. However, the linker shipped with Xcode, ld64, works a bit differently from other linkers; in particular, [it completely ignores undefined symbols by themselves](https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/macho_relocatable_file.cpp#L2455-L2468), and only consider them if they have relocations (something something atoms something fixups, I don't know the details). So to make the `symbols.o` file work on ld64, we need to actually insert a relocation. That's kinda cumbersome to do though, since the relocation must be valid, and hence must point to a valid piece of machine code, and is hence very architecture-specific. Fixes rust-lang#133491, see that for investigation. --- Another option would be to pass `-u _foo` to the final linker invocation. This has the problem that `-u` causes the linker to not be able to dead-strip the symbol, which is undesirable. (If we did this, we would possibly also want to do it by putting the arguments in a file by itself, and passing that file via ``@`,` e.g. ``@undefined_symbols.txt`,` similar to rust-lang#52699, though that [is only supported since Xcode 12](https://developer.apple.com/documentation/xcode-release-notes/xcode-12-release-notes#Linking), and I'm not sure we wanna bump that). Various other options that are probably all undesirable as they affect link time performance: - Pass `-all_load` to the linker. - Pass `-ObjC` to the linker (the Objective-C support in the linker has different code paths that load more of the binary), and instrument the binaries that contain `#[used]` symbols. - Pass `-force_load` to libraries that contain `#[used]` symbols. Failed attempt: Embed `-u _foo` in the object file with `LC_LINKER_OPTION`, akin to rust-lang#121293. Doesn't work, both because `ld64` doesn't read that from archive members unless it already has a reason to load the member (which is what this PR is trying to make it do), and because `ld64` only support the `-l`, `-needed-l`, `-framework` and `-needed_framework` flags in there. --- TODO: - [x] Support all Apple architectures. - [x] Ensure that this works regardless of the actual type of the symbol. - [x] Write up more docs. - [x] Wire up a few proper tests. `@rustbot` label O-apple
I tried this code:
Within the base crate, the
#[divan::bench]
macro is used for benchmarking crate internals. Its generated code translates to something similar to:This crate has a benchmark executable with only:
I expected to see this happen: run the
add
benchmark and output the results. This runs correctly on Linux and Windows, but not macOS.Instead, this happened:
divan::main()
cannot find the benchmark because the pre-main
constructor function never ran.In order to make the benchmarks visible to the executable, the base crate needs to have one of its items appear to be used, such as
black_box
-ing any item from the crate.Note that this also happens with code like:
This issue occurs even when doing
extern crate my_crate
oruse my_crate as _
, which is a known workaround for similar issues.Meta
rustc --version --verbose
:The text was updated successfully, but these errors were encountered: