diff --git a/meta/recipes-core/systemd/dlopen-deps.inc b/meta/recipes-core/systemd/dlopen-deps.inc new file mode 100644 index 0000000000..eaf6ca1f79 --- /dev/null +++ b/meta/recipes-core/systemd/dlopen-deps.inc @@ -0,0 +1,73 @@ +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 +} diff --git a/meta/recipes-core/systemd/systemd_256.7.bb b/meta/recipes-core/systemd/systemd_256.7.bb index 8b7ed2efe2..97537d09ba 100644 --- a/meta/recipes-core/systemd/systemd_256.7.bb +++ b/meta/recipes-core/systemd/systemd_256.7.bb @@ -742,8 +742,6 @@ INSANE_SKIP:libsystemd-shared += "libdir" FILES:libsystemd-shared = "${rootlibdir}/systemd/libsystemd-shared*.so" RPROVIDES:udev = "hotplug" -# This can be removed when we parse .note.dlopen in the ELF -RDEPENDS:udev += "libkmod" RDEPENDS:udev-bash-completion += "bash-completion" RDEPENDS:udev-hwdb += "udev" @@ -915,3 +913,5 @@ pkg_postinst:udev-hwdb () { pkg_prerm:udev-hwdb () { rm -f $D${sysconfdir}/udev/hwdb.bin } + +require dlopen-deps.inc