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.
This commit is contained in:
Yu Watanabe
2026-07-10 16:29:54 +09:00
parent 19d562b0be
commit aa0db003cf
+86 -1
View 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
}