Skip to content

Commit 9642d49

Browse files
committed
Import Dlopen Metadata for ELF Files from systemd.io
This is not systemd specific and in fact other projects are already using it, like SDL3 https://systemd.io/ELF_DLOPEN_METADATA/
1 parent 60d28de commit 9642d49

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ BookToC: false
4040
* [Package Metadata for Executable Files](specs/package_metadata_for_executable_files.md):
4141
Describes the format and mechanism to include packaging metadata in ELF/PE binaries.
4242
([canonical online location](https://uapi-group.org/specifications/specs/package_metadata_for_executable_files/))
43+
* [Dlopen Metadata for ELF Files](specs/elf_dlopen_metadata.md):
44+
Describes the format and mechanism to include dynamically loaded libraries metadata in ELF binaries.
45+
([canonical online location](https://uapi-group.org/specifications/specs/elf_dlopen_metadata/))
4346

4447
## Work in Progress
4548

specs/elf_dlopen_metadata.md

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
---
2+
title: Dlopen Metadata for ELF Files
3+
category: Interfaces
4+
layout: default
5+
SPDX-License-Identifier: LGPL-2.1-or-later
6+
---
7+
8+
# `dlopen()` Metadata for ELF Files
9+
10+
| Version | Changes |
11+
|---------|---------|
12+
| 1.0 | Initial Release |
13+
14+
*Intended audience: hackers working on packaging ELF files that use dlopen to load libraries.*
15+
16+
## Motivation
17+
18+
Using `dlopen()` to load optional dependencies brings several advantages: programs can gracefully downgrade
19+
a feature when a library is not available, and the shared library is only loaded into the process (and its
20+
ELF constructors are run) only when the requested feature is actually used. But it also has some drawbacks,
21+
and the main one is that it is harder to track a program's dependencies, since unlike build-time dynamic
22+
linking there will not be a mention in the ELF metadata. This specification aims to solve this problem by
23+
providing a standardized specification for a custom ELF note that can be used to list `dlopen()`
24+
dependencies.
25+
26+
## Implementation
27+
28+
This document will attempt to define a common metadata format specification, so that multiple implementers
29+
might use it when coding upstream software, and packagers might use it when building packages and setting
30+
dependencies.
31+
32+
The metadata will be embedded in a series of new, 4-byte-aligned, allocated, 0-padded, read-only ELF header
33+
sections, in a JSON array containing name-value objects, either one ELF note per dependency or as a single
34+
note listing multiple dependencies in the top-level array. Implementers working on parsing ELF files should
35+
not assume a specific list of names, but parse anything that is included in the section, and should look for
36+
the note using the `note type`. Implementers working on build tools should strive to use the same names, for
37+
consistency. The most common will be listed here.
38+
39+
* Section header
40+
41+
```
42+
SECTION: `.note.dlopen`
43+
note type: `0x407c0c0a`
44+
Owner: `FDO` (FreeDesktop.org)
45+
Value: an array of JSON objects encoded as a zero-terminated UTF-8 string
46+
```
47+
48+
* JSON payload
49+
50+
```json
51+
[
52+
{
53+
"soname": ["libfoo.so.1"],
54+
"feature": "foo",
55+
"description": "Enables the foo feature",
56+
"priority": "recommended"
57+
}
58+
]
59+
```
60+
61+
The format is a single JSON array containing objects, encoded as a zero-terminated `UTF-8` string. Each key
62+
in each object shall be unique as per recommendations of [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259#section-4).
63+
Strings shall not contain any control characters or use `\uXXX` escaping.
64+
65+
Reference implementations of [packaging tools for `.deb` and `.rpm`](https://github.com/systemd/package-notes)
66+
are available, and provide macros/helpers to parse the note when building packages and adding dependencies.
67+
68+
## Well-known keys
69+
70+
The metadata format is intentionally extensible, so that upstreams and later revisions of this spec can add
71+
their own information. The 'soname' array is required, with at least one element, everything else is
72+
optional. If alternative soname versions for the same library are supported at the same time, an array can
73+
be used, listing the most preferred first, and parsers are expected to select only the first one that is
74+
available on the system, as it is a mechanism to specify alternatives. If the `priority` field is used, it
75+
must follow the specification and use one of the values specified in the table. If it is not specified, a
76+
parser should assume 'recommended' if a priority is needed. If the `feature` field is used, it will identify
77+
an individual feature, and multiple entries using the same `feature` denote functionality that requires all
78+
of the libraries they specify in order to be enabled.
79+
80+
| Key name | Key type | Mandatory | Key description | Example value |
81+
|-------------|----------------------------|-----------|--------------------------------------------------------------------------|----------------------------------|
82+
| soname | array of strings | yes | The library names loaded by `dlopen()` | [ "libfoo.so.1", "libfoo.so.0" ] |
83+
| feature | string | no | A keyword identifying the feature that the library contributes to enable | "foo" |
84+
| description | string | no | A human-readable text string describing the feature | "Enables the foo feature" |
85+
| priority | string | no | The priority of the feature, one of: required, recommended, suggested | "recommended" |
86+
87+
### Priority definition
88+
89+
| Priority | Semantics |
90+
|-------------|--------------------------------------------------------------------------------------------------------------------------------------|
91+
| required | Core functionality needs the dependency, the binary will not work if it cannot be found |
92+
| recommended | Important functionality needs the dependency, the binary will work but in most cases the dependency should be provided |
93+
| suggested | Secondary functionality needs the dependency, the binary will work and the dependency is only needed for full-featured installations |
94+
95+
### Displaying `dlopen()` notes
96+
97+
The raw ELF section can be extracted using `objdump`:
98+
```console
99+
$ objdump -j .note.dlopen -s /usr/lib64/systemd/libsystemd-shared-257.so
100+
101+
/usr/lib64/systemd/libsystemd-shared-257.so: file format elf64-x86-64
102+
103+
Contents of section .note.dlopen:
104+
0334 04000000 8e000000 0a0c7c40 46444f00 ..........|@FDO.
105+
0344 5b7b2266 65617475 7265223a 22627066 [{"feature":"bpf
106+
0354 222c2264 65736372 69707469 6f6e223a ","description":
107+
0364 22537570 706f7274 20666972 6577616c "Support firewal
108+
0374 6c696e67 20616e64 2073616e 64626f78 ling and sandbox
109+
0384 696e6720 77697468 20425046 222c2270 ing with BPF","p
110+
0394 72696f72 69747922 3a227375 67676573 riority":"sugges
111+
03a4 74656422 2c22736f 6e616d65 223a5b22 ted","soname":["
112+
03b4 6c696262 70662e73 6f2e3122 2c226c69 libbpf.so.1","li
113+
03c4 62627066 2e736f2e 30225d7d 5d000000 bbpf.so.0"]}]...
114+
03d4 04000000 9e000000 0a0c7c40 46444f00 ..........|@FDO.
115+
...
116+
```
117+
118+
It is more convenient to use a higher level tool:
119+
```console
120+
$ dlopen-notes /usr/lib64/systemd/libsystemd-shared-257.so
121+
# /usr/lib64/systemd/libsystemd-shared-257.so
122+
[
123+
{
124+
"feature": "archive",
125+
"description": "Support for decompressing archive files",
126+
"priority": "suggested",
127+
"soname": [
128+
"libarchive.so.13"
129+
]
130+
},
131+
{
132+
"feature": "bpf",
133+
"description": "Support firewalling and sandboxing with BPF",
134+
"priority": "suggested",
135+
"soname": [
136+
"libbpf.so.1",
137+
"libbpf.so.0"
138+
]
139+
},
140+
...
141+
```
142+
143+
`dlopen-notes` can display the notes grouped in a few different ways.
144+
One option is to filter the libraries by "feature". This answers the
145+
question "what libraries are needed to provide specified features":
146+
147+
```console
148+
$ dlopen-notes.py -f archive,bpf /usr/lib64/systemd/libsystemd-shared-257.so
149+
# grouped by feature
150+
{
151+
"bpf": {
152+
"description": "Support firewalling and sandboxing with BPF",
153+
"sonames": {
154+
"libbpf.so.1": "suggested",
155+
"libbpf.so.0": "suggested"
156+
}
157+
},
158+
"archive": {
159+
"description": "Support for decompressing archive files",
160+
"sonames": {
161+
"libarchive.so.13": "suggested"
162+
}
163+
}
164+
}
165+
166+
The format that is used when building `deb` packages:
167+
```console
168+
$ dlopen-notes -s /usr/lib64/systemd/libsystemd-shared-257.so
169+
libarchive.so.13 suggested
170+
libbpf.so.0 suggested
171+
libbpf.so.1 suggested
172+
...
173+
```
174+
175+
The format that can be useful when building `rpm` packages:
176+
```console
177+
$ dlopen-notes --rpm-requires archive --rpm-recommends bpf /usr/lib64/systemd/libsystemd-shared-257.so
178+
Requires: libarchive.so.13()(64bit)
179+
Recommends: libbpf.so.1()(64bit)
180+
```

0 commit comments

Comments
 (0)