Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,9 @@ if(USE_LOADER)
target_link_libraries(dd_profiling-shared PRIVATE libddprofiling_embedded_object
ddprof_exe_object)
target_compile_definitions(
dd_profiling-shared PRIVATE DDPROF_EMBEDDED_LIB_DATA DDPROF_EMBEDDED_EXE_DATA
DDPROF_PROFILING_LIBRARY)
dd_profiling-shared
PRIVATE DDPROF_EMBEDDED_LIB_DATA DDPROF_EMBEDDED_EXE_DATA DDPROF_PROFILING_LIBRARY
DDPROF_LOADER_SONAME="lib$<TARGET_PROPERTY:dd_profiling-shared,OUTPUT_NAME>.so")
else()
# Without loader, libdd_profiling.so is basically the same as libdd_profiling-embedded.so plus an
# embedded ddprof executable.
Expand Down
18 changes: 18 additions & 0 deletions docs/Troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,24 @@ example:
MALLOC_CONF=stats_print:true ./ddprof -S test-service service_cmd
```

### Loading libdd_profiling.so via dlopen

The recommended way to use `libdd_profiling.so` is to link against it directly
or use `LD_PRELOAD`. If `dlopen` is required (e.g. conditional profiling based
on a config flag):

```c
dlopen("libdd_profiling.so", RTLD_NOW);
```

The loader internally promotes its own symbols to global scope so that the
embedded library it loads can resolve them.

**musl limitation:** loading `libdd_profiling.so` with `dlopen` is not supported
on musl-based systems (e.g. Alpine Linux). musl rejects initial-exec TLS
cross-library relocations for `dlopen`'d libraries, which causes the embedded
library to fail to load. Use `LD_PRELOAD` instead on musl systems.

### Library issues (LD_PRELOAD or allocation profiling)

You will want to instrument the loader function (loader.c) to figure out what is going on.
Expand Down
30 changes: 30 additions & 0 deletions src/lib/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,35 @@ static void ensure_librt_is_loaded() {
}
}

// When the loader is dlopen'd with RTLD_GLOBAL, glibc does not promote its
// symbols to global scope until dlopen returns. The embedded .so references
// ddprof_lib_state (defined here in the loader) as an undefined symbol, so
// loading it with RTLD_NOW during our constructor fails with
// "undefined symbol: ddprof_lib_state".
//
// Fix: re-open ourselves with RTLD_NOLOAD | RTLD_GLOBAL to promote our
// symbols before loading the embedded .so. When loaded via LD_PRELOAD,
// symbols are already in global scope so this is a harmless no-op.
//
// Note: on musl, dlopen with RTLD_GLOBAL is not supported for this library
// because musl rejects initial-exec TLS cross-library relocations for
// dlopen'd libraries entirely.
static void ensure_loader_symbols_promoted() {
#ifdef DDPROF_LOADER_SONAME
void *self =
my_dlopen(DDPROF_LOADER_SONAME, RTLD_GLOBAL | RTLD_NOLOAD | RTLD_NOW);
if (!self) {
// RTLD_NOLOAD should always find the loader (we are running inside it).
// NULL means the soname has changed or something is seriously wrong;
// the embedded .so will likely fail to load next.
fprintf(stderr,
"ddprof loader: failed to promote symbols to global scope "
"(RTLD_NOLOAD on " DDPROF_LOADER_SONAME
" returned NULL) -- embedded library may fail to load\n");
}
#endif
}

static const char *temp_directory_path() {
const char *tmpdir = NULL;
const char *env[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR", NULL};
Expand Down Expand Up @@ -279,6 +308,7 @@ static void __attribute__((constructor)) loader() {
ensure_libm_is_loaded();
ensure_libpthread_is_loaded();
ensure_librt_is_loaded();
ensure_loader_symbols_promoted();

s_profiling_lib_handle = my_dlopen(lib_profiling_path, RTLD_LOCAL | RTLD_NOW);
free(lib_profiling_path);
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ if(NOT CMAKE_BUILD_TYPE STREQUAL "SanitizedDebug")
COMMAND ${CMAKE_SOURCE_DIR}/tools/check_for_unsafe_libc_functions.py
$<TARGET_FILE:dd_profiling-embedded>)
endif()

endif()

if(NOT CMAKE_BUILD_TYPE STREQUAL "SanitizedDebug")
Expand Down