Skip to content
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

ctor not running for statically linked libraries #27

Closed
fredericvauchelles opened this issue Sep 10, 2019 · 15 comments
Closed

ctor not running for statically linked libraries #27

fredericvauchelles opened this issue Sep 10, 2019 · 15 comments
Labels
blocked Blocked by another project bug Something isn't working help wanted Extra attention is needed

Comments

@fredericvauchelles
Copy link

Hi,

I have an issue with the following setup:

  • app crate defines the binary to produce
  • lib crate defines a rust standard library

When using #[ctor] attribute inside the crate lib, it is not called when running the binary built with app.

Using a rust library dependency statically link it so it should also include the ctor function, but it does not seems to be the case.

Am I missing something or is this an unsupported use case?

@mmastrac
Copy link
Owner

Could you confirm which version you are using (ie: paste your Cargo.toml here)?

@fredericvauchelles
Copy link
Author

I tried to have a minimal repro and investigated further, and I resulted in a behaviour I don't understand.

The repro is attached to this comment.
These are the main files:

app/src/main.rs

fn main() {
    println!("Main execution");
    #[cfg(feature = "assert")]
    assert_eq!(1, unsafe {
        lib::VALUE.load(std::sync::atomic::Ordering::Acquire)
    });
}

app/Cargo.toml

[package]
name = "app"
version = "0.1.0"
edition = "2018"

[dependencies]
lib = { path = "../lib" }

lib/src/lib.rs

#[macro_use]
extern crate ctor;
#[macro_use]
extern crate libc_print;

use std::sync::atomic::{AtomicUsize, Ordering};

pub static mut VALUE: AtomicUsize = AtomicUsize::new(0);

#[ctor]
fn startup() {
    unsafe {
        VALUE.fetch_add(1, Ordering::AcqRel);
    }
    libc_print!("Startup lib\r\n");
}

#[dtor]
fn tear_down() {
    libc_print!("Tear down lib\r\n");
}

fn unused() {
    let _ = unsafe { VALUE.load(Ordering::Acquire) };
}

lib/Cargo.toml

[package]
name = "lib"
version = "0.1.0"
authors = ["Frédéric Vauchelles <fredpointzero@gmail.com>"]
edition = "2018"

[dependencies]
libc-print = "0.1.8"
ctor = "0.1.10"

This is the strange behaviour I get:

  1. cargo run --package app --bin app will show only Main execution in the console.
  2. Comment the #[cfg(feature = "assert")] feature in the main.rs file
  3. cargo run --package app --bin app will succeed

So this is weird, I can't tell if this is working when it does not access the static variable.

Also, I have my issue when using the crate inventory in a bigger project, but I will try to have a minimal repro in that case.

cargo 1.39.0-nightly (22f7dd049 2019-08-27)
rustc 1.39.0-nightly (dfd43f0fd 2019-09-01) (nightly-x86_64-pc-windows-gnu)
rustup 1.19.0 (2af131cf9 2019-09-08)

and
cargo 1.37.0 (9edd08916 2019-08-02)
rustc 1.37.0 (eae3437df 2019-08-13) (stable-x86_64-pc-windows-msvc)
rustup 1.19.0 (2af131cf9 2019-09-08)

test3-rs.zip

@fredericvauchelles
Copy link
Author

In my project using the inventory crate, I run into the same behaviour.

The ctor and dtor of the library project are not executed until I access a static variable defined in the library from the main application.

@mmastrac
Copy link
Owner

Interesting. I wonder if this is an unused symbol getting pruned. Thanks for the repro - I'll poke around.

@mmastrac
Copy link
Owner

I confirmed this is definitely an issue. Looks like there's some sort of whole-module pruning going on.

@fredericvauchelles
Copy link
Author

Hi, do you have any updates on this?

@mmastrac
Copy link
Owner

Nothing yet. I was able to repro it with your steps, but I feel like this might be an LLVM/rustc bug.

@fredericvauchelles
Copy link
Author

Hi, do you have any updates on this? Is this an LLVM/rustc bug as you suggested?

@ebkalderon
Copy link

I can reproduce this on macOS Mojave and Rust 1.39.0 as well. However, it seems that I am able to get the constructor to run at least with the following minimal application:

//! lib.rs

#[ctor::ctor]
fn on_startup() {
    println!("Starting up!");
}

#[ctor::dtor]
unsafe fn on_shutdown() {
    libc::printf("Shutting down!\n\0".as_ptr() as *const i8);
}

pub fn unused() {}
//! main.rs

use foo::unused;

fn main() {
    unused();
    println!("Running");
}

The output produced by this application is:

Starting up!
Running

If I comment out the unused() call in main.rs, the application now produces the following output instead:

Running

I was unable to get the destructor working with the #[dtor] macro, but if you replace the definition of the foo::on_shutdown() function with this instead:

extern "C" fn on_shutdown() {
    unsafe { libc::printf("Shutting down!\n\0".as_ptr() as *const i8) };
}

And then add the following call to libc::atexit() to the foo::on_startup() constructor:

unsafe { libc::atexit(on_shutdown) };

The application now works as expected:

Starting up!
Running
Shutting down!

In short, it seems that a few tweaks to the way your application is written will get the constructor and destructor to run:

  1. Your main.rs must call at least one function or inherent struct method from lib.rs for #[ctor] to register properly. Importing static and const values in the main.rs doesn't seem to help.
  2. #[dtor] doesn't seem to work at all. Register it manually with libc::atexit() in your #[ctor] function.

I'm not sure what is going on, but I am also leaning towards the possibility of an issue with Rust or LLVM.

@Michael-F-Bryan
Copy link

I thought the whole point of the #[used] attribute (as used here was to ensure a symbol is always present in the final binary?

@vallentin
Copy link

Just encountered this issue as well.

In a debug build all 233 #[ctor]s are executed, while in a release build only 60 are being executed. Updating to Rust 1.43.1, and then 62 are executed in a release build.

Sadly, @ebkalderon solution did not fix it for me, however I'm on Windows.

It definitely feels like some sort of "whole-module pruning", as @mmastrac said, as in my case either none or all #[ctor]s in a module is executed.

@mmastrac
Copy link
Owner

Would someone have some bandwidth to file an upstream bug? This definitely seems like a Rust core issue - #[used] items should not be pruned.

@dtolnay
Copy link
Contributor

dtolnay commented May 19, 2020

rust-lang/rust#47384

@mmastrac mmastrac added the blocked Blocked by another project label Aug 31, 2021
@mmastrac mmastrac changed the title ctor not runnng for standard rust libraries statically linked ctor not running for statically linked libraries Aug 31, 2021
@mmastrac
Copy link
Owner

This may be fixed now, however I'll need someone with the issue to attempt to repro.

@mmastrac
Copy link
Owner

Marking as fixed as upstream is fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked Blocked by another project bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

6 participants