--- /dev/null
+PACKAGEFUNCS =+ "package_generate_dlopen_deps"
+
+python package_generate_dlopen_deps() {
+ # https://systemd.io/ELF_DLOPEN_METADATA/
+
+ import struct, json
+
+ def extract_segment(filename, segment):
+ """
+ Return the named segment from the ELF.
+ """
+ import tempfile, subprocess
+
+ with tempfile.NamedTemporaryFile() as f:
+ cmd = [d.getVar("OBJCOPY"), "--dump-section", f"{segment}={f.name}", filename]
+ subprocess.run(cmd, check=True)
+ return f.read()
+
+ def parse(buffer, is_little):
+ deps = []
+ offset = 0
+ while offset < len(buffer):
+ format = f"{'<' if is_little else '>'}iii"
+ name_size, desc_size, note_type = struct.unpack_from(format, buffer, offset)
+ offset += struct.calcsize(format)
+
+ format = f"{name_size}s0i{desc_size}s0i"
+ if note_type == 0x407c0c0a:
+ name_b, desc_b = struct.unpack_from(format, buffer, offset)
+ name = name_b.strip(b"\x00").decode("ascii")
+ if name == "FDO":
+ desc = desc_b.strip(b"\x00").decode("utf-8")
+ deps.append(*json.loads(desc))
+ offset += struct.calcsize(format)
+ return deps
+
+ dep_map = {
+ "required": "RDEPENDS",
+ "recommended": "RRECOMMENDS",
+ "suggested": "RSUGGESTS"
+ }
+
+ shlibs = oe.package.read_shlib_providers(d)
+
+ for pkg, files in pkgfiles.items():
+ # Skip -dbg packages as we won't need to generate dependencies for those
+ # but scanning can take time
+ if pkg.endswith("-dbg"):
+ continue
+
+ for f in files:
+ # Skip symlinks, just look for real libraries
+ if cpath.islink(f):
+ continue
+
+ if ".so." in f or f.endswith(".so"):
+ try:
+ elf = oe.qa.ELFFile(f)
+ elf.open()
+ for dep in parse(extract_segment(f, ".note.dlopen"), elf.isLittleEndian()):
+ for soname in dep["soname"]:
+ if soname in shlibs:
+ # TODO assumes the first match is good
+ package, version = list(shlibs[soname].values())[0]
+ dependency = dep_map[dep["priority"]]
+ bb.note(f"{pkg}: adding {dependency} on {package} via .note.dlopen")
+ d.appendVar(f"{dependency}:{pkg}", f" {package} (>= {version})")
+ else:
+ bb.warn(f"cannot find {soname}")
+ except oe.qa.NotELFFileError as e:
+ bb.note(f"Cannot extract ELF notes: {e}")
+ pass
+}