]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-dlopen: introduce SD_ELF_NOTE_DLOPEN_ANCHORED() macro
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 4 Jul 2026 20:51:27 +0000 (05:51 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 10 Jul 2026 07:29:54 +0000 (16:29 +0900)
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.

Currently, the new macro is unused in our code, but all dlopen notes
will be generated with this in later commits.

src/systemd/sd-dlopen.h

index b108bdae28b7b577b8ac895fb3da6c09369f17d7..493111bd75f2ce7dc772ceedfd3b082f983d9de0 100644 (file)
@@ -23,6 +23,8 @@
 
 #include <stdint.h>
 
+#include "_sd-common.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -94,6 +96,69 @@ extern "C" {
                 ".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, uniq_var, json)                                                                       \
+        _Pragma("GCC diagnostic push")                                                                                          \
+        _Pragma("GCC diagnostic ignored \"-Wnested-externs\"")                                                                  \
+        __attribute__((visibility("hidden")))                                                                                   \
+        extern volatile const char uniq_var __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" (&uniq_var))
+
 #define _SD_SONAME_ARRAY1(a) "[\\\"" a "\\\"]"
 #define _SD_SONAME_ARRAY2(a, b) "[\\\"" a "\\\",\\\"" b "\\\"]"
 #define _SD_SONAME_ARRAY3(a, b, c) "[\\\"" a "\\\",\\\"" b "\\\",\\\"" c "\\\"]"
@@ -102,8 +167,28 @@ extern "C" {
 #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_UNIQ_VAR(tag)                                \
+        _SD_CONCATENATE(_SD_CONCATENATE(__sd_dlopen_anchor_, tag), _SD_CONCATENATE(_, _SD_UNIQ))
+
+#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("[{\\\"feature\\\":\\\"" feature "\\\",\\\"description\\\":\\\"" description "\\\",\\\"priority\\\":\\\"" priority "\\\",\\\"soname\\\":" _SD_SONAME_ARRAY(__VA_ARGS__) "}]")
+        _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_UNIQ_VAR(tag), _SD_DLOPEN_JSON(feature, description, priority, __VA_ARGS__))
+#endif
 
 #ifdef __cplusplus
 }