Skip to main content

Crate ctor

Crate ctor 

Source
Expand description

Build Status

The crate is part of the linktime project.

cratedocsversion
linktimedocs.rscrates.io
ctordocs.rscrates.io
dtordocs.rscrates.io
link-sectiondocs.rscrates.io

§ctor

Module initialization functions for Rust (like __attribute__((constructor)) in C/C++) for Linux, macOS, Windows, WASM, BSD-likes, and many others.

use ctor::ctor;
use libc_print::*;

#[ctor(unsafe)]
fn foo() {
    libc_println!("Life before main!");
}

§MSRV

For most platforms, this library currently has a MSRV of Rust >= 1.60.

MSRV for WASM targets is Rust >= 1.85.

§Lightweight

ctor has no dependencies other than the linktime-proc-macro and link-section crates. The proc-macro is only used to delegate to the declarative macro and should have minimal effect on compilation time.

§Support

This library works and is regularly tested on Linux, macOS, Windows, and FreeBSD, with both +crt-static and -crt-static and bin/cdylib outputs.

Contributions to support other platforms or improve testing are welcome.

OSSupportedCI Tested
Linux
macOS
Windows
FreeBSD
WASM 🕸️
NetBSD-
OpenBSD-
DragonFlyBSD-
Illumos-
Android-
iOS-
AIX-
Haiku-
VxWorks-
Xtensa-
NTO-

🕸️ = WASM wasm-unknown-unknown, wasm-wasip1, wasm-wasip2 are supported.

  • wasm-unknown-unknown requires host environment support for atexit if used with dtor.
  • wasm-wasip2 may require you to manually call __wasm_call_ctors and __wasm_call_dtors at the appropriate times.

§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.

See ::life_before_main for more information.

§Usage

#[ctor] decorates a function item to be called as a module constructor. Both free (a global fn()) and impl functions (Self::method()) are supported.

The example below marks the function foo as a module constructor, called when a static library is loaded or an executable is started:

use std::sync::atomic::{AtomicBool, Ordering};
use ctor::ctor;

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

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

Implementation methods can also be decorated with #[ctor], as long as they have no self parameter:

use ctor::ctor;

struct MyStruct {
    // ...
}

impl MyStruct {
    /// Ensure the required C library is loaded at startup time.
    #[ctor(unsafe)]
    fn load_required_c_library() {
        // ... (do something)
    }
}

§static items

The #[ctor] macro also supports decorating static items, which are initialized at startup time. static items declared in this way must not be accessed from other threads before the module constructors have run (if this is done without caution, the initializer may panic).

The below example creates a HashMap populated with strings, which would normally not be possible with const items:

use std::collections::HashMap;
use ctor::ctor;

#[ctor(unsafe)]
/// 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
};

§As a building block

The #[ctor] macro can be used as a building block for more complex initialization logic. Use the declarative::ctor to easily export macros that re-use ctor functionality.

use ctor::ctor;

trait Driver: 'static + Send + Sync {
    // ...
}

static DRIVERS: ::std::sync::Mutex<Vec<Box<dyn Driver>>> = ::std::sync::Mutex::new(Vec::new());

fn register_driver(name: &'static str, driver: impl Driver) {
    DRIVERS.lock().unwrap().push(Box::new(driver));
}

#[ctor(unsafe, priority = late)]
fn walk_drivers() {
    for driver in DRIVERS.lock().unwrap().iter() {
        // ...
    }
}

macro_rules! register_driver {
    ($name:expr, $driver:expr) => {
        $crate::ctor::declarative::ctor!(
            #[ctor(unsafe, anonymous, priority = 1)]
            fn register() {
                register_driver($name, $driver);
            }
        );
    };
}

struct MyDriver {
    // ...
}

impl Driver for MyDriver {
    // ...
}

register_driver!("my_driver", MyDriver {});

§Under the Hood

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

The above example translates into the following Rust code (approximately):

#[used]
#[cfg_attr(target_os = "linux", link_section = ".init_array")]
#[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func,mod_init_funcs")]
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
/* ... other platforms elided ... */
static FOO: extern fn() = {
    extern fn foo() { /* ... */ };
    foo
};

§Inspiration

The idea for ctor was originally inspired by the Neon project.

§Crate Features

Cargo featureDescription
priorityEnable support for the priority parameter.
proc_macroEnable support for the proc-macro #[ctor] attribute. The declarative form (ctor!(...)) is always available. It is recommended that crates re-exporting the ctor macro disable this feature and only use the declarative form.
stdEnable support for the standard library.

§Macro Attributes

AttributeDescription
anonymous

Do not give the constructor a name in the generated code (allows for multiple constructors with the same name). Equivalent to wrapping the constructor in an anonymous const (i.e.: const _ = { ... };).

body(link_section = ".text.startup")

Place the constructor body in a custom link section. By default, this uses the appropriate platform-specific link section.

Co-locating startup functions may improve performance by allowing the binary to page them in and out of memory together.

crate_path = ::path::to::ctor::crate

The path to the ctor crate containing the support macros. If you re-export ctor items as part of your crate, you can use this to redirect the macro’s output to the correct crate.

Using the declarative ctor! form is preferred over this parameter.

export_name_prefix = "ctor_"

Specify a custom export name prefix for the constructor function.

If specified, an export with the given prefix will be generated in the form:

<prefix><priority>_<unique_id>

link_section = ".ctors"

Place the constructor function pointer in a custom link section. By default, this uses the appropriate platform-specific link section.

naked

Use the least-possibly mangled version of the linker invocation for this constructor. This is not recommended for general use as it may prevent authors of binary crates from having low-level control over the order of initialization.

There are no guarantees about the order of execution of constructors with this attribute, just that it will be called at some point before main.

naked constructors are always executed directly by the underlying C library and/or dynamic loader.

naked cannot be used with the priority attribute.

priority = N | early | late

The priority of the constructor. Higher-N-priority constructors are run last. N must be between 0 and 999 inclusive for ordering guarantees (N >= 1000 ordering is platform-defined).

Priority is specified as an isize, string literal, or the identifiers early or late. The integer value will be clamped to a platform-defined range (typically 0-65535), while string priorities are passed through unprocessed.

Priority is applied as follows:

  • early is the default, and is run first (constructors annotated with early and those with no priority attribute are run in the same phase).
  • N is run in increasing order, from 0 <= N <= 999.
  • late is run last, and will be positioned to run after most constructors, even outside the range 0 <= N <= 999.
  • main is run, for binary targets.

Ordering outside of 0 <= N <= 999 is platform-defined with respect to the list above, however platforms will order constructors within a given length range in ascending order (ie: 10000 will run before 20000).

unsafe

Marks a ctor as unsafe. Required.

The ctor crate rejects #[ctor] without marking the item unsafe; that error can be suppressed by passing RUSTFLAGS="--cfg linktime_no_fail_on_missing_unsafe" to Cargo.

used(linker)

Mark generated function pointers used(linker). Requires nightly for the nightly-only feature feature(used_with_arg) (see https://github.com/rust-lang/rust/issues/93798).

This can be made the default by using the cfg flag linktime_used_linker (RUSTFLAGS="--cfg linktime_used_linker").

For a crate using this macro to function correctly with and without this flag, it is recommended to add the following line to the top of lib.rs in the crate root:

#![cfg_attr(linktime_used_linker, feature(used_with_arg))]

§Defaults

#[cfg(target_os = "linux")]
body_link_section = ".text.startup"

#[cfg(target_os = "android")]
body_link_section = ".text.startup"

#[cfg(target_os = "freebsd")]
body_link_section = ".text.startup"

#[cfg(all(target_vendor = "pc", any(target_env = "gnu", target_env = "msvc")))]
body_link_section = ".text$A"

#[cfg(all(target_vendor = "pc", not(any(target_env = "gnu", target_env = "msvc"))))]
body_link_section = ".text.startup"

#[cfg(target_vendor = "apple")]
body_link_section = "__TEXT,__text_startup,regular,pure_instructions"

// default
body_link_section = ()

§export_name_prefix

#[cfg(target_os = "aix")]
export_name_prefix = "__sinit"

// default
export_name_prefix = ()
#[cfg(target_vendor = "apple")]
link_section = "__DATA,__mod_init_func,mod_init_funcs"

#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd",
target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly",
target_os = "illumos", target_os = "haiku", target_os = "vxworks", target_os =
"nto", target_family = "wasm"))]
link_section = ".init_array"

#[cfg(target_os = "none")]
link_section = ".init_array"

#[cfg(target_arch = "xtensa")]
link_section = ".ctors"

#[cfg(all(target_vendor = "pc", any(target_env = "gnu", target_env = "msvc")))]
link_section = ".CRT$XCU"

#[cfg(all(target_vendor = "pc", not(any(target_env = "gnu", target_env = "msvc"))))]
link_section = ".ctors"

#[cfg(all(target_os = "aix"))]
link_section = ()

// default
link_section = (compile_error! ("Unsupported target for #[ctor]"))

§priority

#[cfg(feature = "priority")]
priority = early

// default
priority = ()

§r#unsafe

#[cfg(linktime_no_fail_on_missing_unsafe)]
r#unsafe = (no_fail_on_missing_unsafe)

// default
r#unsafe = ()

§used_linker

#[cfg(linktime_used_linker)]
used_linker = used_linker

// default
used_linker = ()

Modules§

declarative
Declarative form of the #[ctor] macro.
life_before_main
Life-Before-Main and Other Link-Time Hazards
statics
Support for static variables that are initialized at startup time.

Attribute Macros§

ctor
Marks a function or static variable as a library/executable constructor. This uses OS-specific linker sections to call a specific function at load time.