]> git.ipfire.org Git - thirdparty/kmod.git/commitdiff
libkmod: Add ELF notes to compression libraries
authorLucas De Marchi <lucas.de.marchi@gmail.com>
Wed, 4 Dec 2024 15:24:49 +0000 (09:24 -0600)
committerLucas De Marchi <lucas.de.marchi@gmail.com>
Fri, 6 Dec 2024 21:16:28 +0000 (13:16 -0800)
Follow the new spec for ELF notes as detailed in
https://systemd.io/ELF_PACKAGE_METADATA/.

We can copy mostly verbatim the macros from systemd codebase.

Example output:

$ meson setup --native-file build-dev.ini -Dxz=disabled -Ddlopen=zlib build
...
    dlopen           : zlib

    features         : +ZSTD -XZ +ZLIB +OPENSSL

$ dlopen-notes.py build/libkmod.so.2
# build/libkmod.so.2
[
  {
    "feature": "xz",
    "description": "Support for uncompressing xz-compressed modules",
    "priority": "recommended",
    "soname": [
      "liblzma.so.5"
    ]
  }
]

Signed-off-by: Lucas De Marchi <lucas.de.marchi@gmail.com>
Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
Link: https://github.com/kmod-project/kmod/pull/262
Makefile.am
libkmod/libkmod-file-xz.c
libkmod/libkmod-file-zlib.c
libkmod/libkmod-file-zstd.c
meson.build
shared/elf-note.h [new file with mode: 0644]
shared/macro.h

index e8ff63183d698775109c05b900444f3c9e25b95e..504af5b26248b627b26abc3971838bc5f62e9ef1 100644 (file)
@@ -58,6 +58,7 @@ noinst_LTLIBRARIES = shared/libshared.la
 shared_libshared_la_SOURCES = \
        shared/array.c \
        shared/array.h \
+       shared/elf-note.h \
        shared/hash.c \
        shared/hash.h \
        shared/macro.h \
index f769f835e8e65aad9bbc7f6ff668bb8955c99644..f693877065d2c716e58c2f5fdc3c408e46b8cf82 100644 (file)
@@ -14,6 +14,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <shared/elf-note.h>
 #include <shared/util.h>
 
 #include "libkmod.h"
@@ -29,12 +30,16 @@ DL_SYMBOL_TABLE(DECLARE_SYM)
 
 static int dlopen_lzma(void)
 {
+#if !DLSYM_LOCALLY_ENABLED
+       return 0;
+#else
        static void *dl = NULL;
 
-       if (!DLSYM_LOCALLY_ENABLED)
-               return 0;
+       ELF_NOTE_DLOPEN("xz", "Support for uncompressing xz-compressed modules",
+                       ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "liblzma.so.5");
 
        return dlsym_many(&dl, "liblzma.so.5", DL_SYMBOL_TABLE(DLSYM_ARG) NULL);
+#endif
 }
 
 static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret)
index f8ead1b91ccb94d6a788077f4e8039e2ba9499cd..b26c7d1e6a2ef330a13176b64631c9307e7094a3 100644 (file)
@@ -14,6 +14,7 @@
 #include <unistd.h>
 #include <zlib.h>
 
+#include <shared/elf-note.h>
 #include <shared/util.h>
 
 #include "libkmod.h"
@@ -32,12 +33,16 @@ DL_SYMBOL_TABLE(DECLARE_SYM)
 
 static int dlopen_zlib(void)
 {
+#if !DLSYM_LOCALLY_ENABLED
+       return 0;
+#else
        static void *dl = NULL;
 
-       if (!DLSYM_LOCALLY_ENABLED)
-               return 0;
+       ELF_NOTE_DLOPEN("zlib", "Support for uncompressing zlib-compressed modules",
+                       ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libz.so.1");
 
        return dlsym_many(&dl, "libz.so.1", DL_SYMBOL_TABLE(DLSYM_ARG) NULL);
+#endif
 }
 
 int kmod_file_load_zlib(struct kmod_file *file)
index a2eece7d50d1ef756f58a7c1191851f70eaed9e1..f403a01b5934f58e295b7c71d2c8b292ba92a6db 100644 (file)
@@ -15,6 +15,7 @@
 #include <unistd.h>
 #include <zstd.h>
 
+#include <shared/elf-note.h>
 #include <shared/util.h>
 
 #include "libkmod.h"
@@ -31,12 +32,16 @@ DL_SYMBOL_TABLE(DECLARE_SYM)
 
 static int dlopen_zstd(void)
 {
+#if !DLSYM_LOCALLY_ENABLED
+       return 0;
+#else
        static void *dl = NULL;
 
-       if (!DLSYM_LOCALLY_ENABLED)
-               return 0;
+       ELF_NOTE_DLOPEN("zstd", "Support for uncompressing zstd-compressed modules",
+                       ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libzstd.so.1");
 
        return dlsym_many(&dl, "libzstd.so.1", DL_SYMBOL_TABLE(DLSYM_ARG) NULL);
+#endif
 }
 
 int kmod_file_load_zstd(struct kmod_file *file)
index 64288dd94deba84519c334cb377a56fb0c7a7556..7e7c02097ff8dcb2da3e080e0732183b5e03fb0a 100644 (file)
@@ -363,6 +363,7 @@ libshared = static_library(
   files(
     'shared/array.c',
     'shared/array.h',
+    'shared/elf-note.h',
     'shared/hash.c',
     'shared/hash.h',
     'shared/macro.h',
diff --git a/shared/elf-note.h b/shared/elf-note.h
new file mode 100644 (file)
index 0000000..f29e2a5
--- /dev/null
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "macro.h"
+
+/*
+ * Originally from systemd codebase.
+ *
+ * Reference: https://systemd.io/ELF_PACKAGE_METADATA/
+ */
+
+// clang-format off
+
+#define ELF_NOTE_DLOPEN_VENDOR "FDO"
+#define ELF_NOTE_DLOPEN_TYPE UINT32_C(0x407c0c0a)
+#define ELF_NOTE_DLOPEN_PRIORITY_REQUIRED "required"
+#define ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED "recommended"
+#define ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED "suggested"
+
+/* Add an ".note.dlopen" ELF note to our binary that declares our weak dlopen() dependency. This
+ * information can be read from an ELF file via "readelf -p .note.dlopen" or an equivalent command. */
+#define _ELF_NOTE_DLOPEN(json, variable_name)                              \
+        __attribute__((used, section(".note.dlopen"))) _Alignas(sizeof(uint32_t)) static const struct { \
+                struct {                                                   \
+                        uint32_t n_namesz, n_descsz, n_type;               \
+                } nhdr;                                                    \
+                char name[sizeof(ELF_NOTE_DLOPEN_VENDOR)];                 \
+                _Alignas(sizeof(uint32_t)) char dlopen_json[sizeof(json)]; \
+        } variable_name = {                                                \
+                .nhdr = {                                                  \
+                        .n_namesz = sizeof(ELF_NOTE_DLOPEN_VENDOR),        \
+                        .n_descsz = sizeof(json),                          \
+                        .n_type   = ELF_NOTE_DLOPEN_TYPE,                  \
+                },                                                         \
+                .name = ELF_NOTE_DLOPEN_VENDOR,                            \
+                .dlopen_json = json,                                       \
+        }
+
+#define _SONAME_ARRAY1(a) "[\""a"\"]"
+#define _SONAME_ARRAY2(a, b) "[\""a"\",\""b"\"]"
+#define _SONAME_ARRAY3(a, b, c) "[\""a"\",\""b"\",\""c"\"]"
+#define _SONAME_ARRAY4(a, b, c, d) "[\""a"\",\""b"\",\""c"\"",\""d"\"]"
+#define _SONAME_ARRAY5(a, b, c, d, e) "[\""a"\",\""b"\",\""c"\"",\""d"\",\""e"\"]"
+#define _SONAME_ARRAY_GET(_1,_2,_3,_4,_5,NAME,...) NAME
+#define _SONAME_ARRAY(...) _SONAME_ARRAY_GET(__VA_ARGS__, _SONAME_ARRAY5, _SONAME_ARRAY4, _SONAME_ARRAY3, _SONAME_ARRAY2, _SONAME_ARRAY1)(__VA_ARGS__)
+
+/* The 'priority' must be one of 'required', 'recommended' or 'suggested' as per specification, use the
+ * macro defined above to specify it.
+ * Multiple sonames can be passed and they will be automatically constructed into a json array (but note that
+ * due to preprocessor language limitations if more than the limit defined above is used, a new
+ * _SONAME_ARRAY<X+1> will need to be added). */
+#define ELF_NOTE_DLOPEN(feature, description, priority, ...) \
+        _ELF_NOTE_DLOPEN("[{\"feature\":\"" feature "\",\"description\":\"" description "\",\"priority\":\"" priority "\",\"soname\":" _SONAME_ARRAY(__VA_ARGS__) "}]", UNIQ_T(s, UNIQ))
index 7b4ea4214d4b7a8a0e7586d7c162ad9f745607c9..4cbdc7aad4fbd292861e280b9d814103b33d970f 100644 (file)
@@ -37,6 +37,7 @@
 #define XCONCATENATE(x, y) x##y
 #define CONCATENATE(x, y) XCONCATENATE(x, y)
 #define UNIQ(x) CONCATENATE(x, __COUNTER__)
+#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
 
 /* Attributes */