From: Yu Watanabe Date: Sat, 4 Jul 2026 20:51:27 +0000 (+0900) Subject: sd-dlopen: introduce SD_ELF_NOTE_DLOPEN_ANCHORED() macro X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=aa0db003cf537aeb051cc51a2f7e95d51a5756cd;p=thirdparty%2Fsystemd.git sd-dlopen: introduce SD_ELF_NOTE_DLOPEN_ANCHORED() macro 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. --- diff --git a/src/systemd/sd-dlopen.h b/src/systemd/sd-dlopen.h index b108bdae28b..493111bd75f 100644 --- a/src/systemd/sd-dlopen.h +++ b/src/systemd/sd-dlopen.h @@ -23,6 +23,8 @@ #include +#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 }