12 releases

✓ Uses Rust 2018 edition

new 0.1.11 Sep 20, 2019
0.1.10 Aug 14, 2019
0.1.9 Apr 12, 2019
0.1.7 Feb 3, 2019
0.1.6 Dec 30, 2018
Download history 6478/week @ 2019-06-07 5429/week @ 2019-06-14 6391/week @ 2019-06-21 6438/week @ 2019-06-28 6637/week @ 2019-07-05 8049/week @ 2019-07-12 8881/week @ 2019-07-19 8798/week @ 2019-07-26 9120/week @ 2019-08-02 9290/week @ 2019-08-09 12944/week @ 2019-08-16 11442/week @ 2019-08-23 11241/week @ 2019-08-30 12365/week @ 2019-09-06 12383/week @ 2019-09-13

41,485 downloads per month
Used in 185 crates (5 directly)

Apache-2.0 OR MIT

12KB
210 lines

rust-ctor

Build Status docs.rs crates.io

Module initialization/teardown functions for Rust (like __attribute__((constructor)) in C/C++) for Linux, OSX, and Windows.

This library currently requires Rust > 1.31.0 at a minimum for the procedural macro support.

Idea inspired by this code in the Neon project.

Support

This library works and has been tested for Linux, OSX and Windows, with both +crt-static and -crt-static. This library will also work as expected in both bin and cdylib outputs, ie: the ctor and dtor will run at executable or library startup/shutdown respectively.

Warnings

Rust's philosophy is that nothing happens before or after main and this library explicitly subverts that. The code that runs in the ctor and dtor functions should be careful to limit itself to libc functions and code that does not rely on Rust's stdlib services.

For example, using stdout in a dtor function is a guaranteed panic. Consider using the libc-print crate for output to stderr/stdout during #[ctor] and #[dtor] methods. Other issues may involve signal processing or panic handling in that early code.

In most cases, sys_common::at_exit is a better choice than #[dtor]. Caveat emptor!

On some platforms, unloading of shared libraries may not actually happen until process exit, even if explicitly unloaded. The rules for this are arcane and difficult to understand. For example, thread-local storage on OSX will affect this (see this comment).

Examples

Marks the function foo as a module constructor, called when a static library is loaded or an executable is started:

    static INITED: AtomicBool = AtomicBool::new(false);

    #[ctor]
    fn foo() {
        INITED.store(true, Ordering::SeqCst);
    }

Creates a HashMap populated with strings when a static library is loaded or an executable is started (new in 0.1.7):

    #[ctor]
    /// This is an immutable static, evaluated at init time
    static STATIC_CTOR: HashMap<u32, &'static str> = {
        let mut m = HashMap::new();
        m.insert(0, "foo");
        m.insert(1, "bar");
        m.insert(2, "baz");
        m
    };

Print a message at shutdown time. Note that Rust may have shut down some stdlib services at this time.

    #[dtor]
    unsafe fn shutdown() {
        // Using println or eprintln here will panic as Rust has shut down
        libc::printf("Shutting down!\n\0".as_ptr() as *const i8);
    }

Under the Hood

The #[ctor] macro makes use of linker sections to ensure that a function is run at startup time.

The macro translates a marked function to the following Rust code (approximately):

    #[used]
    #[cfg_attr(target_os = "linux", link_section = ".ctors")]
    #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
    #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
    pub static foo: extern fn() = { 
        extern fn foo() { ... };
        foo 
    }

The #[dtor] macro effectively creates a constructor that calls libc::atexit with the provided function, ie roughly equivalent to:

    #[ctor]
    fn dtor_atexit() {
        libc::atexit(dtor);
    }

Dependencies

~0.7–1MB
~22K SLoC