Skip to content

Commit 94f123f

Browse files
authored
refactor(got-patching): extract shared GOT-patching primitives into libdd-got-hook (#2282)
Stacked under [feat(got-hook): add DT_HASH fallback, relocation type guard, and hook_symbol](#2297) # What does this PR do? Moves the ELF GOT-patching infrastructure out of libdd-profiling-heap-gotter into a new shared crate libdd-got-hook, so multiple crates can reuse the same machinery for runtime function interposition. This is a pure code move. I tried to have no behavioral changes here. What moves to `libdd-got-hook` - `DynamicInfo::from_phdr`: parse PT_DYNAMIC from a loaded ELF object - `gnu_hash` / `gnu_hash_symbol_count` / `gnu_hash_lookup` / `check_sym`: GNU hash table utilities - `iterate_libraries`: this is a `dl_iterate_phdr` wrapper with panic-safe trampoline - `PageProtGuard` / `read_proc_maps` / `MapEntry`: RELRO-aware page protection management - `lookup_symbol` / `LookupResult`: used for resolving a symbol across all loaded objects - `elf64_r_sym`: relocation info helper - ELF types and constants What stays in `libdd-profiling-heap-gotter` - `SymbolOverrides`: multi-symbol registry - `hooks.rs`: the actual `malloc`/`free`/`calloc`/`realloc` hook functions - lib.rs: `install_heap_overrides` / `update_heap_overrides` public API # Motivation What inspired you to submit this pull request? # Additional Notes Gated on @scottgerring's approval # How to test the change? Describe here in detail how the change can be validated.
1 parent 38a1987 commit 94f123f

11 files changed

Lines changed: 787 additions & 573 deletions

File tree

.github/CODEOWNERS

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,22 @@ deny.toml @DataDog/libdatadog
4343
docker-bake.hcl @DataDog/apm-common-components-core
4444
docs @DataDog/libdatadog
4545
examples @DataDog/libdatadog
46+
libdd-agent-client @DataDog/apm-common-components-core
4647
libdd-alloc/ @DataDog/libdatadog-profiling
4748
libdd-capabilities*/ @DataDog/apm-common-components-core
4849
libdd-common*/ @DataDog/libdatadog
4950
libdd-crashtracker*/ @DataDog/libdatadog-profiling
5051
libdd-data-pipeline*/ @DataDog/libdatadog-apm
5152
libdd-ddsketch*/ @DataDog/libdatadog-apm @DataDog/apm-common-components-core
5253
libdd-dogstatsd-client @DataDog/apm-common-components-core
53-
libdd-profiling-heap-*/ @DataDog/libdatadog-profiling
54+
libdd-gotter/ @DataDog/libdatadog @DataDog/libdatadog-profiling
5455
libdd-http-client @DataDog/apm-common-components-core
55-
libdd-agent-client @DataDog/apm-common-components-core
5656
libdd-library-config*/ @DataDog/apm-sdk-capabilities-rust
5757
libdd-log*/ @DataDog/apm-common-components-core
5858
libdd-otel-thread-ctx/ @DataDog/apm-common-components-core
5959
libdd-otel-thread-ctx-ffi/ @DataDog/apm-common-components-core
6060
libdd-profiling*/ @DataDog/libdatadog-profiling
61+
libdd-profiling-heap-*/ @DataDog/libdatadog-profiling
6162
libdd-sampling/ @DataDog/apm-common-components-core
6263
libdd-shared-runtime*/ @DataDog/apm-common-components-core
6364
libdd-telemetry*/ @DataDog/apm-common-components-core

Cargo.lock

Lines changed: 10 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ members = [
77
"libdd-alloc",
88
"libdd-profiling-heap-sampler",
99
"libdd-profiling-heap-allocator",
10+
"libdd-gotter",
1011
"libdd-profiling-heap-gotter",
1112
"libdd-profiling-heap-gotter-ffi",
1213
"libdd-crashtracker",

LICENSE-3rdparty.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ js-sys,https://github.com/rustwasm/wasm-bindgen/tree/master/crates/js-sys,MIT OR
220220
kernel32-sys,https://github.com/retep998/winapi-rs,MIT,Peter Atashian <retep998@gmail.com>
221221
kv-log-macro,https://github.com/yoshuawuyts/kv-log-macro,MIT OR Apache-2.0,Yoshua Wuyts <yoshuawuyts@gmail.com>
222222
lazy_static,https://github.com/rust-lang-nursery/lazy-static.rs,MIT OR Apache-2.0,Marvin Löbel <loebel.marvin@gmail.com>
223-
libc,https://github.com/rust-lang/libc,MIT OR Apache-2.0,The Rust Project Developers
223+
libc,https://github.com/rust-lang/libc,MIT OR Apache-2.0,The libc Authors
224224
libdd-libunwind-sys,https://github.com/DataDog/libdatadog/tree/main/libdd-libunwind-sys,Apache-2.0,The libdd-libunwind-sys Authors
225225
libloading,https://github.com/nagisa/rust_libloading,ISC,Simonas Kazlauskas <libloading@kazlauskas.me>
226226
libredox,https://gitlab.redox-os.org/redox-os/libredox,MIT,4lDO2 <4lDO2@protonmail.com>

libdd-gotter/Cargo.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright 2025-Present Datadog, Inc. https://www.datadoghq.com/
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
[package]
5+
name = "libdd-gotter"
6+
version = "0.1.0"
7+
description = "ELF GOT-patching primitives for runtime function interposition on 64-bit Linux."
8+
homepage = "https://github.com/DataDog/libdatadog/tree/main/libdd-gotter"
9+
repository = "https://github.com/DataDog/libdatadog/tree/main/libdd-gotter"
10+
edition.workspace = true
11+
rust-version.workspace = true
12+
license.workspace = true
13+
14+
[lib]
15+
bench = false
16+
17+
[dependencies]
18+
libc.workspace = true

libdd-gotter/README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# libdd-gotter
2+
3+
> [!WARNING]
4+
> This library does runtime function interposition by patching the Global Offset Table (GOT) of loaded ELF objects. This is a substantial intervention in a running process — it modifies function pointers that the dynamic linker has already resolved, affecting all code that calls through those GOT entries. Incorrect use can cause crashes, infinite recursion, heap corruption, or silent data loss. Understand the ELF dynamic linking model before using this crate.
5+
6+
## What it does
7+
8+
When a shared library calls an external function like `malloc`, it jumps through a pointer in its **Global Offset Table** -- a writable table that the dynamic linker fills at load time. This crate walks every loaded ELF object via `dl_iterate_phdr`, parses its `PT_DYNAMIC` segment, and rewrites GOT entries so calls are redirected to a hook function. The original function address is resolved and returned so the hook can forward to it.
9+
10+
## Usage
11+
12+
### Single-symbol hook (crashtracker intercepting `__assert_fail`)
13+
14+
```rust
15+
use libdd_got_hook::hook_symbol;
16+
17+
static ORIG_FN: AtomicUsize = AtomicUsize::new(0);
18+
19+
unsafe extern "C" fn my_hook(/* same signature as target */) {
20+
// ... do work ...
21+
// forward to original via ORIG_FN
22+
}
23+
24+
let mut orig_addr: usize = 0;
25+
unsafe {
26+
hook_symbol(c"__assert_fail", my_hook as *const () as usize, &mut orig_addr);
27+
}
28+
// Release pairs with the Acquire load in my_hook, ensuring the GOT
29+
// patches from hook_symbol are visible before the hook reads orig_addr.
30+
ORIG_FN.store(orig_addr, Ordering::Release);
31+
```
32+
33+
### Multi-symbol registry (heap profiling hooking malloc/free/calloc/realloc)
34+
35+
See [`libdd-profiling-heap-gotter`](../libdd-profiling-heap-gotter) which builds a `SymbolOverrides` registry on top of the primitives exported by this crate.
36+
37+
## Support
38+
This library can be used on ARM64 and AMD64 Linux in processes using glibc or musl runtimes.
39+
Only symbols that have been dynamically linked can be intercept.
40+
For instance, if you want to intercept the `malloc` of your C runtime,
41+
you _cannot_ do so if the application has been statically linked against musl.

0 commit comments

Comments
 (0)