summaryrefslogtreecommitdiffstats
path: root/meta/recipes-core/systemd/dlopen-deps.inc
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-core/systemd/dlopen-deps.inc')
-rw-r--r--meta/recipes-core/systemd/dlopen-deps.inc81
1 files changed, 81 insertions, 0 deletions
diff --git a/meta/recipes-core/systemd/dlopen-deps.inc b/meta/recipes-core/systemd/dlopen-deps.inc
new file mode 100644
index 0000000000..e0b333398c
--- /dev/null
+++ b/meta/recipes-core/systemd/dlopen-deps.inc
@@ -0,0 +1,81 @@
1PACKAGEFUNCS =+ "package_generate_dlopen_deps"
2
3python package_generate_dlopen_deps() {
4 # https://systemd.io/ELF_DLOPEN_METADATA/
5
6 import struct, json
7
8 def extract_segment(filename, segment):
9 """
10 Return the named segment from the ELF.
11 """
12 import tempfile, subprocess
13
14 with tempfile.NamedTemporaryFile() as f:
15 try:
16 cmd = [d.getVar("OBJCOPY"), "--dump-section", f"{segment}={f.name}", filename]
17 subprocess.run(cmd, check=True)
18 with open(f.name, "rb") as f2:
19 return f2.read()
20 except subprocess.CalledProcessError as e:
21 # binutils-objcopy has 0 exit code if the segment can't be found, but llvm-objcopy
22 # does not. Assume the failure isn't critical and ignore errors.
23 if e.returncode == 1:
24 return b""
25 raise e
26
27 def parse(buffer, is_little):
28 deps = []
29 offset = 0
30 while offset < len(buffer):
31 format = f"{'<' if is_little else '>'}iii"
32 name_size, desc_size, note_type = struct.unpack_from(format, buffer, offset)
33 offset += struct.calcsize(format)
34
35 format = f"{name_size}s0i{desc_size}s0i"
36 if note_type == 0x407c0c0a:
37 name_b, desc_b = struct.unpack_from(format, buffer, offset)
38 name = name_b.strip(b"\x00").decode("ascii")
39 if name == "FDO":
40 desc = desc_b.strip(b"\x00").decode("utf-8")
41 deps.append(*json.loads(desc))
42 offset += struct.calcsize(format)
43 return deps
44
45 dep_map = {
46 "required": "RDEPENDS",
47 "recommended": "RRECOMMENDS",
48 "suggested": "RSUGGESTS"
49 }
50
51 shlibs = oe.package.read_shlib_providers(d)
52
53 for pkg, files in pkgfiles.items():
54 # Skip -dbg packages as we won't need to generate dependencies for those
55 # but scanning can take time
56 if pkg.endswith("-dbg"):
57 continue
58
59 for f in files:
60 # Skip symlinks, just look for real libraries
61 if cpath.islink(f):
62 continue
63
64 if ".so." in f or f.endswith(".so"):
65 try:
66 elf = oe.qa.ELFFile(f)
67 elf.open()
68 for dep in parse(extract_segment(f, ".note.dlopen"), elf.isLittleEndian()):
69 for soname in dep["soname"]:
70 if soname in shlibs:
71 # TODO assumes the first match is good
72 package, version = list(shlibs[soname].values())[0]
73 dependency = dep_map[dep["priority"]]
74 bb.note(f"{pkg}: adding {dependency} on {package} via .note.dlopen")
75 d.appendVar(f"{dependency}:{pkg}", f" {package} (>= {version})")
76 else:
77 bb.warn(f"cannot find {soname}")
78 except oe.qa.NotELFFileError as e:
79 bb.note(f"Cannot extract ELF notes: {e}")
80 pass
81}