#override #constructor #ld-preload

no-std ld_preload_helpers

在加载时运行代码并覆盖 C 函数的宏

2 个版本

0.1.1 2023 年 5 月 28 日
0.1.0 2023 年 5 月 27 日

#1492 in Rust 模式

每月 21 次下载

Apache-2.0

7KB
54

ld_preload_helpers

Rust 宏,用于在加载时运行代码并覆盖 C 函数。

主要用于 LD_PRELOAD 钩子,因此得名。

示例

C 函数覆盖

在这里,open 被覆盖以打印一些内容,然后转发到真实的 open。在极少数需要递归到覆盖而不是调用真实 API 的情况下,使用 crate::open。在模块中的任何其他地方,open 将指向覆盖。要引用程序中的真实函数,请使用 real_open(如声明中所命名)。

单个宏调用中可以指定多个覆盖,并且如果需要,支持多个 extern_c_overrides 调用。以下示例展示了覆盖 getuid 并将用户的 ID 增加 2000。

std 模式(默认模式)中,覆盖中的 panic 被捕获并导致捕获块执行。在 no_std 模式(由于使用 OnceLock 而未得到良好支持;见下文),没有特殊的 panic 处理,并且捕获块被忽略。

extern_c_overrides! {
  unsafe fn open/real_open(pathname: *const c_char, flags: c_int, mode: libc::mode_t) -> c_int {
    println!("RUST OPEN {:?} {:x} {:x}", unsafe { CStr::from_ptr(pathname) }, flags, mode);
    // panic!("oops");
    return open(pathname, flags, mode);
  } catch {
    errno::set_errno(errno::Errno(libc::ENODEV));
    return -1;
  }

  unsafe fn getuid/real_getuid() -> libc::uid_t {
    getuid() + 2000
  } catch { u32::MAX }
}

图像加载钩子

在程序/库加载时调用,在运行主入口点之前。注意,这演示了调用覆盖的 getuid 和真实的 getuid

目前由于使用的生成函数具有固定名称,因此只能有一个此类钩子。但在未来可能会扩展以允许用户传递名称。

on_load! {{
  println!("INIT LIB {} {}", unsafe { getuid() }, unsafe { real_getuid() });
}}

示例运行

$ LD_PRELOAD=target/debug/liboverride.so id
INIT LIB 2501 501
uid=2501 gid=501 euid=501
$ LD_PRELOAD=target/debug/liboverride.so wc Cargo.lock
INIT LIB 2501 501
RUST OPEN "Cargo.lock" 0 52c47940
  820  1588 21572 Cargo.lock

no_std 使用说明

  • std::sync::OnceLock 是对 std 的唯一依赖。应考虑使用 conquer_once 或类似 crate 来支持 no_std
  • 还可以在单线程代码中禁用此依赖,以允许使用 no_std
  • 待办事项:在 no_std 客户端代码中,捕获块未使用;考虑在该情况下不要求/接受它。

依赖项

~43KB