Skip to content

feat(gotter)!: unpatch symbol functionality - #2342

Draft
gyuheon0h wants to merge 4 commits into
mainfrom
gyuheon0h/unpatch-gotter
Draft

feat(gotter)!: unpatch symbol functionality#2342
gyuheon0h wants to merge 4 commits into
mainfrom
gyuheon0h/unpatch-gotter

Conversation

@gyuheon0h

@gyuheon0h gyuheon0h commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

This pr adds unhook_symbol API to libdd-gotter that reverses a previous hook_symbol call by restoring GOT entries that currently point at the hook function back to the original address. This only restores entries whose current value matches hook_fn, making it safe to call even if libraries were loaded/unloaded after the original hook or entries were independently modified.

Motivation

Embedders that load libdatadog as a shared library and later dlclose it would leave other modules' GOT slots pointing at hook_assert_fail inside the now-unmapped image. A subsequent C assert() in any patched module would jump to stale code.

Additional Notes

Anything else we should know when reviewing?

How to test the change?

Unit tests

gyuheon0h commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

@gyuheon0h gyuheon0h changed the title Unpatch feat(gotter): unpatch symbol functionality Aug 11, 2026
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

📚 Documentation Check Results

⚠️ 6 documentation warning(s) found

📦 libdd-gotter - 6 warning(s)


Updated: 2026-09-11 18:30:18 UTC | Commit: 512dcac | missing-docs job results

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

🔒 Cargo Deny Results

No issues found!

📦 libdd-gotter - ✅ No issues


Updated: 2026-09-11 18:30:32 UTC | Commit: 512dcac | dependency-check job results

@gyuheon0h
gyuheon0h force-pushed the gyuheon0h/unpatch-gotter branch from 71ed1ee to dc757c4 Compare August 11, 2026 16:28
@pr-commenter

pr-commenter Bot commented Aug 11, 2026

Copy link
Copy Markdown

Benchmarks

Comparison

Benchmark execution time: 2026-09-11 18:43:01

Comparing candidate commit b5deb14 in PR branch gyuheon0h/unpatch-gotter with baseline commit 3f833f8 in branch main.

📊 Benchmarking dashboard

Found 0 performance improvements and 0 performance regressions! Performance is the same for 1 metrics, 0 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

Candidate

Candidate benchmark details

Group 1

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8175M CPU @ 2.50GHz b5deb14 1789151262 gyuheon0h/unpatch-gotter
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
receiver_entry_point/report/2644 execution_time 6.293ms 6.432ms ± 0.064ms 6.454ms ± 0.037ms 6.479ms 6.516ms 6.533ms 6.539ms 1.32% -0.543 -0.957 1.00% 0.005ms 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
receiver_entry_point/report/2644 execution_time [6.423ms; 6.441ms] or [-0.138%; +0.138%] None None None

Baseline

Baseline benchmark details

Group 1

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8175M CPU @ 2.50GHz 3f833f8 1789135942 main
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
receiver_entry_point/report/2644 execution_time 6.315ms 6.374ms ± 0.023ms 6.375ms ± 0.012ms 6.385ms 6.411ms 6.439ms 6.475ms 1.57% 0.528 2.283 0.36% 0.002ms 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
receiver_entry_point/report/2644 execution_time [6.371ms; 6.377ms] or [-0.051%; +0.051%] None None None

@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tests

All CI checks and tests passed.

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 87.20%
Overall Coverage: 77.76% (-0.00%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: b5deb14 | Docs | View more details | Give us feedback!

@gyuheon0h
gyuheon0h force-pushed the gyuheon0h/unpatch-gotter branch 2 times, most recently from 30f311c to fe3f528 Compare August 11, 2026 16:58
@dd-octo-sts

dd-octo-sts Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Artifact Size Benchmark Report

aarch64-alpine-linux-musl
Artifact Baseline Commit Change
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.a 96.01 MB 96.02 MB +0% (+7.82 KB) 👌
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.so 9.02 MB 9.02 MB 0% (0 B) 👌
aarch64-unknown-linux-gnu
Artifact Baseline Commit Change
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.so 12.19 MB 12.19 MB +0% (+264 B) 👌
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.a 107.42 MB 107.42 MB +0% (+7.56 KB) 👌
libdatadog-x64-windows
Artifact Baseline Commit Change
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.dll 29.04 MB 29.04 MB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.lib 96.08 KB 96.08 KB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.pdb 191.77 MB 191.77 MB 0% (0 B) 👌
/libdatadog-x64-windows/debug/static/datadog_profiling_ffi.lib 818.18 MB 818.18 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.dll 9.69 MB 9.69 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.lib 96.08 KB 96.08 KB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.pdb 27.51 MB 27.51 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/static/datadog_profiling_ffi.lib 55.57 MB 55.57 MB 0% (0 B) 👌
libdatadog-x86-windows
Artifact Baseline Commit Change
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.dll 25.41 MB 25.41 MB 0% (0 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.lib 97.58 KB 97.58 KB 0% (0 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.pdb 197.08 MB 197.10 MB +0% (+16.00 KB) 👌
/libdatadog-x86-windows/debug/static/datadog_profiling_ffi.lib 800.48 MB 800.48 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.dll 7.51 MB 7.51 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.lib 97.58 KB 97.58 KB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.pdb 29.63 MB 29.63 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/static/datadog_profiling_ffi.lib 52.55 MB 52.55 MB 0% (0 B) 👌
x86_64-alpine-linux-musl
Artifact Baseline Commit Change
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.a 85.97 MB 85.98 MB +0% (+8.12 KB) 👌
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.so 10.03 MB 10.03 MB 0% (0 B) 👌
x86_64-unknown-linux-gnu
Artifact Baseline Commit Change
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.a 101.85 MB 101.86 MB +0% (+8.12 KB) 👌
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.so 12.25 MB 12.25 MB +0% (+200 B) 👌

@gyuheon0h
gyuheon0h marked this pull request as ready for review August 11, 2026 17:36
@gyuheon0h
gyuheon0h requested review from a team as code owners August 11, 2026 17:36

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fe3f528b44

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread libdd-gotter/src/elf.rs Outdated
@gyuheon0h
gyuheon0h marked this pull request as draft August 12, 2026 13:34
@gyuheon0h
gyuheon0h marked this pull request as ready for review August 12, 2026 19:12
@gyuheon0h
gyuheon0h requested a review from scottgerring August 12, 2026 19:12

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c1bed325d8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread libdd-gotter/src/elf.rs Outdated
Comment thread libdd-gotter/src/elf.rs Outdated
Comment thread libdd-gotter/src/elf.rs Outdated
@gyuheon0h
gyuheon0h marked this pull request as draft August 12, 2026 19:23
@gyuheon0h
gyuheon0h force-pushed the gyuheon0h/unpatch-gotter branch 4 times, most recently from 91800af to aaf32c6 Compare August 17, 2026 17:09
@gyuheon0h
gyuheon0h force-pushed the gyuheon0h/unpatch-gotter branch from aaf32c6 to 78478b3 Compare September 9, 2026 02:48
@gyuheon0h
gyuheon0h marked this pull request as ready for review September 9, 2026 03:29
@gyuheon0h gyuheon0h changed the title feat(gotter): unpatch symbol functionality feat(gotter)!: unpatch symbol functionality Sep 9, 2026
@gyuheon0h
gyuheon0h force-pushed the gyuheon0h/unpatch-gotter branch from 8a63381 to c9368c1 Compare September 9, 2026 03:33

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a63381422

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread libdd-gotter/src/elf.rs Outdated
Comment thread libdd-gotter/src/elf.rs Outdated
Comment thread libdd-gotter/src/elf.rs Outdated
Comment thread libdd-gotter/src/elf.rs Outdated
@gyuheon0h
gyuheon0h marked this pull request as draft September 9, 2026 03:38

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4405231fd2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread libdd-gotter/src/elf.rs
Comment thread libdd-gotter/src/elf.rs Outdated
Comment on lines +1308 to +1310
let slot = addr as *const AtomicUsize;
match unsafe {
(*slot).compare_exchange(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid atomic access to unaligned relocation slots

For supported absolute relocations, r_offset is not guaranteed to be usize-aligned; for example, a function-pointer field in a packed global produces an R_X86_64_64 relocation at an unaligned address, which is why the patch path uses read_unaligned/write_unaligned. Dereferencing that address as AtomicUsize requires atomic alignment, so unhooking such a slot invokes undefined behavior and can fault, especially on AArch64; use an alignment-safe conditional replacement strategy or exclude unaligned slots.

AGENTS.md reference: AGENTS.md:L70-L71

Useful? React with 👍 / 👎.

Comment thread libdd-gotter/src/elf.rs
Comment thread libdd-gotter/src/elf.rs Outdated
Comment thread libdd-gotter/src/elf.rs
Comment thread libdd-gotter/src/elf.rs Outdated
@gyuheon0h
gyuheon0h marked this pull request as draft September 9, 2026 14:51
@gyuheon0h
gyuheon0h force-pushed the gyuheon0h/unpatch-gotter branch from 4405231 to 424b097 Compare September 9, 2026 18:57
@gyuheon0h
gyuheon0h force-pushed the gyuheon0h/unpatch-gotter branch 2 times, most recently from b789947 to 2603f92 Compare September 11, 2026 16:57
@gyuheon0h
gyuheon0h marked this pull request as ready for review September 11, 2026 17:04
Comment thread libdd-gotter/src/elf.rs
Comment on lines +1283 to +1285
/// Every address in `hook_result.slots` must still point into a
/// currently-mapped region. No library whose GOT was patched may be
/// `dlclose`d before this function returns.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a hard to satisfy requirement. Need to protect against when this happens in the API explicitly

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2603f92e89

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread libdd-gotter/src/elf.rs
Comment thread libdd-gotter/src/elf.rs
// JUMP_SLOT/GLOB_DAT are always pointer-aligned, but
// R_X86_64_64/R_AARCH64_ABS64 entries in data sections may not be.
// SAFETY: `addr` points to a writable GOT slot in mapped memory.
unsafe { core::ptr::write_unaligned(addr as *mut usize, original_value) };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Quiesce callers before non-atomic restoration

When a supported R_X86_64_64 or R_AARCH64_ABS64 function-pointer slot is unaligned, a concurrent caller can observe a torn value during this write_unaligned and jump to an invalid address, despite the new contract explicitly declaring concurrent calls safe. The fresh evidence after the prior atomic-alignment finding is that the final implementation switched to a non-atomic unaligned write while retaining that guarantee; either require callers to be quiesced or exclude unaligned slots/use a synchronization strategy that actually makes replacement atomic.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... I guess there isnt really anything we can do here since we can't lock function calls through the GOT... and we physically cannot have atomic guarantees on non-aligned ptrs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't that contradict the documentation of this function (writes are aligned and pointer-sized thus atomic)? Or at least it's an addendum that could be added? Just to be sure to understand the situation: if a thread goes through the GOT slot, then all good (aligned, atomic, sees either the old or the new function). However for direct relocations, this is not the case. So a concurrent thread reading the target of a relocation could observe an inconsistent state, right?

@gyuheon0h
gyuheon0h force-pushed the gyuheon0h/unpatch-gotter branch from 2603f92 to a7e16b3 Compare September 11, 2026 17:54
@gyuheon0h
gyuheon0h force-pushed the gyuheon0h/unpatch-gotter branch from a7e16b3 to b5deb14 Compare September 11, 2026 18:29

@yannham yannham left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the scenario for unloading the crashtracker lib? Can it happen in the middle of a normal run, or is it something like at exit (if we're unloaded in the wrong order, this causes spurious crashes)?

The issue with unhooking is that the lifecycle for pure relocations that don't go through the GOT/PLT seems to be quite delicate:

Overwriting with the wrong value

For example, let's say you have static like

static void *(*allocate)(size_t) = malloc;

Then, during the lifetime of the program, there's a new assignment allocate = my_personal_allocator. During unhooking, we might accidentally restore malloc because we look at the original relocations, but those that aren't modified if a static variable gets updated. So it's not because we've initially patched a relocation target that it still contains the same thing. I guess we can just check the value of the target first and compare it to hook_symbol, although it introduces new fun data races.

Data races

Since we might overwrite mutable memory that is concurrently used, with misaligned pointer writes, there's no protection at all against data races. This is UB and could very well happen if another thread is trying to read the variable. Maybe we could just skip patching unaligned locations? I really don't expect this to happen a lot, especially for something like assert.

Stale data

Another possibility is that the value has been copied somewhere else. For example in a Vec<callbacks> from the static, or even just in a register. Those values can't be traced at all, and thus can't be "unhooked". They will continue to point back to the original hook, which might not exist anymore.

Should we patch relocations?

I don't know all the tradeoffs involved here, but I feel if we can get our way in practice without touching relocations at all, our life will be easier/safer. It means we'll miss some calls, and I guess we would have to see if it matters or not in practice (like if 0.01% of calls to assert are done through direct relocations, then 🤷‍♂️ ).

Not patching unaligned relocation target would already get rid of the data race issue (at least most of it, because there's still a TOCTOU when some threads might have already loaded the old hook address locally in a register as we unhook, but those are 1. unavoidable I guess and 2. the window for those is much, much smaller - we would have to unhook and unload the module between a thread loading the address and actually jumping to the function).

Comment thread libdd-gotter/src/elf.rs
// If /proc/self/maps isn't available (or the page isn't in it,
// which shouldn't happen for a mapped GOT page) fall back to
// PROT_READ, the RELRO'd default. Tighter than leaving pages RW.
let orig = self.original_prot(aligned).unwrap_or(PROT_READ);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means that if something else (imagine a libdatadog-like library, or the host program itself) is relying on this mapping being writeable, this might create strange memory protection errors if we restore a stronger permission than what was here originally. Now I assume this will be an exceptional situation (writeable required + no /proc/self/maps), and I still think restoring READ only is the right thing to do security-wise, but I wonder if that will come to bite us some day with a strange bug. Maybe we could document this behavior, e.g. in the README or somewhere else?

Comment thread libdd-gotter/src/elf.rs
/// If [`slots_failed`] is non-zero, one or more GOT entries still point at
/// the hook function because `mprotect` was rejected (e.g. a seccomp policy
/// installed after hooking). Those slots remain live pointers into the
/// library being unloaded; the caller **must not** unload until all slots

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit strange to talk about "the caller" for a struct documentation. Would the actual function be a better place to put this documentation? Should this be a safety condition?

Comment thread libdd-gotter/src/elf.rs
///
/// `hook_fn` must point to a function with the same calling convention
/// and signature as the symbol being hooked. The patching is permanent.
/// and signature as the symbol being hooked.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: mention in this function's documentation the unhook function?

Comment thread libdd-gotter/src/elf.rs
/// hook's own library regardless of PIE vs non-PIE (where `dlpi_addr`
/// may be 0 for the main executable).
///
/// We MUST NOT dlopen a library that contains the hook symbol during the duration

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: should that be part of a proper # Safety section? Or is it not really unsafe but just leads to unwanted behavior

Comment thread libdd-gotter/src/elf.rs
// Reserve before patching so push is guaranteed not to allocate
// after any GOT entry in this library has been overwritten.
let count = count_matching_got_entries(&dyn_info, symbol_name_bytes);
unsafe { (*slots_ptr).reserve(count) };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have to be unsafe code? Could we share a mutable reference, use reserve safely, and only convert it into a pointer just in time when calling patch_got_entries? (maybe not, but asking just in case)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, patch_got_entries takes an &mut, so I suspect slots can stay a safe reference all along?

Comment thread libdd-gotter/src/elf.rs
for reloc in relocs {
try_count(reloc.r_info);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (style): instead of having to make a closure here, I think you could just chain iterators instead, something like for reloc in dyn_info.rels().into_iter().chain(dyn_info.relas()).chain(dyn_info.jmprels()) { ...count... }.

Comment thread libdd-gotter/src/elf.rs
// JUMP_SLOT/GLOB_DAT are always pointer-aligned, but
// R_X86_64_64/R_AARCH64_ABS64 entries in data sections may not be.
// SAFETY: `addr` points to a writable GOT slot in mapped memory.
unsafe { core::ptr::write_unaligned(addr as *mut usize, original_value) };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't that contradict the documentation of this function (writes are aligned and pointer-sized thus atomic)? Or at least it's an addendum that could be added? Just to be sure to understand the situation: if a thread goes through the GOT slot, then all good (aligned, atomic, sees either the old or the new function). However for direct relocations, this is not the case. So a concurrent thread reading the target of a relocation could observe an inconsistent state, right?

@gyuheon0h

Copy link
Copy Markdown
Contributor Author

@yannham Yeah. I spent (too much -- IMO shouldve bailed earlier) time trying to force an "unhook" API, but making unhooks that work all the time for all cases and is always consistent and reliable after a "permanent" GOT modification has been a struggle.

The reason I started this is because PHP apparently sometimes unloads the tracer, which the customer application dynamically links to. So, if we ever patch something that gets called later on, it is a segfault as our patch function is no longer mapped. I think I will change the direction of this to implement an unhook that is only to be called if crashtracker is unloaded -- making it clear in the docs that its not intended to be called regularly, and GOT patching through libdd-gotter shouldbe considered permanent.

@gyuheon0h
gyuheon0h marked this pull request as draft September 14, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants