Last updated: 2026-02-09
- Crate:
sighook - Current version:
0.6.0 - Status: multi-platform runtime hook crate with Apple/Linux/Android support (AArch64 + Linux x86_64).
- Current architecture focus: platform-specific backend code behind stable public API.
patchcode(address, new_opcode) -> Result<u32, SigHookError>instrument(address, callback) -> Result<u32, SigHookError>instrument_no_original(address, callback) -> Result<u32, SigHookError>inline_hook(addr, replace_fn) -> Result<u32, SigHookError>original_opcode(address) -> Option<u32>
- Callback signature is fixed:
extern "C" fn(address: u64, ctx: *mut HookContext) - Callback may mutate registers/PC in-place through
HookContext. instrument: callback + original instruction trampoline.instrument_no_original: callback + skip original instruction (unless callback updates control flow).
aarch64-apple-darwinaarch64-apple-iosaarch64-unknown-linux-gnuaarch64-linux-androidx86_64-unknown-linux-gnu
src/memory.rsexecutable-permission restore now uses a fallback strategy on macOS x86_64:- first try
VM_PROT_READ | VM_PROT_EXECUTE - if that fails, retry with
VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY
- first try
- This avoids regressions where restoring executable protection fails in some injected/runtime environments.
rust-macos-aarch64: fmt/check/doc-test/clippy + 4 example smoke tests.rust-linux-x86_64: check/doc-test/clippy + 4 example smoke tests.rust-linux-aarch64: check/doc-test/clippy + 4 example smoke tests.rust-ios-aarch64: check/tests-compile/clippy + build all examples.rust-android-aarch64: check/tests-compile/clippy + build all examples (NDK linker configured).
instrument_with_original:calc(1, 2) = 42instrument_no_original:calc(4, 5) = 99inline_hook_far:target_add(6, 7) = 42patchcode_add_to_mul:calc(6, 7) = 42
src/lib.rs: stable API surface and platform-gated exports.src/signal.rs: signal handlers and trap dispatch flow.src/context.rs:ucontext/thread-state remap toHookContext.src/memory.rs: patching, page permission management, branch encoding.src/trampoline.rs: original instruction trampoline generation.src/state.rs: global slot registry + original bytes/opcode cache.
- Single-thread model by design (
static mutglobal state). - Fixed instrument slot array (
MAX_INSTRUMENTS = 256; no dynamic allocator-based slot registry). - No locking primitives in hot path.
- Current implementation intentionally uses a fixed array + linear scan for slot lookup.
- Reason: signal-trap path prioritizes deterministic behavior and avoids allocator/locking complexity.
- At current scale (
<=256active patchpoints), lookup overhead is acceptable compared with trap/signal overhead. - Do not switch directly to
HashMapin trap hot path without a signal-safety/concurrency design review. - If future demand exceeds 256 hooks or profiling shows lookup bottleneck, prefer a preallocated table + lock-free read strategy rather than allocator-driven
HashMapmutation in hot path.
- Keep public API signatures stable unless explicitly planned as breaking change.
- Prefer single-entry helper APIs with target-specific
cfgat function definition level (avoid duplicated call-sitecfgbranching when possible). - Keep platform branches explicit in
context/signal/memorymodules; these branches are architecture/ABI-driven and should not be over-abstracted.
- Use explicit patchpoint symbol
calc_add_insnforcalcexamples. - Do not rely on compiler-layout fixed offsets for Linux AArch64.
instrument*examples use deterministic assembly layout + fixed patch offset (+0x4fromcalc).patchcode_add_to_muluses deterministic assembly layout + fixed patch offset (+0x6fromcalc).- Linux builds use
-rdynamicin example smoke scripts sodlsymon executable symbols remains reliable.
- Absolute jump fallback in trampoline must not clobber return register.
- Current implementation uses RIP-indirect
jmp qword ptr [rip]with inline 64-bit target literal to preserveraxsemantics.
- Public APIs in
src/lib.rsnow include English rustdoc with concise behavior notes and minimalno_runexamples. - Goal: hovering functions in IDE should show direct usage without leaving source.
Run before release/tag:
cargo fmt --all -- --check
cargo check --all-targets
cargo test --doc
cargo clippy --all-targets -- -D warnings
cargo check --all-targets --target aarch64-apple-darwin
cargo check --all-targets --target aarch64-apple-ios
cargo check --all-targets --target aarch64-linux-android
cargo check --all-targets --target x86_64-unknown-linux-gnu
# optional but recommended pre-release target checks
cargo check --tests --target aarch64-apple-ios
cargo check --tests --target aarch64-linux-androidIf local toolchain supports it, also run:
cargo check --all-targets --target aarch64-unknown-linux-gnu
cargo clippy --all-targets --target aarch64-unknown-linux-gnu -- -D warningsAdditional fast local checks currently in-tree:
cargo test --all-targetssrc/memory.rs now contains unit tests for:
- page-range alignment math (
protect_range_start_len) - target-dependent executable restore protection list on Apple targets
- Validate runtime hooking behavior on real iOS/Android environments (trap delivery and patch permissions).
- Add benchmark/profiling harness for slot lookup cost under high patchpoint counts.
- Decide threshold/plan for optional slot-registry redesign if active patchpoints regularly approach
MAX_INSTRUMENTS. - Optional: introduce a lightweight backend trait to reduce cfg branching in
lib.rsand simplify future ports.
- Rust toolchain baseline: stable
1.85+. - Required components in CI/dev:
rustfmt,clippy. - Android build baseline: NDK
r26d(or compatible) and linker env:CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=.../aarch64-linux-android34-clang. - Linux example smoke tests rely on
ccand-rdynamicfordlsymsymbol visibility. - iOS target currently validates compile/link (
check,check --tests,clippy, examples build), not preload-style runtime smoke.