@@ -17,8 +17,9 @@ use core::ffi::{c_char, c_int, c_void};
1717use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
1818
1919use libdd_profiling_heap_sampler:: {
20- dd_alloc_req_t, dd_allocation_created, dd_allocation_freed, dd_allocation_realloc_commit,
21- dd_allocation_realloc_prepare, dd_allocation_requested, dd_tl_state_init,
20+ dd_alloc_req_is_sampled, dd_alloc_req_t, dd_allocation_created, dd_allocation_freed,
21+ dd_allocation_realloc_commit, dd_allocation_realloc_prepare, dd_allocation_requested,
22+ dd_tl_state_init,
2223} ;
2324
2425/// Resolved address of the real `malloc`; filled by `install_heap_overrides`.
@@ -39,9 +40,27 @@ pub(crate) static ORIG_DLOPEN: AtomicUsize = AtomicUsize::new(0);
3940/// Resolved address of the real `pthread_create`.
4041pub ( crate ) static ORIG_PTHREAD_CREATE : AtomicUsize = AtomicUsize :: new ( 0 ) ;
4142
43+ /// Counts hook invocations so integration tests outside this crate can
44+ /// prove the patched GOT was actually exercised, not just that nothing
45+ /// crashed. Only `malloc`/`free` increment it: that's enough to prove the
46+ /// hooks ran without adding bookkeeping to every symbol.
47+ #[ cfg( feature = "test-support" ) ]
48+ pub ( crate ) static HOOK_HITS : AtomicUsize = AtomicUsize :: new ( 0 ) ;
49+
4250/// Load a resolved function pointer from one of the `ORIG_*` slots.
51+ ///
52+ /// # Safety
53+ ///
54+ /// `T` must be exactly the `extern "C" fn(...)` pointer type that
55+ /// `apply_overrides` writes into `slot` (see the `ORIG_*` docs above); if
56+ /// `slot` is still `0`, this returns `None` rather than transmuting.
4357#[ inline]
4458unsafe fn load_fn < T > ( slot : & AtomicUsize ) -> Option < T > {
59+ // `Acquire` pairs with the `store(Release)` in elf.rs's
60+ // `apply_overrides`, which runs before the GOT is ever patched to
61+ // route calls into these hooks. That gives a real happens-before
62+ // edge: once this observes a non-zero slot, it's guaranteed to see
63+ // the fully-published address, not just "no torn read".
4564 let v = slot. load ( Ordering :: Acquire ) ;
4665 if v == 0 {
4766 None
@@ -95,17 +114,21 @@ type PthreadCreateFn = unsafe extern "C" fn(
95114
96115#[ no_mangle]
97116pub unsafe extern "C" fn gotter_malloc ( size : usize ) -> * mut c_void {
117+ #[ cfg( feature = "test-support" ) ]
118+ HOOK_HITS . fetch_add ( 1 , Ordering :: Relaxed ) ;
98119 let Some ( real) : Option < MallocFn > = load_fn ( & ORIG_MALLOC ) else {
99120 return std:: ptr:: null_mut ( ) ;
100121 } ;
101122 // Default alignment for malloc on glibc is 2*sizeof(void*) == 16.
102- let req = dd_allocation_requested ( size, core:: mem:: align_of :: < u64 > ( ) * 2 ) ;
123+ let req = dd_allocation_requested ( size, core:: mem:: align_of :: < * mut c_void > ( ) * 2 ) ;
103124 let raw = real ( req. size ) ;
104125 dd_allocation_created ( raw, req)
105126}
106127
107128#[ no_mangle]
108129pub unsafe extern "C" fn gotter_free ( ptr : * mut c_void ) {
130+ #[ cfg( feature = "test-support" ) ]
131+ HOOK_HITS . fetch_add ( 1 , Ordering :: Relaxed ) ;
109132 let Some ( real) : Option < FreeFn > = load_fn ( & ORIG_FREE ) else {
110133 return ;
111134 } ;
@@ -125,14 +148,14 @@ pub unsafe extern "C" fn gotter_calloc(nmemb: usize, size: usize) -> *mut c_void
125148 let Some ( total) = nmemb. checked_mul ( size) else {
126149 return real ( nmemb, size) ;
127150 } ;
128- let req = dd_allocation_requested ( total, core:: mem:: align_of :: < u64 > ( ) * 2 ) ;
151+ let req = dd_allocation_requested ( total, core:: mem:: align_of :: < * mut c_void > ( ) * 2 ) ;
129152 // calloc takes (nmemb, size); when the sampler bumps `req.size` we
130153 // funnel the extra bytes into the size argument (nmemb stays 1's
131154 // worth conceptually). The simplest robust path is to switch to a
132155 // single (1, req.size) allocation when sampling kicks in, so the
133156 // underlying allocator zeroes everything we hand back. Unsampled
134157 // path keeps the user's (nmemb, size) verbatim.
135- let raw = if req. weight == 0 {
158+ let raw = if ! dd_alloc_req_is_sampled ( req) {
136159 real ( nmemb, size)
137160 } else {
138161 real ( 1 , req. size )
0 commit comments