Skip to content

Commit

Permalink
Embed ELF metadata about dynamic runtime dependencies
Browse files Browse the repository at this point in the history
Embed metadata about runtime dependencies of dynamic libraries used via
dlopen(3) in the binary so that distributions can automatically generate
package dependencies.

Specification: https://systemd.io/ELF_DLOPEN_METADATA/ (TODO: link not yet available)
Inspired-by: systemd/systemd#32234
  • Loading branch information
cgzones committed May 18, 2024
1 parent 8dfe7ed commit 7dc0ab1
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 0 deletions.
37 changes: 37 additions & 0 deletions Macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ in the source distribution for its full text.
#define IGNORE_WCASTQUAL_END
#endif

#if defined(__clang__)
#define IGNORE_W11EXTENSIONS_BEGIN _Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wc11-extensions\"")
#define IGNORE_W11EXTENSIONS_END _Pragma("clang diagnostic pop")
#else
#define IGNORE_W11EXTENSIONS_BEGIN
#define IGNORE_W11EXTENSIONS_END
#endif

/* Cheaper function for checking NaNs. Unlike the standard isnan(), this may
throw an FP exception on a "signaling NaN".
(ISO/IEC TS 18661-1 and the C23 standard stated that isnan() throws no
Expand All @@ -142,4 +151,32 @@ static inline unsigned long long saturatingSub(unsigned long long a, unsigned lo
return a > b ? a - b : 0;
}

#ifdef HAVE_ALIGNAS
#include <stdalign.h>

#define ELF_NOTE_DLOPEN_OWNER "FDO"
#define ELF_NOTE_DLOPEN_TYPE UINT32_C(0x407c0c0a)

#define DECLARE_ELF_NOTE_DLOPEN(content) \
IGNORE_W11EXTENSIONS_BEGIN \
__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_OWNER)]; \
alignas(sizeof(uint32_t)) char dlopen_content[sizeof(content)]; \
} variable_name = { \
.nhdr = { \
.n_namesz = sizeof(ELF_NOTE_DLOPEN_OWNER), \
.n_descsz = sizeof(content), \
.n_type = ELF_NOTE_DLOPEN_TYPE, \
}, \
.name = ELF_NOTE_DLOPEN_OWNER, \
.dlopen_content = content, \
}; \
IGNORE_W11EXTENSIONS_END
#else
#define DECLARE_ELF_NOTE_DLOPEN()
#endif

#endif /* HEADER_Macros */
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ On most BSD systems `kvm` is a requirement to read kernel information.

More information on required and optional dependencies can be found in [configure.ac](configure.ac).

#### ELF Note
The optional runtime dependencies are also embedded in the binary via the ELF note `.note.dlopen`. See the [specification](https://systemd.io/ELF_DLOPEN_METADATA/) for details.

## Usage
See the manual page (`man htop`) or the help menu (**F1** or **h** inside `htop`) for a list of supported key commands.

Expand Down
12 changes: 12 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,18 @@ AC_LINK_IFELSE([
[AC_MSG_RESULT(no)
AC_MSG_ERROR([can not find required macros: NAN, isgreater() and isgreaterequal()])])

AC_MSG_CHECKING(for alignas support)
AC_COMPILE_IFELSE([
AC_LANG_SOURCE(
[[
#include <stdalign.h>
alignas(128) char buffer[128];
]]
)],
AC_DEFINE([HAVE_ALIGNAS], 1, [The alignas C11 feauture is supported.])
AC_MSG_RESULT(yes),
AC_MSG_RESULT(no))

# ----------------------------------------------------------------------


Expand Down
6 changes: 6 additions & 0 deletions linux/LibNl.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ static void unload_libnl(void) {
}

static int load_libnl(void) {

DECLARE_ELF_NOTE_DLOPEN("[{\"soname\":[\"libnl-3.so\",\"libnl-3.so.200\"],\"feature\":\"delay-accounting\"," \
"\"description:\":\"Enables delay accounting support\",\"priority\":\"recommended\"},{\"soname\":[" \
"\"libnl-genl-3.so\",\"libnl-genl-3.so.200\"],\"feature\":\"delay-accounting\",\"description:\":"\
"\"Enables delay accounting support\",\"priority\":\"recommended\"}]")

if (libnlHandle && libnlGenlHandle)
return 0;

Expand Down
3 changes: 3 additions & 0 deletions linux/LibSensors.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ int LibSensors_init(void) {

#else

DECLARE_ELF_NOTE_DLOPEN("[{\"soname\":[\"libsensors.so\",\"libsensors.so.5\",\"libsensors.so.4\"],\"feature\":"\
"\"sensors\",\"description:\":\"Enables hardware sensor support\",\"priority\":\"recommended\"}]")

if (!dlopenHandle) {
/* Find the unversioned libsensors.so (symlink) and prefer that, but Debian has .so.5 and Fedora .so.4 without
matching symlinks (unless people install the -dev packages) */
Expand Down
3 changes: 3 additions & 0 deletions linux/SystemdMeter.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ static void SystemdMeter_done(ATTR_UNUSED Meter* this) {
static int updateViaLib(bool user) {
SystemdMeterContext_t* ctx = user ? &ctx_user : &ctx_system;
#ifndef BUILD_STATIC
DECLARE_ELF_NOTE_DLOPEN("[{\"soname\":[\"libsystemd.so.0\"],\"feature\":\"systemd\",\"description:\":"\
"\"Enables systemd support\",\"priority\":\"suggested\"}]")

if (!dlopenHandle) {
dlopenHandle = dlopen("libsystemd.so.0", RTLD_LAZY);
if (!dlopenHandle)
Expand Down

0 comments on commit 7dc0ab1

Please sign in to comment.