@@ -12,6 +12,7 @@ import sys
12
12
import hashlib
13
13
from collections import namedtuple
14
14
from enum import Enum , auto
15
+ import json
15
16
16
17
17
18
class ModTable :
@@ -398,19 +399,13 @@ def install_busybox(dest_dir, sysroot):
398
399
os .symlink ("busybox" , os .path .join (dest_dir , "usr/bin" , c ))
399
400
400
401
401
- def install_misc (dest_dir , sysroot ):
402
+ def install_misc (dest_dir , sysroot , deb_arch ):
402
403
# dmsetup rules
403
404
rules = package_files (["dmsetup" ], sysroot )
404
405
to_include = re .compile (r".*rules.d/" )
405
406
rules = [i for i in rules if to_include .match (i )]
406
407
install_files (rules , dest_dir , sysroot )
407
408
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 ]
414
409
files = [
415
410
"/usr/bin/kmod" ,
416
411
"/usr/bin/mount" ,
@@ -616,6 +611,67 @@ def create_initrd_pkg_list(dest_dir, sysroot):
616
611
pkgs ).decode ("utf-8" )
617
612
pkg_list .write (out )
618
613
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 ]
619
675
620
676
def create_initrd (parser , args ):
621
677
# TODO generate microcode instead of shipping in debian package
@@ -631,6 +687,8 @@ def create_initrd(parser, args):
631
687
if args .kernelver :
632
688
args .output = "-" .join ([args .output , args .kernelver ])
633
689
with tempfile .TemporaryDirectory (suffix = ".ubuntu-core-initramfs" ) as d :
690
+ deb_arch = get_deb_arch (rootfs )
691
+
634
692
kernel_root = os .path .join (d , "kernel" )
635
693
modules = os .path .join (kernel_root , "usr" , "lib" , "modules" )
636
694
os .makedirs (modules , exist_ok = True )
@@ -650,7 +708,7 @@ def create_initrd(parser, args):
650
708
# Copy systemd bits
651
709
install_systemd_files (main , rootfs )
652
710
# Other miscelanea stuff
653
- install_misc (main , rootfs )
711
+ install_misc (main , rootfs , deb_arch )
654
712
# Copy snapd bits
655
713
snapd_lib = path_join_make_rel_paths (rootfs , "/usr/lib/snapd" )
656
714
snapd_files = [os .path .join (snapd_lib , "snap-bootstrap" ),
@@ -698,6 +756,9 @@ def create_initrd(parser, args):
698
756
)
699
757
check_call (["depmod" , "-a" , "-b" , main , args .kernelver ])
700
758
759
+ if not verify_missing_dlopen (main , os .path .join ("/usr/lib" , deb_arch )):
760
+ sys .exit (1 )
761
+
701
762
# Create manifest with packages with files included in the initramfs
702
763
create_initrd_pkg_list (main , rootfs )
703
764
0 commit comments