Skip to content

Commit ed285d8

Browse files
committed
Initial commit: glibc support
0 parents  commit ed285d8

File tree

8 files changed

+183
-0
lines changed

8 files changed

+183
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
target
2+
Cargo.lock

Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[package]
2+
name = "redhook"
3+
version = "0.0.1"
4+
authors = ["Geoffrey Thomas <[email protected]>"]
5+
examples = []

README.md

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
redhook
2+
=======
3+
4+
redhook is a helper crate for writing interposition libraries
5+
(`LD_PRELOAD`, `DYLD_INSERT_LIBRARIES`, etc.) in Rust.
6+
7+
To use redhook, add the lines
8+
9+
```rust
10+
#[macro_use]
11+
extern crate redhook;
12+
```
13+
14+
to your library, then use the `hook!` macro to declare the function you
15+
want to hook, and the name you want to give to your hook function:
16+
17+
```rust
18+
hook! {
19+
fn existing_function(x: i32) -> i32 => my_function {
20+
42
21+
}
22+
}
23+
```
24+
25+
To access the underlying function, use `real!(existing_function)`. Then
26+
compile your library as a `dylib`.
27+
28+
There are a few [examples](examples) included, as separate crates.
29+
30+
At the moment, redhook only supports `LD_PRELOAD` on glibc, but support
31+
for other platforms is planned.
32+
33+
redhook is named after the [Red Hook](http://en.wikipedia.org/wiki/Red_Hook,_Brooklyn)
34+
neighborhood in Brooklyn, New York.

examples/fakeroot/Cargo.toml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "redhook_ex_fakeroot"
3+
version = "0.0.1"
4+
authors = ["Geoffrey Thomas <[email protected]>"]
5+
6+
[lib]
7+
name = "fakeroot"
8+
crate_type = ["dylib"]
9+
10+
[dependencies.redhook]
11+
path = "../.."

examples/fakeroot/src/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#[macro_use]
2+
extern crate redhook;
3+
4+
hook! {
5+
fn getuid() -> u64 => i_am_root {
6+
0
7+
}
8+
}

examples/readlinkspy/Cargo.toml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "redhook_ex_readlinkspy"
3+
version = "0.0.1"
4+
authors = ["Geoffrey Thomas <[email protected]>"]
5+
6+
[lib]
7+
name = "readlinkspy"
8+
crate_type = ["dylib"]
9+
10+
[dependencies.redhook]
11+
path = "../.."

examples/readlinkspy/src/lib.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![feature(libc)]
2+
3+
extern crate libc;
4+
5+
#[macro_use]
6+
extern crate redhook;
7+
8+
use libc::{size_t,ssize_t,c_char};
9+
10+
hook! {
11+
fn readlink(path: *const c_char, buf: *mut c_char, bufsiz: size_t) -> ssize_t => my_readlink {
12+
if let Ok(path) = std::str::from_utf8(unsafe {std::ffi::CStr::from_ptr(path).to_bytes()}) {
13+
println!("readlink(\"{}\")", path);
14+
} else {
15+
println!("readlink(...)");
16+
}
17+
18+
unsafe { real!(readlink)(path, buf, bufsiz) }
19+
}
20+
}

src/lib.rs

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#![feature(libc)]
2+
3+
extern crate libc;
4+
5+
use libc::{c_char, c_void};
6+
use std::sync::atomic;
7+
8+
#[link(name="dl")]
9+
extern {
10+
fn dlsym(handle: *const c_void, symbol: *const c_char) -> *const c_void;
11+
}
12+
13+
const RTLD_NEXT: *const c_void = -1is as *const c_void;
14+
15+
pub unsafe fn dlsym_next(symbol: &'static str) -> *const u8 {
16+
let ptr = dlsym(RTLD_NEXT, symbol.as_ptr() as *const c_char);
17+
if ptr.is_null() {
18+
panic!("redhook: Unable to find underlying function for {}", symbol);
19+
}
20+
ptr as *const u8
21+
}
22+
23+
/* Some Rust library functionality (e.g., jemalloc) initializes
24+
* lazily, after the hooking library has inserted itself into the call
25+
* path. If the initialization uses any hooked functions, this will lead
26+
* to an infinite loop. Work around this by running some initialization
27+
* code in a static constructor, and bypassing all hooks until it has
28+
* completed. */
29+
30+
static INIT_STATE: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;
31+
32+
pub fn initialized() -> bool {
33+
INIT_STATE.load(atomic::Ordering::SeqCst)
34+
}
35+
36+
extern fn initialize() {
37+
Box::new(0u8);
38+
INIT_STATE.store(true, atomic::Ordering::SeqCst);
39+
}
40+
41+
/* Rust doesn't directly expose __attribute__((constructor)), but this
42+
* is how GNU implements it. */
43+
#[link_section=".init_array"]
44+
pub static INITIALIZE_CTOR: extern fn() = initialize;
45+
46+
#[macro_export]
47+
macro_rules! hook {
48+
(fn $real_fn:ident ( $($v:ident : $t:ty),* ) -> $r:ty => $hook_fn:ident $body:block) => {
49+
#[allow(non_camel_case_types)]
50+
pub struct $real_fn {__private_field: ()}
51+
#[allow(non_upper_case_globals)]
52+
static $real_fn: $real_fn = $real_fn {__private_field: ()};
53+
54+
impl $real_fn {
55+
fn get(&self) -> unsafe extern fn ( $($v : $t),* ) -> $r {
56+
use std::sync::{Once, ONCE_INIT};
57+
58+
static mut REAL: *const u8 = 0 as *const u8;
59+
static mut ONCE: Once = ONCE_INIT;
60+
61+
unsafe {
62+
ONCE.call_once(|| {
63+
REAL = $crate::dlsym_next(concat!(stringify!($real_fn), "\0"));
64+
});
65+
std::mem::transmute(REAL)
66+
}
67+
}
68+
69+
#[no_mangle]
70+
pub unsafe fn $real_fn ( $($v : $t),* ) -> $r {
71+
if $crate::initialized() {
72+
$hook_fn ( $($v),* )
73+
} else {
74+
$real_fn.get() ( $($v),* )
75+
}
76+
}
77+
}
78+
79+
pub fn $hook_fn ( $($v : $t),* ) -> $r {
80+
$body
81+
}
82+
};
83+
84+
(fn $real_fn:ident ( $($v:ident : $t:ty),* ) => $hook_fn:ident $body:block) => {
85+
hook! { fn $real_fn ( $($v : $t),* ) -> () => $hook_fn $body }
86+
};
87+
}
88+
89+
#[macro_export]
90+
macro_rules! real {
91+
($real_fn:ident) => {$real_fn.get()}
92+
}

0 commit comments

Comments
 (0)