Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embed ELF metadata about dynamic runtime dependencies #1482

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

cgzones
Copy link
Member

@cgzones cgzones commented May 18, 2024

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)
Alternative link: https://github.com/systemd/systemd/blob/main/docs/ELF_DLOPEN_METADATA.md

Inspired-by: systemd/systemd#32234

@cgzones cgzones added the dependencies Pull requests that update a dependency file label May 18, 2024
@cgzones
Copy link
Member Author

cgzones commented May 18, 2024

See https://salsa.debian.org/debian/htop/-/merge_requests/5 for the packaging update.

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
}; \
IGNORE_W11EXTENSIONS_END
#else
#define DECLARE_ELF_NOTE_DLOPEN()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should name the content parameter for consistency.


#define DECLARE_ELF_NOTE_DLOPEN(content) \
IGNORE_W11EXTENSIONS_BEGIN \
__attribute__((used, section(".note.dlopen"))) alignas(sizeof(uint32_t)) static const struct { \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
__attribute__((used, section(".note.dlopen"))) alignas(sizeof(uint32_t)) static const struct { \
__attribute__((packed, used, section(".note.dlopen"))) static const struct { \

Just byte-align ourselves and round up with some little magic later

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another problem: C99 doesn't support anonymous struct yet.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another problem: C99 doesn't support anonymous struct yet.

The workaround for that is kinda easy though, too …

struct { \
uint32_t n_namesz, n_descsz, n_type; \
} nhdr; \
char name[sizeof(ELF_NOTE_DLOPEN_OWNER)]; \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
char name[sizeof(ELF_NOTE_DLOPEN_OWNER)]; \
char name[(sizeof(ELF_NOTE_DLOPEN_OWNER)+3)&(~3)]; \

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)]; \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
alignas(sizeof(uint32_t)) char dlopen_content[sizeof(content)]; \
char dlopen_content[(sizeof(content)+3)&(~3)]; \

Although one could argue if this last expansion is still required at the end of the attribute section.

Comment on lines +276 to +287
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 feature is supported.])
AC_MSG_RESULT(yes),
AC_MSG_RESULT(no))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cf. Macros.h for a way on how to do this without C11 support. If we throw around GCC/Clang compiler extensions, we also could just use packed, which at least is even kinda portable to other compilers too …

Comment on lines +79 to +82
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\"}]")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any objections to have this literal span multiple lines for better maintainability?

Please add proper meta-commands for astyle to leave this block alone afterwards.

Also, this note probably should only contain the optional dependencies we actually have enabled; i.e. if configured without delay accounting, there's no need to declare this as an optional runtime dependency if we would never try to load that library anyway.

Suggested change
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\"}]")
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\""\
"}"\
"]" )

Copy link
Contributor

@Explorer09 Explorer09 May 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of having a full JSON syntax in a macro caller. Can we rework the macros so that only important fields will be used as parameters?

I expect the usage would ideally work like this:

#define ELF_NOTE_DLOPEN_DEP(sonames, feature_tag, priority) ...
// Example
ELF_NOTE_DLOPEN_DEP(
   "[\"libnl-3.so\",\"libnl-3.so.200\"]", "delay-accounting", "recommended")
ELF_NOTE_DLOPEN_DEP(
   "[\"libnl-genl-3.so\",\"libnl-genl-3.so.200\"]", "delay-accounting", "recommended")

How do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I also thought about this, including making generating the JSON be split into different macro calls (definitively possible AFAICS), there's one minor issue with the pre-processor IMHO, that prevents this from being really nice syntax. The issue is, that you can't properly auto-terminate lists of arguments to insert the semicolon in list contexts, thus you'd most likely have to resort to something like "[" ELF_NOTE_DEP(foo…) "," ELF_NOTE_DEP(bar…) "]"; which doesn't look too terrible, but in a really great world could work without the explicit "," token in between.

Also from a practcal PoV, I'd prefer such macros to go ELF_NOTE_DLOPEN_DEP(freature, level, description, solist)`, but comparibly that's a minor change to implement.

} nhdr; \
char name[sizeof(ELF_NOTE_DLOPEN_OWNER)]; \
alignas(sizeof(uint32_t)) char dlopen_content[sizeof(content)]; \
} variable_name = { \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be variable_name be dynamic?

Comment on lines +59 to +60
DECLARE_ELF_NOTE_DLOPEN("[{\"soname\":[\"libsensors.so\",\"libsensors.so.5\",\"libsensors.so.4\"],\"feature\":"\
"\"sensors\",\"description:\":\"Enables hardware sensor support\",\"priority\":\"recommended\"}]")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple lines, example above …

Comment on lines +101 to +102
DECLARE_ELF_NOTE_DLOPEN("[{\"soname\":[\"libsystemd.so.0\"],\"feature\":\"systemd\",\"description:\":"\
"\"Enables systemd support\",\"priority\":\"suggested\"}]")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple lines, example above …

@BenBE BenBE added the build system 🔧 Affects the build system rather then the user experience label May 19, 2024
@BenBE
Copy link
Member

BenBE commented May 20, 2024

Any objections to having the code for generating these annotations be its own source code file?

NB: There's also __attribute__((aligned(4)))) available to play around, which seems to have been supported by GCC for a long time (no idea re Clang) …

@Explorer09
Copy link
Contributor

Explorer09 commented May 20, 2024

Just my general comments:
I am personally not a fan of the specification of embedding the dlopen dependency information in ELF binaries. For me it look like increasing the binary size with little benefit, and the same information can be provided by (distros) package managers already.

Besides, this information is ELF-specific, which could potentially discriminate other OSes such as BSDs.
I wonder if the same information can be provided through a separate file, not embedding into binaries? The manifest file for Windows executable binaries can work either way - provided separately with ".manifest" extension, or embedding into resource sections of ".exe" files.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
build system 🔧 Affects the build system rather then the user experience dependencies Pull requests that update a dependency file
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants