Skip to content

ExecSolib strategy: make ddtrace.so directly executable#3711

Open
cataphract wants to merge 14 commits intomasterfrom
glopes/exec-solib
Open

ExecSolib strategy: make ddtrace.so directly executable#3711
cataphract wants to merge 14 commits intomasterfrom
glopes/exec-solib

Conversation

@cataphract
Copy link
Contributor

@cataphract cataphract commented Mar 19, 2026

Introduce the ExecSolib spawn strategy by embedding an ELF entry point (_dd_solib_start) into ddtrace.so itself.

ddtrace.so becomes pie executable and runs without the dynamic linker. After self-relocation:

  • it loads the trampoline into memory, but doesn't execute it yet
  • it copies ddtrace.so (/proc/self/exe) into a memfd and massages it so its loading by the dynamic linker without php doesn't fail.
  • replaces in the command line all occurrences to ddtrace.so to /proc/self/fd/, so that dlopen from the trampoline will use the massaged version
  • loads the dynamic linker into memory
  • jumps into the dynamic linker after massaging the auxiliary vector so that ld.so executes the loaded trampoline

tested on glibc and musl on linux aarch64

Description

Reviewer checklist

  • Test coverage seems ok.
  • Appropriate labels assigned.

@cataphract cataphract requested review from a team as code owners March 19, 2026 17:44
@datadog-datadog-prod-us1
Copy link

datadog-datadog-prod-us1 bot commented Mar 20, 2026

⚠️ Tests

Fix all issues with BitsAI or with Cursor

⚠️ Warnings

🧪 13 Tests failed

testSearchPhpBinaries from integration.DDTrace\Tests\Integration\PHPInstallerTest (Datadog) (Fix with Cursor)
DDTrace\Tests\Integration\PHPInstallerTest::testSearchPhpBinaries
Test code or tested code printed unexpected output: Searching for available php binaries, this operation might take a while.
testSimplePushAndProcess from laravel-58-test.DDTrace\Tests\Integrations\Laravel\V5_8\QueueTest (Datadog) (Fix with Cursor)
DDTrace\Tests\Integrations\Laravel\V5_8\QueueTest::testSimplePushAndProcess
Test code or tested code printed unexpected output: spanLinksTraceId: 69bf535600000000139bf088bb44d8c2
tid: 69bf535600000000
hexProcessTraceId: 139bf088bb44d8c2
hexProcessSpanId: 801c3cc67afcbbc7
processTraceId: 1412987378158852290
processSpanId: 9231320159367248839

phpvfscomposer://tests/vendor/phpunit/phpunit/phpunit:106
testSimplePushAndProcess from laravel-8x-test.DDTrace\Tests\Integrations\Laravel\V8_x\QueueTest (Datadog) (Fix with Cursor)
DDTrace\Tests\Integrations\Laravel\V8_x\QueueTest::testSimplePushAndProcess
Test code or tested code printed unexpected output: spanLinksTraceId: 69bf53960000000014cf947f78b12f70
tid: 69bf539600000000
hexProcessTraceId: 14cf947f78b12f70
hexProcessSpanId: 7e118766120969b4
processTraceId: 1499580476144299888
processSpanId: 9084190795842021812
View all

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 9293858 | Docs | Datadog PR Page | Was this helpful? React with 👍/👎 or give us feedback!

@codecov-commenter
Copy link

codecov-commenter commented Mar 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 68.73%. Comparing base (b9a49fa) to head (9293858).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3711      +/-   ##
==========================================
- Coverage   68.77%   68.73%   -0.04%     
==========================================
  Files         166      166              
  Lines       19030    19030              
  Branches     1797     1797              
==========================================
- Hits        13087    13080       -7     
- Misses       5127     5135       +8     
+ Partials      816      815       -1     
Flag Coverage Δ
helper-rust-unit 49.36% <ø> (-0.08%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.
see 3 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b9a49fa...9293858. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Introduce the ExecSolib spawn strategy by embedding an ELF entry point
(_dd_solib_start) into ddtrace.so itself.
But the import can't be declared hidden. In the end the symbol will be
in the got but linker to emits a RELATIVE reloc (not GLOB_DAT) -- so
should work with our self-relocation.
cataphract and others added 2 commits March 20, 2026 12:29
The x86-64 inline asm restoring the kernel stack and jumping to ld.so:

    "mov %[sp], %%rsp\n"
    "xor %%edx, %%edx\n"   // required: rdx = 0 for ld.so startup ABI
    "jmpq *%[entry]\n"

GCC at -O0 allocated %[entry] (ldso_entry) to rdx, causing the xor to
zero the jump target before the jmpq executed → SIGSEGV at address 0x0
on every x86-64 ExecSolib launch.

The fix is to pin ldso_entry to rax via the "a" constraint.  Using the
"rdx" clobber alone is not sufficient: GCC is permitted to allocate
input operands into clobbered registers because inputs are consumed
before the asm fires.  A specific register constraint ("a" = rax) is
the correct and optimization-safe solution.

With the fix, GCC emits:
    mov  %rcx, %rsp    ; stack_top in rcx (or any non-rax "r")
    xor  %edx, %edx    ; zero rdx (harmless: entry is in rax)
    jmpq *%rax         ; jump to ldso_entry

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@pr-commenter
Copy link

pr-commenter bot commented Mar 20, 2026

Benchmarks [ tracer ]

Benchmark execution time: 2026-03-22 03:32:20

Comparing candidate commit 9293858 in PR branch glopes/exec-solib with baseline commit b9a49fa in branch master.

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

scenario:MessagePackSerializationBench/benchMessagePackSerialization-opcache

  • 🟥 execution_time [+3.391µs; +4.449µs] or [+3.050%; +4.001%]

@cataphract cataphract force-pushed the glopes/exec-solib branch 2 times, most recently from 76bb66d to 4e0b02e Compare March 21, 2026 12:44
@cataphract cataphract force-pushed the glopes/exec-solib branch 2 times, most recently from 4cffacf to 9293858 Compare March 22, 2026 02:16
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