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:
+86
-1
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user