Hi Luca, I did some testing of your first 2 patches of this series and found a couple of test errors on various platforms:
CentOS 7 gcc -std=gnu99 -std=gnu11 -DHAVE_CONFIG_H -I. -I../../gltests -I.. -DGNULIB_STRICT_CHECKING=1 -DIN_GNULIB_TESTS=1 -I. -I../../gltests -I.. -I../../gltests/.. -I../gllib -I../../gltests/../gllib -Wall -DCONTINUE_AFTER_ASSERT -Wno-error -Wno-error -g -O2 -MT test-sd-dlopen.o -MD -MP -MF .deps/test-sd-dlopen.Tpo -c -o test-sd-dlopen.o ../../gltests/test-sd-dlopen.c /tmp/ccsYRr90.s: Assembler messages: /tmp/ccsYRr90.s:7: Fatal error: bad .section directive: want a,l,w,x,M,S,G,T in string make[4]: *** [test-sd-dlopen.o] Error 1 AlmaLinux 9 gcc -std=gnu99 -std=gnu11 -DHAVE_CONFIG_H -I. -I../../gltests -I.. -DGNULIB_STRICT_CHECKING=1 -DIN_GNULIB_TESTS=1 -I. -I../../gltests -I.. -I../../gltests/.. -I../gllib -I../../gltests/../gllib -Wall -DCONTINUE_AFTER_ASSERT -Wno-error -Wno-error -g -O2 -MT test-sd-dlopen.o -MD -MP -MF .deps/test-sd-dlopen.Tpo -c -o test-sd-dlopen.o ../../gltests/test-sd-dlopen.c /tmp/ccTZtxDv.s: Assembler messages: /tmp/ccTZtxDv.s:8: Fatal error: bad .section directive: want a,l,w,x,M,S,G,T in string make[4]: *** [Makefile:1494: test-sd-dlopen.o] Error 1 NetBSD 10.0 gcc -DHAVE_CONFIG_H -I. -I../../gltests -I.. -DGNULIB_STRICT_CHECKING=1 -DIN_GNULIB_TESTS=1 -I. -I../../gltests -I.. -I../../gltests/.. -I../gllib -I../../gltests/../gllib -I/usr/pkg/include -Wall -DCONTINUE_AFTER_ASSERT -Wno-error -Wno-error -g -O2 -MT test-sd-dlopen.o -MD -MP -MF .deps/test-sd-dlopen.Tpo -c -o test-sd-dlopen.o ../../gltests/test-sd-dlopen.c /tmp//ccObcS4r.s: Assembler messages: /tmp//ccObcS4r.s:7: Fatal error: bad .section directive: want a,l,w,x,M,S,G,T in string *** Error code 1 Solaris 11.4 gcc -m64 -Wno-error -Wno-error -g -O2 -L/root/lib -o test-sd-dlopen test-sd-dlopen.o libtests.a ../gllib/libgnu.a libtests.a ../gllib/libgnu.a libtests.a ld: fatal: file test-sd-dlopen.o: wrong ELF OSABI: ELFOSABI_GNU collect2: error: ld returned 1 exit status *** Error code 1 Therefore, once the copyright assignment paperwork is complete, here is an augmented set of patches that I plan to commit: the two commits from you (with GNU-style ChangeLog and a correction in m4/libdl.m4) and 4 commits (1 for clarity, 3 to avoid the errors above).
>From ba276ea0689777fa1496e4ef20ba56e1fb7a62a6 Mon Sep 17 00:00:00 2001 From: Luca Boccassi <[email protected]> Date: Sun, 19 Jul 2026 18:29:18 +0100 Subject: [PATCH 2/6] sd-dlopen: Add tests. * tests/test-sd-dlopen.c: New file. * tests/test-sd-dlopen-disabled.c: New file. * tests/test-sd-dlopen.sh: New file. * modules/sd-dlopen-tests: New file. --- ChangeLog | 6 +++++ modules/sd-dlopen-tests | 19 +++++++++++++++ tests/test-sd-dlopen-disabled.c | 41 +++++++++++++++++++++++++++++++++ tests/test-sd-dlopen.c | 36 +++++++++++++++++++++++++++++ tests/test-sd-dlopen.sh | 21 +++++++++++++++++ 5 files changed, 123 insertions(+) create mode 100644 modules/sd-dlopen-tests create mode 100644 tests/test-sd-dlopen-disabled.c create mode 100644 tests/test-sd-dlopen.c create mode 100755 tests/test-sd-dlopen.sh diff --git a/ChangeLog b/ChangeLog index 31c871b53c..115446119d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2026-07-21 Luca Boccassi <[email protected]> + sd-dlopen: Add tests. + * tests/test-sd-dlopen.c: New file. + * tests/test-sd-dlopen-disabled.c: New file. + * tests/test-sd-dlopen.sh: New file. + * modules/sd-dlopen-tests: New file. + sd-dlopen: New module. * lib/sd-dlopen.h: New file, based on systemd/src/systemd/sd-dlopen.h. * m4/libdl.m4 (gl_LIBDL): Also set HAVE_DLOPEN and define HAVE_DLOPEN. diff --git a/modules/sd-dlopen-tests b/modules/sd-dlopen-tests new file mode 100644 index 0000000000..fdbd652755 --- /dev/null +++ b/modules/sd-dlopen-tests @@ -0,0 +1,19 @@ +Files: +tests/test-sd-dlopen.c +tests/test-sd-dlopen-disabled.c +tests/test-sd-dlopen.sh + +Depends-on: +test-framework-sh + +configure.ac: +AC_CHECK_TOOL([OBJDUMP], [objdump], [false]) +SD_DLOPEN_SUPPORTED=$gl_cv_asm_ifndef_endif_balign +AC_SUBST([SD_DLOPEN_SUPPORTED]) + +Makefile.am: +TESTS_ENVIRONMENT += \ + OBJDUMP='$(OBJDUMP)' \ + SD_DLOPEN_SUPPORTED='$(SD_DLOPEN_SUPPORTED)' +TESTS += test-sd-dlopen test-sd-dlopen-disabled test-sd-dlopen.sh +check_PROGRAMS += test-sd-dlopen test-sd-dlopen-disabled diff --git a/tests/test-sd-dlopen-disabled.c b/tests/test-sd-dlopen-disabled.c new file mode 100644 index 0000000000..a23d3cd6d6 --- /dev/null +++ b/tests/test-sd-dlopen-disabled.c @@ -0,0 +1,41 @@ +/* Test the disabled sd-dlopen macros. + Copyright 2026 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +#include <config.h> + +#undef HAVE_ASM_IFNDEF_ENDIF_BALIGN + +#include "sd-dlopen.h" + +#ifdef _SD_ELF_NOTE_DLOPEN +# error "sd-dlopen implementation enabled without assembler support" +#endif + +SD_ELF_NOTE_DLOPEN ("test", + "Test optional dependency", + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + "libtest.so.1"); + +int +main (void) +{ + SD_ELF_NOTE_DLOPEN_ANCHORED (test_required, + "test", + "Test required dependency", + SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED, + "libtest.so.1"); + return 0; +} diff --git a/tests/test-sd-dlopen.c b/tests/test-sd-dlopen.c new file mode 100644 index 0000000000..1faaa36c2c --- /dev/null +++ b/tests/test-sd-dlopen.c @@ -0,0 +1,36 @@ +/* Test of sd-dlopen module. + Copyright 2026 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include "sd-dlopen.h" + +SD_ELF_NOTE_DLOPEN ("test", + "Test optional dependency", + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + "libtest.so.1"); + +int +main (void) +{ + SD_ELF_NOTE_DLOPEN_ANCHORED (test_required, + "test", + "Test required dependency", + SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED, + "libtest.so.1"); + + return SD_ELF_NOTE_DLOPEN_TYPE != 0x407c0c0a; +} diff --git a/tests/test-sd-dlopen.sh b/tests/test-sd-dlopen.sh new file mode 100755 index 0000000000..36dd3baae2 --- /dev/null +++ b/tests/test-sd-dlopen.sh @@ -0,0 +1,21 @@ +#!/bin/sh +. "${srcdir=.}/init.sh" + +test "$SD_DLOPEN_SUPPORTED" = yes \ + || skip_ ".note.dlopen is not supported" +test "$OBJDUMP" != false \ + || skip_ "objdump is not available" + +program="$initial_cwd_/test-sd-dlopen${EXEEXT}" +LC_ALL=C "$OBJDUMP" -h "$program" > sections \ + || skip_ "objdump cannot read the test executable" +grep '[.]note[.]dlopen' sections > /dev/null \ + || fail_ ".note.dlopen section is missing" + +LC_ALL=C "$OBJDUMP" -s -j .note.dlopen "$program" > contents \ + || fail_ "cannot dump the .note.dlopen section" +vendor_count=$(grep -c '46444f00' contents) +test "$vendor_count" = 2 \ + || fail_ "expected two FDO dlopen notes, found $vendor_count" + +Exit 0 -- 2.53.0
>From 5183e0053df3ffd84b94eb501d117b209fc08cd6 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Tue, 21 Jul 2026 22:29:23 +0200 Subject: [PATCH 4/6] sd-dlopen: Fix test compilation error on AlmaLinux 9 and CentOS 7. * m4/sd-dlopen.m4 (gl_SD_DLOPEN): Test whether the assembler supports the section flag R. Define _SD_ELF_NOTE_DLOPEN_SECTION_FLAGS. --- ChangeLog | 4 ++++ m4/sd-dlopen.m4 | 20 +++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index a85e110501..b04ff54e95 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2026-07-21 Bruno Haible <[email protected]> + sd-dlopen: Fix test compilation error on AlmaLinux 9 and CentOS 7. + * m4/sd-dlopen.m4 (gl_SD_DLOPEN): Test whether the assembler supports + the section flag R. Define _SD_ELF_NOTE_DLOPEN_SECTION_FLAGS. + sd-dlopen: Reference the specification. * modules/sd-dlopen (Description): Add reference to the specification. * lib/sd-dlopen.h: Improve comments. diff --git a/m4/sd-dlopen.m4 b/m4/sd-dlopen.m4 index 1511548dfb..3f0e2cea13 100644 --- a/m4/sd-dlopen.m4 +++ b/m4/sd-dlopen.m4 @@ -1,5 +1,5 @@ # sd-dlopen.m4 -# serial 1 +# serial 2 dnl Copyright (C) 2026 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -26,5 +26,23 @@ AC_DEFUN([gl_SD_DLOPEN] if test $gl_cv_asm_ifndef_endif_balign = yes; then AC_DEFINE([HAVE_ASM_IFNDEF_ENDIF_BALIGN], [1], [Define to 1 if the assembler supports .ifndef, .endif, and .balign.]) + + AC_CACHE_CHECK([whether the assembler supports the section flag R], + [gl_cv_asm_section_R], + [dnl This flag is only supported by binutils >= 2.36 or clang >= 13. + AC_COMPILE_IFELSE( + [AC_LANG_SOURCE([[ + __asm__ (".section .foo,\"aR\""); + ]])], + [gl_cv_asm_section_R=yes], + [gl_cv_asm_section_R=no])]) + if test $gl_cv_asm_section_R = yes; then + gl_sd_dlopen_section_flags='"aGR"' + else + gl_sd_dlopen_section_flags='"aG"' + fi + AC_DEFINE_UNQUOTED([_SD_ELF_NOTE_DLOPEN_SECTION_FLAGS], + [$gl_sd_dlopen_section_flags], + [Define to the section flags for sd-dlopen.h.]) fi ]) -- 2.53.0
>From 593badd99399c4f0940598b537fb5711bf12efbf Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Tue, 21 Jul 2026 23:51:05 +0200 Subject: [PATCH 5/6] sd-dlopen: Fix test compilation error on NetBSD 10. * m4/sd-dlopen.m4 (gl_SD_DLOPEN): Test whether the assembler supports the section flag o. Define _SD_ELF_NOTE_SUPPORTS_REFERENCES. * lib/sd-dlopen.h (SD_ELF_NOTE_DLOPEN_ANCHORED) [!__clang__]: Use _SD_ELF_NOTE_SUPPORTS_REFERENCES from the autoconfiguration. --- ChangeLog | 6 ++++++ lib/sd-dlopen.h | 6 +++--- m4/sd-dlopen.m4 | 16 +++++++++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index b04ff54e95..d23b321025 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2026-07-21 Bruno Haible <[email protected]> + sd-dlopen: Fix test compilation error on NetBSD 10. + * m4/sd-dlopen.m4 (gl_SD_DLOPEN): Test whether the assembler supports + the section flag o. Define _SD_ELF_NOTE_SUPPORTS_REFERENCES. + * lib/sd-dlopen.h (SD_ELF_NOTE_DLOPEN_ANCHORED) [!__clang__]: Use + _SD_ELF_NOTE_SUPPORTS_REFERENCES from the autoconfiguration. + sd-dlopen: Fix test compilation error on AlmaLinux 9 and CentOS 7. * m4/sd-dlopen.m4 (gl_SD_DLOPEN): Test whether the assembler supports the section flag R. Define _SD_ELF_NOTE_DLOPEN_SECTION_FLAGS. diff --git a/lib/sd-dlopen.h b/lib/sd-dlopen.h index 87760ada7a..b3e36ccd77 100644 --- a/lib/sd-dlopen.h +++ b/lib/sd-dlopen.h @@ -182,12 +182,12 @@ extern "C" { _SD_ELF_NOTE_DLOPEN(_SD_DLOPEN_JSON(feature, description, priority, __VA_ARGS__)) /* The anchored note requires LLVM >= 18 (see above). Fall back to the non-anchored note on older clang. */ -#if defined(__clang__) && !defined(__apple_build_version__) && __clang_major__ < 18 +#if (defined(__clang__) ? defined(__apple_build_version__) || __clang_major__ >= 18 : _SD_ELF_NOTE_SUPPORTS_REFERENCES) # define SD_ELF_NOTE_DLOPEN_ANCHORED(tag, feature, description, priority, ...) \ - SD_ELF_NOTE_DLOPEN(feature, description, priority, __VA_ARGS__) + _SD_ELF_NOTE_DLOPEN_ANCHORED(tag, _SD_DLOPEN_JSON(feature, description, priority, __VA_ARGS__)) #else # define SD_ELF_NOTE_DLOPEN_ANCHORED(tag, feature, description, priority, ...) \ - _SD_ELF_NOTE_DLOPEN_ANCHORED(tag, _SD_DLOPEN_JSON(feature, description, priority, __VA_ARGS__)) + SD_ELF_NOTE_DLOPEN(feature, description, priority, __VA_ARGS__) #endif #else diff --git a/m4/sd-dlopen.m4 b/m4/sd-dlopen.m4 index 3f0e2cea13..d9214b08bb 100644 --- a/m4/sd-dlopen.m4 +++ b/m4/sd-dlopen.m4 @@ -1,5 +1,5 @@ # sd-dlopen.m4 -# serial 2 +# serial 3 dnl Copyright (C) 2026 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -27,6 +27,20 @@ AC_DEFUN([gl_SD_DLOPEN] AC_DEFINE([HAVE_ASM_IFNDEF_ENDIF_BALIGN], [1], [Define to 1 if the assembler supports .ifndef, .endif, and .balign.]) + AC_CACHE_CHECK([whether the assembler supports the section flag o], + [gl_cv_asm_section_o], + [dnl This flag is only supported by binutils >= 2.35 or clang >= 5. + AC_COMPILE_IFELSE( + [AC_LANG_SOURCE([[ + __asm__ (".section .def; foobar:; .section .foo,\"ao\",%note,foobar"); + ]])], + [gl_cv_asm_section_o=yes], + [gl_cv_asm_section_o=no])]) + if test $gl_cv_asm_section_o = yes; then + AC_DEFINE([_SD_ELF_NOTE_SUPPORTS_REFERENCES], [1], + [Define to 1 if the ELF .section command supports the o flag.]) + fi + AC_CACHE_CHECK([whether the assembler supports the section flag R], [gl_cv_asm_section_R], [dnl This flag is only supported by binutils >= 2.36 or clang >= 13. -- 2.53.0
From a876f16e2fa532d6a801e8f8c4c2d22745065a99 Mon Sep 17 00:00:00 2001 From: Luca Boccassi <[email protected]> Date: Sun, 19 Jul 2026 18:29:17 +0100 Subject: [PATCH 1/6] sd-dlopen: New module. * lib/sd-dlopen.h: New file, based on systemd/src/systemd/sd-dlopen.h. * m4/libdl.m4 (gl_LIBDL): Also set HAVE_DLOPEN and define HAVE_DLOPEN. * m4/sd-dlopen.m4: New file. * modules/sd-dlopen: New file. --- ChangeLog | 8 ++ lib/sd-dlopen.h | 203 ++++++++++++++++++++++++++++++++++++++++++++++ m4/libdl.m4 | 21 +++-- m4/sd-dlopen.m4 | 30 +++++++ modules/sd-dlopen | 27 ++++++ 5 files changed, 283 insertions(+), 6 deletions(-) create mode 100644 lib/sd-dlopen.h create mode 100644 m4/sd-dlopen.m4 create mode 100644 modules/sd-dlopen diff --git a/ChangeLog b/ChangeLog index bd8952adf7..31c871b53c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2026-07-21 Luca Boccassi <[email protected]> + + sd-dlopen: New module. + * lib/sd-dlopen.h: New file, based on systemd/src/systemd/sd-dlopen.h. + * m4/libdl.m4 (gl_LIBDL): Also set HAVE_DLOPEN and define HAVE_DLOPEN. + * m4/sd-dlopen.m4: New file. + * modules/sd-dlopen: New file. + 2026-07-21 Bruno Haible <[email protected]> mbiter-aux: Avoid gcc -Wcomment warning. diff --git a/lib/sd-dlopen.h b/lib/sd-dlopen.h new file mode 100644 index 0000000000..3f3b275e10 --- /dev/null +++ b/lib/sd-dlopen.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: MIT-0 */ +#ifndef foosddlopenhfoo +#define foosddlopenhfoo + +/*** + Copyright © 2026 The systemd Project + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +/* Note: do not include other internal headers from here, to keep it MIT-0 and self-contained */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ELF note macros implementing the FDO .note.dlopen standard. + * + * These macros embed metadata in an ELF binary's .note.dlopen section, + * declaring optional shared library dependencies that are loaded via + * dlopen() at runtime. Package managers and build systems can read + * these notes to discover runtime dependencies not visible in ELF + * DT_NEEDED entries. + * + * Usage: + * + * SD_ELF_NOTE_DLOPEN("myfeature", "Feature description", + * SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + * "libfoo.so.1"); + * + * See SD_ELF_NOTE_DLOPEN(3) for details. + */ + +#define SD_ELF_NOTE_DLOPEN_VENDOR "FDO" +#define SD_ELF_NOTE_DLOPEN_TYPE 0x407c0c0a +#define SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED "required" +#define SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED "recommended" +#define SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED "suggested" + +#if defined __ELF__ && (defined __GNUC__ || defined __clang__) \ + && defined HAVE_ASM_IFNDEF_ENDIF_BALIGN && HAVE_ASM_IFNDEF_ENDIF_BALIGN + +/* The "R" (SHF_GNU_RETAIN) flag is only understood by binutils >= 2.36, so it can be disabled by defining + * _SD_ELF_NOTE_DLOPEN_SECTION_FLAGS as 'aG' manually, but note that if --gc-sections is used when linking, + * the note will be dropped. */ +#ifndef _SD_ELF_NOTE_DLOPEN_SECTION_FLAGS +# define _SD_ELF_NOTE_DLOPEN_SECTION_FLAGS "aGR" +#endif + +/* This guard tag ensures the assembler folds identical notes when compiling. Unfortunately hppa redefines + * '.equ' so it cannot be used. '.equiv' works on all arches, but it breaks with binutils 2.35 which is used + * on CentOS 9, so we need an ifdef here until the binutils baseline is updated, as above. */ +#if defined(__hppa__) || defined(__hppa64__) +# define _SD_ELF_NOTE_DLOPEN_GUARD ".set" +#else +# define _SD_ELF_NOTE_DLOPEN_GUARD ".equ" +#endif + +/* The json argument is a C string containing already backslash-escaped double quotes (i.e. the bytes + * [{\"feature\"...), so that once it is pasted into the assembler's .asciz directive the assembler decodes + * them back to plain quotes. The note is emitted into a COMDAT group keyed on the payload itself, which + * makes byte-identical notes fold to a single copy: the assembler folds duplicates within a translation + * unit (via the .ifndef guard) and the linker folds them across translation units (via the COMDAT group). + * This way a binary that triggers the same optional dependency from multiple call sites ends up with just + * one note for it. The n_type value below must match SD_ELF_NOTE_DLOPEN_TYPE (it is spelled out as a + * literal here as it cannot be expanded inside the assembler string). */ +#define _SD_ELF_NOTE_DLOPEN(json) \ + __asm__ ( \ + ".ifndef \"sd_dlopen:" json "\"\n" \ + _SD_ELF_NOTE_DLOPEN_GUARD " \"sd_dlopen:" json "\", 1\n" /* guard tag, to fold identical notes */ \ + ".pushsection .note.dlopen, \"" _SD_ELF_NOTE_DLOPEN_SECTION_FLAGS "\", %note, \"sd_dlopen:" json "\", comdat\n" \ + ".balign 4\n" /* notes require 4-byte alignment for the header and fields */ \ + ".long 884f - 883f\n" /* n_namesz: byte length of the vendor name (label 883->884) */ \ + ".long 882f - 881f\n" /* n_descsz: byte length of the JSON payload (label 881->882) */ \ + ".long 0x407c0c0a\n" /* n_type: must match SD_ELF_NOTE_DLOPEN_TYPE */ \ + "883:\n" /* start of vendor name */ \ + ".asciz \"" SD_ELF_NOTE_DLOPEN_VENDOR "\"\n" \ + "884:\n" /* end of vendor name */ \ + ".balign 4\n" \ + "881:\n" /* start of JSON payload */ \ + ".asciz \"" json "\"\n" \ + "882:\n" /* end of JSON payload */ \ + ".balign 4\n" \ + ".popsection\n" \ + ".endif\n") + +/* + * SD_ELF_NOTE_DLOPEN_ANCHORED() emits a .note.dlopen ELF note that is "anchored" to a dummy symbol via the + * SHF_LINK_ORDER ('o') section flag, in addition to SHF_GROUP ('G') for folding identical notes together. + * + * Unlike the plain SD_ELF_NOTE_DLOPEN() macro, this variant ties the note's lifetime to a dummy symbol named + * with the specified tag: if the linker's --gc-sections removes the function that calls the macro (e.g. + * because the function is never referenced), the associated .note.dlopen entry is garbage-collected along + * with it. + * + * USAGE NOTE: + * - The 'tag' argument must be a unique symbol name to characterize the dlopen note (e.g. generated from + * feature and its priority). + * + * Example: + * void dlopen_libfoo(int log_level) { + * SD_ELF_NOTE_DLOPEN_ANCHORED(foo_suggested, "foo", "description of foo", "suggested", "libfoo.so.0"); + * ... + * } + * + * TOOLCHAIN REQUIREMENTS: + * - GNU as (binutils) >= 2.35: this is when support for specifying the SHF_LINK_ORDER ('o') associated + * symbol as a direct argument to .section/.pushsection was added. + * - LLVM (clang -integrated-as) >= 18: matching support for the 'o' flag argument combined with 'G' + * (SHF_GROUP) landed around LLVM 18. + * - Do NOT add 'R' (SHF_GNU_RETAIN) to these flags: combined with SHF_LINK_ORDER, it would prevent the + * linker from ever garbage-collecting the note, defeating the whole purpose of anchoring it to a symbol in + * the first place. + */ +#define _SD_ELF_NOTE_DLOPEN_ANCHORED(tag, json) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wnested-externs\"") \ + _Pragma("GCC diagnostic ignored \"-Wredundant-decls\"") \ + __attribute__((visibility("hidden"))) \ + extern volatile const char __sd_dlopen_anchor_##tag __asm__("__sd_dlopen_anchor_" #tag); \ + _Pragma("GCC diagnostic pop") \ + __asm__ ( \ + ".ifndef \"sd_dlopen:emitted:" #tag "\"\n" \ + _SD_ELF_NOTE_DLOPEN_GUARD " \"sd_dlopen:emitted:" #tag "\", 1\n" \ + ".pushsection .data.sd_dlopen_dummy." #tag ", \"awG\", %progbits, sd_dlopen_group_" #tag ", comdat\n" \ + ".weak __sd_dlopen_anchor_" #tag "\n" \ + ".hidden __sd_dlopen_anchor_" #tag "\n" \ + ".type __sd_dlopen_anchor_" #tag ", %object\n" \ + ".balign 8\n" \ + "__sd_dlopen_anchor_" #tag ":\n" \ + ".byte 0\n" \ + ".popsection\n" \ + ".pushsection .note.dlopen, \"aGo\", %note, __sd_dlopen_anchor_" #tag ", sd_dlopen_group_" #tag ", comdat\n" \ + ".balign 4\n" \ + ".long 884f - 883f\n" \ + ".long 882f - 881f\n" \ + ".long 0x407c0c0a\n" \ + "883:\n" \ + ".asciz \"" SD_ELF_NOTE_DLOPEN_VENDOR "\"\n" \ + "884:\n" \ + ".balign 4\n" \ + "881:\n" \ + ".asciz \"" json "\"\n" \ + "882:\n" \ + ".balign 4\n" \ + ".popsection\n" \ + ".endif\n" \ + ); \ + __asm__ volatile ("" : : "r" (&__sd_dlopen_anchor_##tag)) + +#define _SD_SONAME_ARRAY1(a) "[\\\"" a "\\\"]" +#define _SD_SONAME_ARRAY2(a, b) "[\\\"" a "\\\",\\\"" b "\\\"]" +#define _SD_SONAME_ARRAY3(a, b, c) "[\\\"" a "\\\",\\\"" b "\\\",\\\"" c "\\\"]" +#define _SD_SONAME_ARRAY4(a, b, c, d) "[\\\"" a "\\\",\\\"" b "\\\",\\\"" c "\\\",\\\"" d "\\\"]" +#define _SD_SONAME_ARRAY5(a, b, c, d, e) "[\\\"" a "\\\",\\\"" b "\\\",\\\"" c "\\\",\\\"" d "\\\",\\\"" e "\\\"]" +#define _SD_SONAME_ARRAY_GET(_1,_2,_3,_4,_5,NAME,...) NAME +#define _SD_SONAME_ARRAY(...) _SD_SONAME_ARRAY_GET(__VA_ARGS__, _SD_SONAME_ARRAY5, _SD_SONAME_ARRAY4, _SD_SONAME_ARRAY3, _SD_SONAME_ARRAY2, _SD_SONAME_ARRAY1)(__VA_ARGS__) + +#define _SD_DLOPEN_JSON(feature, description, priority, ...) \ + "[{" \ + "\\\"feature\\\":\\\"" feature "\\\"," \ + "\\\"description\\\":\\\"" description "\\\"," \ + "\\\"priority\\\":\\\"" priority "\\\"," \ + "\\\"soname\\\":" _SD_SONAME_ARRAY(__VA_ARGS__) \ + "}]" + +#define SD_ELF_NOTE_DLOPEN(feature, description, priority, ...) \ + _SD_ELF_NOTE_DLOPEN(_SD_DLOPEN_JSON(feature, description, priority, __VA_ARGS__)) + +/* The anchored note requires LLVM >= 18 (see above). Fall back to the non-anchored note on older clang. */ +#if defined(__clang__) && !defined(__apple_build_version__) && __clang_major__ < 18 +# define SD_ELF_NOTE_DLOPEN_ANCHORED(tag, feature, description, priority, ...) \ + SD_ELF_NOTE_DLOPEN(feature, description, priority, __VA_ARGS__) +#else +# define SD_ELF_NOTE_DLOPEN_ANCHORED(tag, feature, description, priority, ...) \ + _SD_ELF_NOTE_DLOPEN_ANCHORED(tag, _SD_DLOPEN_JSON(feature, description, priority, __VA_ARGS__)) +#endif + +#else + +#define SD_ELF_NOTE_DLOPEN(feature, description, priority, ...) +#define SD_ELF_NOTE_DLOPEN_ANCHORED(tag, feature, description, priority, ...) + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/m4/libdl.m4 b/m4/libdl.m4 index 79d6906363..09e3b13131 100644 --- a/m4/libdl.m4 +++ b/m4/libdl.m4 @@ -1,5 +1,5 @@ # libdl.m4 -# serial 3 +# serial 4 dnl Copyright (C) 2024-2026 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -10,6 +10,7 @@ dnl Set LIBDL to '-ldl' if it is needed to use the functions declared dnl in <dlfcn.h> (dlopen, dlsym, etc.), or to empty otherwise. +dnl Set HAVE_DLOPEN to 1 if dlopen and dlsym are available, or to 0 otherwise. AC_DEFUN([gl_LIBDL], [ dnl dlopen, dlsym are @@ -21,8 +22,10 @@ AC_DEFUN([gl_LIBDL] [AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[#include <dlfcn.h> + #include <stddef.h> ]], - [[return ! dlsym (RTLD_DEFAULT, "main");]])], + [[void *handle = dlopen (NULL, RTLD_LAZY); + return ! (handle && dlsym (handle, "main"));]])], [gl_cv_lib_dl=none], [gl_cv_lib_dl=maybe]) if test $gl_cv_lib_dl = maybe; then @@ -31,16 +34,22 @@ AC_DEFUN([gl_LIBDL] AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[#include <dlfcn.h> + #include <stddef.h> ]], - [[return ! dlsym (RTLD_DEFAULT, "main");]])], + [[void *handle = dlopen (NULL, RTLD_LAZY); + return ! (handle && dlsym (handle, "main"));]])], [gl_cv_lib_dl='-ldl'], - [gl_cv_lib_dl=none]) + [gl_cv_lib_dl=no]) LIBS="$saved_LIBS" fi ]) case "$gl_cv_lib_dl" in - none) LIBDL='' ;; - *) LIBDL="$gl_cv_lib_dl" ;; + none) LIBDL=''; HAVE_DLOPEN=1 ;; + no) LIBDL=''; HAVE_DLOPEN=0 ;; + *) LIBDL="$gl_cv_lib_dl"; HAVE_DLOPEN=1 ;; esac AC_SUBST([LIBDL]) + AC_SUBST([HAVE_DLOPEN]) + AC_DEFINE_UNQUOTED([HAVE_DLOPEN], [$HAVE_DLOPEN], + [Define to 1 if dlopen and dlsym are available.]) ]) diff --git a/m4/sd-dlopen.m4 b/m4/sd-dlopen.m4 new file mode 100644 index 0000000000..1511548dfb --- /dev/null +++ b/m4/sd-dlopen.m4 @@ -0,0 +1,30 @@ +# sd-dlopen.m4 +# serial 1 +dnl Copyright (C) 2026 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl This file is offered as-is, without any warranty. + +AC_DEFUN([gl_SD_DLOPEN], +[ + AC_REQUIRE([gl_LIBDL]) + + AC_CACHE_CHECK([whether the assembler supports .ifndef, .endif, and .balign], + [gl_cv_asm_ifndef_endif_balign], + [AC_COMPILE_IFELSE( + [AC_LANG_SOURCE([[ +#if !defined __ELF__ || !(defined __GNUC__ || defined __clang__) +# error "not an ELF target with GNU-style inline assembly" +#endif +__asm__ (".ifndef \"gl_sd_dlopen_test\"\n" + ".balign 4\n" + ".endif\n"); +]])], + [gl_cv_asm_ifndef_endif_balign=yes], + [gl_cv_asm_ifndef_endif_balign=no])]) + if test $gl_cv_asm_ifndef_endif_balign = yes; then + AC_DEFINE([HAVE_ASM_IFNDEF_ENDIF_BALIGN], [1], + [Define to 1 if the assembler supports .ifndef, .endif, and .balign.]) + fi +]) diff --git a/modules/sd-dlopen b/modules/sd-dlopen new file mode 100644 index 0000000000..179a1f1ef5 --- /dev/null +++ b/modules/sd-dlopen @@ -0,0 +1,27 @@ +Description: +Emit ELF notes for package dependencies loaded with dlopen. + +Files: +lib/sd-dlopen.h +m4/sd-dlopen.m4 +m4/libdl.m4 + +Depends-on: +stdint-h + +configure.ac: +gl_SD_DLOPEN + +Makefile.am: + +Include: +"sd-dlopen.h" + +Link: +$(LIBDL) + +License: +LGPLv2+ + +Maintainer: +all -- 2.53.0
>From 6183bf1d55d7581f5b8ad8297f788cb4c4fd0155 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Tue, 21 Jul 2026 21:51:31 +0200 Subject: [PATCH 3/6] sd-dlopen: Reference the specification. * modules/sd-dlopen (Description): Add reference to the specification. * lib/sd-dlopen.h: Improve comments. --- ChangeLog | 6 ++++++ lib/sd-dlopen.h | 5 +++-- modules/sd-dlopen | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 115446119d..a85e110501 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2026-07-21 Bruno Haible <[email protected]> + + sd-dlopen: Reference the specification. + * modules/sd-dlopen (Description): Add reference to the specification. + * lib/sd-dlopen.h: Improve comments. + 2026-07-21 Luca Boccassi <[email protected]> sd-dlopen: Add tests. diff --git a/lib/sd-dlopen.h b/lib/sd-dlopen.h index 3f3b275e10..87760ada7a 100644 --- a/lib/sd-dlopen.h +++ b/lib/sd-dlopen.h @@ -27,7 +27,8 @@ extern "C" { #endif -/* ELF note macros implementing the FDO .note.dlopen standard. +/* ELF note macros implementing the UAPI Group's .note.dlopen specification + * https://uapi-group.org/specifications/specs/elf_dlopen_metadata/ * * These macros embed metadata in an ELF binary's .note.dlopen section, * declaring optional shared library dependencies that are loaded via @@ -44,7 +45,7 @@ extern "C" { * See SD_ELF_NOTE_DLOPEN(3) for details. */ -#define SD_ELF_NOTE_DLOPEN_VENDOR "FDO" +#define SD_ELF_NOTE_DLOPEN_VENDOR "FDO" /* freedesktop.org */ #define SD_ELF_NOTE_DLOPEN_TYPE 0x407c0c0a #define SD_ELF_NOTE_DLOPEN_PRIORITY_REQUIRED "required" #define SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED "recommended" diff --git a/modules/sd-dlopen b/modules/sd-dlopen index 179a1f1ef5..452ef2ecc0 100644 --- a/modules/sd-dlopen +++ b/modules/sd-dlopen @@ -1,5 +1,6 @@ Description: -Emit ELF notes for package dependencies loaded with dlopen. +Emit ELF notes for package dependencies loaded with dlopen, according to +https://uapi-group.org/specifications/specs/elf_dlopen_metadata/ Files: lib/sd-dlopen.h -- 2.53.0
>From 88757c1aa233fb65075d07959173bde6740fea97 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Wed, 22 Jul 2026 00:14:55 +0200 Subject: [PATCH 6/6] sd-dlopen: Fix test compilation error on Solaris 11.4. * lib/sd-dlopen.h: Disable the asm macros on Solaris. --- ChangeLog | 3 +++ lib/sd-dlopen.h | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index d23b321025..08b3ce1552 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2026-07-21 Bruno Haible <[email protected]> + sd-dlopen: Fix test compilation error on Solaris 11.4. + * lib/sd-dlopen.h: Disable the asm macros on Solaris. + sd-dlopen: Fix test compilation error on NetBSD 10. * m4/sd-dlopen.m4 (gl_SD_DLOPEN): Test whether the assembler supports the section flag o. Define _SD_ELF_NOTE_SUPPORTS_REFERENCES. diff --git a/lib/sd-dlopen.h b/lib/sd-dlopen.h index b3e36ccd77..57b7554754 100644 --- a/lib/sd-dlopen.h +++ b/lib/sd-dlopen.h @@ -52,7 +52,8 @@ extern "C" { #define SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED "suggested" #if defined __ELF__ && (defined __GNUC__ || defined __clang__) \ - && defined HAVE_ASM_IFNDEF_ENDIF_BALIGN && HAVE_ASM_IFNDEF_ENDIF_BALIGN + && defined HAVE_ASM_IFNDEF_ENDIF_BALIGN && HAVE_ASM_IFNDEF_ENDIF_BALIGN \ + && !defined __sun /* Avoid linker error "wrong ELF OSABI: ELFOSABI_GNU" */ /* The "R" (SHF_GNU_RETAIN) flag is only understood by binutils >= 2.36, so it can be disabled by defining * _SD_ELF_NOTE_DLOPEN_SECTION_FLAGS as 'aG' manually, but note that if --gc-sections is used when linking, -- 2.53.0
