@@ -12,6 +12,7 @@ import sys
1212import hashlib
1313from collections import namedtuple
1414from enum import Enum , auto
15+ import json
1516
1617
1718class ModTable :
@@ -398,19 +399,13 @@ def install_busybox(dest_dir, sysroot):
398399 os .symlink ("busybox" , os .path .join (dest_dir , "usr/bin" , c ))
399400
400401
401- def install_misc (dest_dir , sysroot ):
402+ def install_misc (dest_dir , sysroot , deb_arch ):
402403 # dmsetup rules
403404 rules = package_files (["dmsetup" ], sysroot )
404405 to_include = re .compile (r".*rules.d/" )
405406 rules = [i for i in rules if to_include .match (i )]
406407 install_files (rules , dest_dir , sysroot )
407408
408- # Other needed stuff
409- proc_env = os .environ .copy ()
410- proc_env ["DPKG_DATADIR" ] = sysroot + "/usr/share/dpkg"
411- out = check_output (["dpkg-architecture" , "-q" ,
412- "DEB_HOST_MULTIARCH" ], env = proc_env ).decode ("utf-8" )
413- deb_arch = out .splitlines ()[0 ]
414409 files = [
415410 "/usr/bin/kmod" ,
416411 "/usr/bin/mount" ,
@@ -616,6 +611,67 @@ def create_initrd_pkg_list(dest_dir, sysroot):
616611 pkgs ).decode ("utf-8" )
617612 pkg_list .write (out )
618613
614+ # verify_missing_dlopen looks at the .notes.dlopen section of ELF
615+ # binaries to find libraries that are not in the dynamic section, and
616+ # that will be loaded with dynamically dlopen when needed.
617+ # See https://systemd.io/ELF_DLOPEN_METADATA/
618+ def verify_missing_dlopen (destdir , libdir ):
619+ missing = {}
620+ for dirpath , dirs , files in os .walk (destdir ):
621+ for f in files :
622+ path = os .path .join (dirpath , f )
623+ if os .path .islink (path ) or not os .path .isfile (path ):
624+ continue
625+ with open (path , 'rb' ) as b :
626+ if b .read (4 ) != b'\x7f ELF' :
627+ continue
628+ out = check_output (["dlopen-notes" , path ])
629+ split = out .splitlines ()
630+ json_doc = b'\n ' .join ([s for s in split if not s [:1 ] == b'#' ])
631+ doc = json .loads (json_doc )
632+ for dep in doc :
633+ sonames = dep ["soname" ]
634+ priority = dep ["priority" ]
635+ found_sonames = []
636+ for soname in sonames :
637+ dest = os .path .join (destdir , os .path .relpath (libdir , "/" ), soname )
638+ if os .path .exists (os .path .join (destdir , dest )):
639+ found_sonames .append (soname )
640+ if not found_sonames :
641+ # We did not find any library.
642+ # In this case we need to mark all sonames as
643+ # missing. This is required because some features
644+ # may have common subset of sonames and those
645+ # features might have different priorities.
646+ for soname in sonames :
647+ current_priority = missing .get (soname )
648+ if current_priority == "required" :
649+ continue
650+ elif current_priority == "recommended" and priority not in ["required" ]:
651+ continue
652+ elif current_priority == "suggested" and priority not in ["required" , "recommended" ]:
653+ continue
654+ else :
655+ missing [soname ] = priority
656+
657+ fatal = False
658+ if missing :
659+ print (f"WARNING: These sonames are missing:" , file = sys .stderr )
660+ for m , priority in missing .items ():
661+ print (f" * { m } ({ priority } )" , file = sys .stderr )
662+ if priority in ["required" , "recommended" ]:
663+ fatal = True
664+ if fatal :
665+ print (f"WARNING: Some missing sonames are required or recommended. Failing." , file = sys .stderr )
666+
667+ return not fatal
668+
669+ def get_deb_arch (sysroot ):
670+ proc_env = os .environ .copy ()
671+ proc_env ["DPKG_DATADIR" ] = sysroot + "/usr/share/dpkg"
672+ out = check_output (["dpkg-architecture" , "-q" ,
673+ "DEB_HOST_MULTIARCH" ], env = proc_env ).decode ("utf-8" )
674+ return out .splitlines ()[0 ]
619675
620676def create_initrd (parser , args ):
621677 # TODO generate microcode instead of shipping in debian package
@@ -631,6 +687,8 @@ def create_initrd(parser, args):
631687 if args .kernelver :
632688 args .output = "-" .join ([args .output , args .kernelver ])
633689 with tempfile .TemporaryDirectory (suffix = ".ubuntu-core-initramfs" ) as d :
690+ deb_arch = get_deb_arch (rootfs )
691+
634692 kernel_root = os .path .join (d , "kernel" )
635693 modules = os .path .join (kernel_root , "usr" , "lib" , "modules" )
636694 os .makedirs (modules , exist_ok = True )
@@ -650,7 +708,7 @@ def create_initrd(parser, args):
650708 # Copy systemd bits
651709 install_systemd_files (main , rootfs )
652710 # Other miscelanea stuff
653- install_misc (main , rootfs )
711+ install_misc (main , rootfs , deb_arch )
654712 # Copy snapd bits
655713 snapd_lib = path_join_make_rel_paths (rootfs , "/usr/lib/snapd" )
656714 snapd_files = [os .path .join (snapd_lib , "snap-bootstrap" ),
@@ -698,6 +756,9 @@ def create_initrd(parser, args):
698756 )
699757 check_call (["depmod" , "-a" , "-b" , main , args .kernelver ])
700758
759+ if not verify_missing_dlopen (main , os .path .join ("/usr/lib" , deb_arch )):
760+ sys .exit (1 )
761+
701762 # Create manifest with packages with files included in the initramfs
702763 create_initrd_pkg_list (main , rootfs )
703764
0 commit comments