Skip to content

Record dynamic dependencies in .note.dlopen elf section #13078

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 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
20 changes: 20 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ dep_option(SDL_ARMNEON "Use NEON assembly routines" ON "SDL_ASSEMBLY
dep_option(SDL_LSX "Use LSX assembly routines" ON "SDL_ASSEMBLY;SDL_CPU_LOONGARCH64" OFF)
dep_option(SDL_LASX "Use LASX assembly routines" ON "SDL_ASSEMBLY;SDL_CPU_LOONGARCH64" OFF)

dep_option(SDL_DLOPEN_NOTES "Record dlopen dependencies in .note.dlopen section" TRUE UNIX_SYS OFF)
set_option(SDL_LIBC "Use the system C library" ${SDL_LIBC_DEFAULT})
set_option(SDL_SYSTEM_ICONV "Use iconv() from system-installed libraries" ${SDL_SYSTEM_ICONV_DEFAULT})
set_option(SDL_LIBICONV "Prefer iconv() from libiconv, if available, over libc version" OFF)
Expand Down Expand Up @@ -1559,6 +1560,25 @@ elseif(EMSCRIPTEN)
CheckLibUnwind()

elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)

if(SDL_DLOPEN_NOTES)
set(CHECK_ELF_DLNOTES_SRC [==[
#ifndef __ELF__
ELF DL notes is only supported on ELF platforms
#endif
__attribute__ ((used,aligned(4),section(".note.dlopen"))) static const struct {
struct { int a; int b; int c; } hdr; char name[4]; __attribute__((aligned(4))) char json[24];
} dlnote = { { 4, 0x407c0c0aU, 16 }, "FDO", "[\\"a\\":{\\"a\\":\\"1\\",\\"b\\":\\"2\\"}]" };
int main(int argc, char *argv[]) {
return argc + dlnote.hdr.a;
}
]==])
check_c_source_compiles("${CHECK_ELF_DLNOTES_SRC}" COMPILER_SUPPORTS_ELFNOTES)
if(COMPILER_SUPPORTS_ELFNOTES)
set(HAVE_DLOPEN_NOTES TRUE)
endif()
endif()

if(SDL_AUDIO)
if(NETBSD)
set(SDL_AUDIO_DRIVER_NETBSD 1)
Expand Down
2 changes: 1 addition & 1 deletion docs/README-linux.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Ubuntu 22.04+ can also add `libpipewire-0.3-dev libwayland-dev libdecor-0-dev li
Fedora 35, all available features enabled:

sudo yum install gcc git-core make cmake \
alsa-lib-devel pulseaudio-libs-devel nas-devel pipewire-devel \
alsa-lib-devel pulseaudio-libs-devel pipewire-devel \
libX11-devel libXext-devel libXrandr-devel libXcursor-devel libXfixes-devel \
libXi-devel libXScrnSaver-devel dbus-devel ibus-devel \
systemd-devel mesa-libGL-devel libxkbcommon-devel mesa-libGLES-devel \
Expand Down
2 changes: 2 additions & 0 deletions include/build_config/SDL_build_config.h.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@
#cmakedefine HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR 1
#cmakedefine HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP 1

#cmakedefine HAVE_DLOPEN_NOTES 1

/* SDL internal assertion support */
#cmakedefine SDL_DEFAULT_ASSERT_LEVEL_CONFIGURED 1
#ifdef SDL_DEFAULT_ASSERT_LEVEL_CONFIGURED
Expand Down
1 change: 1 addition & 0 deletions src/SDL_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include "SDL_build_config.h"

#include "dynapi/SDL_dynapi.h"
#include "dynapi/SDL_dynapi_dlopennote.h"

#if SDL_DYNAMIC_API
#include "dynapi/SDL_dynapi_overrides.h"
Expand Down
11 changes: 9 additions & 2 deletions src/audio/aaudio/SDL_aaudio.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ struct SDL_PrivateAudioData

#define LIB_AAUDIO_SO "libaaudio.so"

SDL_ELF_NOTE_DLOPEN(
"audio-aaudio",
"Support for audio through AAudio",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
LIB_AAUDIO_SO
);

typedef struct AAUDIO_Data
{
SDL_SharedObject *handle;
Expand Down Expand Up @@ -308,8 +315,8 @@ static bool BuildAAudioStream(SDL_AudioDevice *device)
ctx.AAudioStreamBuilder_setFormat(builder, format);
ctx.AAudioStreamBuilder_setSampleRate(builder, device->spec.freq);
ctx.AAudioStreamBuilder_setChannelCount(builder, device->spec.channels);
// If no specific buffer size has been requested, the device will pick the optimal

// If no specific buffer size has been requested, the device will pick the optimal
if(SDL_GetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES)) {
ctx.AAudioStreamBuilder_setBufferCapacityInFrames(builder, 2 * device->sample_frames); // AAudio requires that the buffer capacity is at least
ctx.AAudioStreamBuilder_setFramesPerDataCallback(builder, device->sample_frames); // twice the size of the data callback buffer size
Expand Down
7 changes: 7 additions & 0 deletions src/audio/alsa/SDL_alsa_audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,13 @@ static bool load_alsa_syms(void)

#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC

SDL_ELF_NOTE_DLOPEN(
"audio-libalsa",
"Support for audio through libalsa",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_AUDIO_DRIVER_ALSA_DYNAMIC
);

static void UnloadALSALibrary(void)
{
if (alsa_handle) {
Expand Down
7 changes: 7 additions & 0 deletions src/audio/jack/SDL_jackaudio.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ static bool load_jack_syms(void);

#ifdef SDL_AUDIO_DRIVER_JACK_DYNAMIC

SDL_ELF_NOTE_DLOPEN(
"audio-libjack",
"Support for audio through libjack",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_AUDIO_DRIVER_JACK_DYNAMIC
);

static const char *jack_library = SDL_AUDIO_DRIVER_JACK_DYNAMIC;
static SDL_SharedObject *jack_handle = NULL;

Expand Down
7 changes: 7 additions & 0 deletions src/audio/pipewire/SDL_pipewire.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ static int (*PIPEWIRE_pw_properties_setf)(struct pw_properties *, const char *,

#ifdef SDL_AUDIO_DRIVER_PIPEWIRE_DYNAMIC

SDL_ELF_NOTE_DLOPEN(
"audio-libpipewire",
"Support for audio through libpipewire",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_AUDIO_DRIVER_PIPEWIRE_DYNAMIC
);

static const char *pipewire_library = SDL_AUDIO_DRIVER_PIPEWIRE_DYNAMIC;
static SDL_SharedObject *pipewire_handle = NULL;

Expand Down
7 changes: 7 additions & 0 deletions src/audio/pulseaudio/SDL_pulseaudio.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ static bool load_pulseaudio_syms(void);

#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC

SDL_ELF_NOTE_DLOPEN(
"audio-libpulseaudio",
"Support for audio through libpulseaudio",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC
);

static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC;
static SDL_SharedObject *pulseaudio_handle = NULL;

Expand Down
7 changes: 7 additions & 0 deletions src/audio/sndio/SDL_sndioaudio.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ static bool load_sndio_syms(void)

#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC

SDL_ELF_NOTE_DLOPEN(
"audio-libsndio",
"Support for audio through libsndio",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
);

static void UnloadSNDIOLibrary(void)
{
if (sndio_handle) {
Expand Down
7 changes: 7 additions & 0 deletions src/camera/pipewire/SDL_camera_pipewire.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ static int (*PIPEWIRE_pw_properties_setf)(struct pw_properties *, const char *,

#ifdef SDL_CAMERA_DRIVER_PIPEWIRE_DYNAMIC

SDL_ELF_NOTE_DLOPEN(
"camera-libpipewire",
"Support for camera through libalsa",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_CAMERA_DRIVER_PIPEWIRE_DYNAMIC
);

static const char *pipewire_library = SDL_CAMERA_DRIVER_PIPEWIRE_DYNAMIC;
static SDL_SharedObject *pipewire_handle = NULL;

Expand Down
10 changes: 9 additions & 1 deletion src/core/linux/SDL_dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,20 @@

#ifdef SDL_USE_LIBDBUS
// we never link directly to libdbus.
static const char *dbus_library = "libdbus-1.so.3";
#define SDL_DRIVER_DBUS_DYNAMIC "libdbus-1.so.3"
static const char *dbus_library = SDL_DRIVER_DBUS_DYNAMIC;
static SDL_SharedObject *dbus_handle = NULL;
static char *inhibit_handle = NULL;
static unsigned int screensaver_cookie = 0;
static SDL_DBusContext dbus;

SDL_ELF_NOTE_DLOPEN(
"core-libdbus",
"Support for DBus RPC",
SDL_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED,
SDL_DRIVER_DBUS_DYNAMIC
);

static bool LoadDBUSSyms(void)
{
#define SDL_DBUS_SYM2_OPTIONAL(TYPE, x, y) \
Expand Down
11 changes: 11 additions & 0 deletions src/core/linux/SDL_udev.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@

static const char *SDL_UDEV_LIBS[] = { "libudev.so.1", "libudev.so.0" };

#ifdef SDL_UDEV_DYNAMIC

SDL_ELF_NOTE_DLOPEN(
"events-udev",
"Support for events through libudev",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_UDEV_DYNAMIC
);

#endif

static SDL_UDEV_PrivateData *_this = NULL;

static bool SDL_UDEV_load_sym(const char *fn, void **addr);
Expand Down
98 changes: 98 additions & 0 deletions src/dynapi/SDL_dynapi_dlopennote.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

#ifndef SDL_dynapi_dlopennote_h
#define SDL_dynapi_dlopennote_h

#define SDL_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED "required"
#define SDL_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED "recommended"
#define SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED "suggested"

#if defined(__ELF__) && defined(HAVE_DLOPEN_NOTES)

#define SDL_ELF_NOTE_DLOPEN_VENDOR "FDO"
#define SDL_ELF_NOTE_DLOPEN_TYPE 0x407c0c0aU

#define SDL_ELF_NOTE_INTERNAL2(json, variable_name) \
__attribute__((aligned(4), used, section(".note.dlopen"))) \
static const struct { \
struct { \
Uint32 n_namesz; \
Uint32 n_descsz; \
Uint32 n_type; \
} nhdr; \
char name[4]; \
__attribute__((aligned(4))) char dlopen_json[sizeof(json)]; \
} variable_name = { \
{ \
sizeof(SDL_ELF_NOTE_DLOPEN_VENDOR), \
sizeof(json), \
SDL_ELF_NOTE_DLOPEN_TYPE \
}, \
SDL_ELF_NOTE_DLOPEN_VENDOR, \
json \
}

#define SDL_ELF_NOTE_INTERNAL(json, variable_name) \
SDL_ELF_NOTE_INTERNAL2(json, variable_name)

#define SDL_SONAME_ARRAY1(N1) "[\"" N1 "\"]"
#define SDL_SONAME_ARRAY2(N1,N2) "[\"" N1 "\",\"" N2 "\"]"
#define SDL_SONAME_ARRAY3(N1,N2,N3) "[\"" N1 "\",\"" N2 "\",\"" N3 "\"]"
#define SDL_SONAME_ARRAY4(N1,N2,N3,N4) "[\"" N1 "\",\"" N2 "\",\"" N3 "\",\"" N4 "\"]"
#define SDL_SONAME_ARRAY5(N1,N2,N3,N4,N5) "[\"" N1 "\",\"" N2 "\",\"" N3 "\",\"" N4 "\",\"" N5 "\"]"
#define SDL_SONAME_ARRAY6(N1,N2,N3,N4,N5,N6) "[\"" N1 "\",\"" N2 "\",\"" N3 "\",\"" N4 "\",\"" N5 "\",\"" N6 "\"]"
#define SDL_SONAME_ARRAY7(N1,N2,N3,N4,N5,N6,N7) "[\"" N1 "\",\"" N2 "\",\"" N3 "\",\"" N4 "\",\"" N5 "\",\"" N6 "\",\"" N7 "\"]"
#define SDL_SONAME_ARRAY8(N1,N2,N3,N4,N5,N6,N7,N8) "[\"" N1 "\",\"" N2 "\",\"" N3 "\",\"" N4 "\",\"" N5 "\",\"" N6 "\",\"" N7 "\",\"" N8 "\"]"
#define SDL_SONAME_ARRAY_GET(N1,N2,N3,N4,N5,N6,N7,N8,NAME,...) NAME
#define SDL_SONAME_ARRAY(...) \
SDL_SONAME_ARRAY_GET(__VA_ARGS__, \
SDL_SONAME_ARRAY8, \
SDL_SONAME_ARRAY7, \
SDL_SONAME_ARRAY6, \
SDL_SONAME_ARRAY5, \
SDL_SONAME_ARRAY4, \
SDL_SONAME_ARRAY3, \
SDL_SONAME_ARRAY2, \
SDL_SONAME_ARRAY1 \
)(__VA_ARGS__)

// Create "unique" variable name using __LINE__,
// so creating elf notes on the same line is not supported
#define SDL_ELF_NOTE_JOIN2(A,B) A##B
#define SDL_ELF_NOTE_JOIN(A,B) SDL_ELF_NOTE_JOIN2(A,B)
#define SDL_ELF_NOTE_UNIQUE_NAME SDL_ELF_NOTE_JOIN(s_dlopen_note_, __LINE__)

#define SDL_ELF_NOTE_DLOPEN(feature, description, priority, ...) \
SDL_ELF_NOTE_INTERNAL( \
"[{\"feature\":\"" feature \
"\",\"description\":\"" description \
"\",\"priority\":\"" priority \
"\",\"soname\":" SDL_SONAME_ARRAY(__VA_ARGS__) "}]", \
SDL_ELF_NOTE_UNIQUE_NAME)

#else

#define SDL_ELF_NOTE_DLOPEN(...)

#endif

#endif
9 changes: 9 additions & 0 deletions src/hidapi/SDL_hidapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@

#ifndef SDL_HIDAPI_DISABLED

#ifdef SDL_LIBUSB_DYNAMIC
SDL_ELF_NOTE_DLOPEN(
"hidabi-libusb",
"Support for joysticks through libusb",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_LIBUSB_DYNAMIC
);
#endif

#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
#include "../core/windows/SDL_windows.h"
#endif
Expand Down
10 changes: 9 additions & 1 deletion src/io/io_uring/SDL_asyncio_liburing.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,17 @@ static bool (*AsyncIOFromFile)(const char *file, const char *mode, SDL_AsyncIO *
// we never link directly to liburing.
// (this says "-ffi" which sounds like a scripting language binding thing, but the non-ffi version
// is static-inline code we can't lookup with dlsym. This is by design.)
static const char *liburing_library = "liburing-ffi.so.2";
#define SDL_DRIVER_LIBURING_DYNAMIC "liburing-ffi.so.2"
static const char *liburing_library = SDL_DRIVER_LIBURING_DYNAMIC;
static void *liburing_handle = NULL;

SDL_ELF_NOTE_DLOPEN(
"io-io_uring",
"Support for async IO through liburing",
SDL_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED,
SDL_DRIVER_LIBURING_DYNAMIC
);

#define SDL_LIBURING_FUNCS \
SDL_LIBURING_FUNC(int, io_uring_queue_init, (unsigned entries, struct io_uring *ring, unsigned flags)) \
SDL_LIBURING_FUNC(struct io_uring_probe *,io_uring_get_probe,(void)) \
Expand Down
29 changes: 18 additions & 11 deletions src/storage/steam/SDL_steamstorage.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,23 @@

#include "../SDL_sysstorage.h"

#if defined(_WIN64)
#define SDL_DRIVER_STEAMAPI_DYNAMIC "steam_api64.dll"
#elif defined(_WIN32)
#define SDL_DRIVER_STEAMAPI_DYNAMIC "steam_api.dll"
#elif defined(__APPLE__)
#define SDL_DRIVER_STEAMAPI_DYNAMIC "libsteam_api.dylib"
#else
#define SDL_DRIVER_STEAMAPI_DYNAMIC "libsteam_api.so"
#endif

SDL_ELF_NOTE_DLOPEN(
"storage-steam",
"Support for Steam storage",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_DRIVER_STEAMAPI_DYNAMIC
);

// !!! FIXME: Async API can use SteamRemoteStorage_ReadFileAsync
// !!! FIXME: Async API can use SteamRemoteStorage_WriteFileAsync

Expand Down Expand Up @@ -154,17 +171,7 @@ static SDL_Storage *STEAM_User_Create(const char *org, const char *app, SDL_Prop
return NULL;
}

steam->libsteam_api = SDL_LoadObject(
#if defined(_WIN64)
"steam_api64.dll"
#elif defined(_WIN32)
"steam_api.dll"
#elif defined(__APPLE__)
"libsteam_api.dylib"
#else
"libsteam_api.so"
#endif
);
steam->libsteam_api = SDL_LoadObject(SDL_DRIVER_STEAMAPI_DYNAMIC);
if (steam->libsteam_api == NULL) {
SDL_free(steam);
return NULL;
Expand Down
Loading
Loading