Skip to content

Commit 658c99a

Browse files
committed
SSI execsolib impl
1 parent 9293858 commit 658c99a

File tree

6 files changed

+118
-9
lines changed

6 files changed

+118
-9
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components-rs/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ hashbrown = "0.15"
5353

5454
[build-dependencies]
5555
cbindgen = "0.27"
56+
cc = "1"
5657

5758
[lints.rust]
5859
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(php_shared_build)'] }

components-rs/build.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
fn main() {
2+
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
3+
// SHARED=1 is set by config.m4 when building in rust-library-split (SSI) mode.
4+
// In that mode this crate is the cdylib that carries the Rust code.
5+
// We compile ssi_entry.c into it to make libddtrace_php.so directly executable
6+
// via the dynamic loader (ld.so).
7+
let shared_build = std::env::var("SHARED").as_deref() == Ok("1");
8+
9+
if target_os == "linux" && shared_build {
10+
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
11+
cc::Build::new()
12+
.file(format!("{manifest_dir}/ssi_entry.c"))
13+
.flag("-fvisibility=hidden")
14+
.compile("ssi_entry");
15+
16+
println!("cargo:rustc-link-arg=-Wl,-e,_dd_ssi_entry");
17+
}
18+
}

components-rs/ssi_entry.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// SSI entry point for libddtrace_php.so executed directly.
2+
//
3+
// The spawner invokes libddtrace_php.so via the dynamic loader explicitly:
4+
// execve(ld_path, [ld_path, lib_path, process_name, "", lib_path, deps..., symbol], envp)
5+
// ld.so loads libc and all other dependencies, then jumps to _dd_ssi_entry.
6+
// By that time the process is fully initialised (TLS, libc, allocator) so we
7+
// can use ordinary C library calls.
8+
//
9+
// argv layout as seen by _dd_ssi_entry (from the kernel stack):
10+
// argv[0] = ld_path ← skip
11+
// argv[1] = lib_path ← skip
12+
// argv[2] = process_name ← standard trampoline argv[0]
13+
// argv[3] = "" ← standard trampoline argv[1]
14+
// argv[4] = lib_path ← standard trampoline argv[2]
15+
// ...
16+
// argv[argc-1] = symbol_name
17+
//
18+
// In fact, this file's like a simplified version of trampoline.c. We ignore
19+
// everything but the symbol name, which we assume lives in libddtrace_php.so.
20+
21+
#define _GNU_SOURCE
22+
#include <dlfcn.h>
23+
#include <unistd.h>
24+
25+
struct trampoline_data {
26+
int argc;
27+
char **argv;
28+
char **dependency_paths;
29+
};
30+
31+
__attribute__((noreturn, used))
32+
static void ssi_main(int argc, char **argv)
33+
{
34+
if (argc < 4) _exit(1);
35+
argc -= 2;
36+
argv += 2;
37+
38+
const char *symbol = argv[argc - 1];
39+
40+
void (*fn)(struct trampoline_data *) =
41+
(void (*)(struct trampoline_data *))dlsym(RTLD_DEFAULT, symbol);
42+
43+
if (!fn) _exit(2);
44+
45+
struct trampoline_data td = { argc, argv, NULL };
46+
fn(&td);
47+
_exit(0);
48+
}
49+
50+
// Architecture-specific _start-like stub: read argc/argv from the kernel
51+
// stack and tail-call ssi_main.
52+
53+
#if defined(__aarch64__)
54+
__asm__(
55+
".text\n"
56+
".global _dd_ssi_entry\n"
57+
".type _dd_ssi_entry, @function\n"
58+
"_dd_ssi_entry:\n"
59+
" mov x29, #0\n"
60+
" mov x30, #0\n"
61+
" ldr x0, [sp]\n" /* argc */
62+
" add x1, sp, #8\n" /* argv */
63+
" b ssi_main\n" /* noreturn tail call */
64+
".size _dd_ssi_entry, .-_dd_ssi_entry\n"
65+
);
66+
#elif defined(__x86_64__)
67+
__asm__(
68+
".text\n"
69+
".global _dd_ssi_entry\n"
70+
".type _dd_ssi_entry, @function\n"
71+
"_dd_ssi_entry:\n"
72+
" xor %ebp, %ebp\n"
73+
" movl (%rsp), %edi\n" /* argc */
74+
" lea 8(%rsp), %rsi\n" /* argv */
75+
" jmp ssi_main\n" /* noreturn tail call */
76+
".size _dd_ssi_entry, .-_dd_ssi_entry\n"
77+
);
78+
#else
79+
# error "ssi_entry.c: unsupported architecture"
80+
#endif

config.m4

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -228,11 +228,16 @@ if test "$PHP_DDTRACE" != "no"; then
228228
DD_TRACE_PHP_SOURCES="$DD_TRACE_PHP_SOURCES \
229229
ext/compat_getrandom.c"
230230

231-
dnl On Linux, add the solib bootstrap (makes ddtrace.so directly executable)
231+
dnl On Linux, add the solib bootstrap (makes the library directly executable).
232+
dnl In rust-library-split (SSI) mode the bootstrap goes into libddtrace_php.so
233+
dnl via components-rs/build.rs instead, because that is the library that
234+
dnl carries the Rust code (and therefore DD_TRAMPOLINE_BIN).
232235
case $host_os in
233236
linux*)
234-
DD_TRACE_PHP_SOURCES="$DD_TRACE_PHP_SOURCES \
235-
ext/solib_bootstrap.c"
237+
if test "$PHP_DDTRACE_RUST_LIBRARY_SPLIT" = "no"; then
238+
DD_TRACE_PHP_SOURCES="$DD_TRACE_PHP_SOURCES \
239+
ext/solib_bootstrap.c"
240+
fi
236241
;;
237242
esac
238243

@@ -286,15 +291,19 @@ if test "$PHP_DDTRACE" != "no"; then
286291
EXTRA_CFLAGS="$EXTRA_CFLAGS -fvisibility=hidden"
287292
EXTRA_LDFLAGS="$EXTRA_LDFLAGS -export-symbols $ext_srcdir/ddtrace.sym -flto -fuse-linker-plugin"
288293

289-
dnl On Linux, set the ELF entry point so ddtrace.so can be executed directly
294+
dnl On Linux, set the ELF entry point so ddtrace.so can be executed directly.
295+
dnl In rust-library-split (SSI) mode the entry point is set on libddtrace_php.so
296+
dnl via components-rs/build.rs instead.
290297
case $host_os in
291298
linux*)
292-
EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-e,_dd_solib_start"
293-
dnl ExecSolib requires execute permission; PHP's make install defaults to
294-
dnl INSTALL_DATA = install -m 644. Override to 0755 so execve() works.
295-
cat <<'EOT' >> Makefile.fragments
299+
if test "$PHP_DDTRACE_RUST_LIBRARY_SPLIT" = "no"; then
300+
EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-e,_dd_solib_start"
301+
dnl ExecSolib requires execute permission; PHP's make install defaults to
302+
dnl INSTALL_DATA = install -m 644. Override to 0755 so execve() works.
303+
cat <<'EOT' >> Makefile.fragments
296304
INSTALL_DATA = $(INSTALL) -m 0755
297305
EOT
306+
fi
298307
;;
299308
esac
300309

libdatadog

0 commit comments

Comments
 (0)