#destructor #constructor #declarative-macro #ctor #no-std #dtor

no-std bin+lib hictor

用于实现 __attribute__((constructor))/__attribute__((destructor)) 的声明宏

7 个版本

0.1.6 2023年11月17日
0.1.5 2023年11月17日
0.1.4 2023年10月15日

#1 in #destructor


3 个 crate 使用

MIT/Apache

13KB
159 代码行数(不包括注释)

hictor

0.1.5

提供三个辅助函数来获取命令行参数

fn args() -> &'static [*const u8];
fn program_invocation_name() -> &'static str;
fn program_invocation_short_name() -> &'static str;

功能介绍

基于声明宏实现,支持 gcc 的 __attribute__((constructor)) 和 __attribute__((destructor)) 功能。

与 ctor crate 的不同之处在于

  1. 声明宏;ctor 是过程宏实现。
  2. 只支持函数;ctor 还支持静态变量。
  3. ctor_args, ctor_custom 支持获取命令行输入参数;ctor 未支持。
  4. dtor 完全等同于 __attribute__((destructor)),Windows 等平台不支持;ctor 封装了 atexit。

无论如何,业务代码不能依赖于多个初始化函数的调用顺序,这无法保证。

注:仅在 Linux 平台下进行过测试。

attribute((constructor))

如果不需要关心命令行输入参数,则可以使用无参数的函数实现。函数类型必须是 unsafe fn()

unsafe fn init() {
    println!("init before main");
}

hictor::ctor!(init);

如果需要获取命令行输入,函数类型必须是 unsafe extern "C" fn(i32, *const *const i8)

unsafe extern "C" fn init(argc: i32, argv: *const *const i8) {
    println!("argc = {argc}, program = {:?}", std::ffi::CStr::from_ptr(*argv));
}

hictor::ctor_args!(init);

某些平台下支持更多的参数,例如 Linux 平台下还可以获取环境变量,其函数类型为 unsafe extern "C" fn(u32, *const *const u8, *const *const u8)

#[cfg(target_os = "linux")]
unsafe extern "C" fn init(argc: i32, argv: *const *const i8, envs: *const *const i8) {
    println!("argc = {argc}, program = {:?} env.0 = {:?}", std::ffi::CStr::from_ptr(*argv), std::ffi::CStr::from_ptr(*envs));
}

#[cfg(target_os = "linux")]
hictor::ctor_custom!(init);

attribute((destructor))

函数类型必须是 unsafe fn(). 业务不应该依赖于这个机制,因为应用程序可能因多种未知因素异常退出,这些函数可能不会被调用。高可用性软件应该为这种无法绝对避免的场景做准备,例如启动时进行恢复检查或额外的监控和容错处理程序等。

unsafe fn fini() {
    println!("fini after main");
}

hictor::dtor!(fini);

获取命令行输入

如果不在 main 函数中,可以获取当前应用程序的命令行输入参数。

fn foo() {
    let args: &'static [*const i8] = hictor::args();
}

说明

其实现原理是定义一个全局变量,初始化为指定的函数指针,并指定在 ELF 的特殊段(Linux 下是 .init_array)中。需要定义一个静态变量来实现这个功能。但是声明宏中无法像 C 的宏那样,通过组合行号等其他信息来生成一个变量名(concat_idents 宏仅在 nightly 版本可用),为了避免静态变量的重复定义,宏的实现中利用函数名的唯一性定义了一个子模块。如果恰好有一个和函数同名的模块存在,则会出现冲突。在这种情况下,需要指定一个唯一的模块名。

mod init {
    pub(super) unsafe fn init() {
        println!("init before main");
    }
}

use init::init;
hictor::ctor!(init_mod, init);

无运行时依赖