From 22ad038ac6e4fe5e4a68555f0e70bd0a16fb5616 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 19 Dec 2022 15:36:07 +0100 Subject: ukify: Allow passing multiple directories to --tools --- man/ukify.xml | 8 ++++---- src/ukify/ukify.py | 10 ++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/man/ukify.xml b/man/ukify.xml index 17546d543d..c5bf91de02 100644 --- a/man/ukify.xml +++ b/man/ukify.xml @@ -221,11 +221,11 @@ - + - Specify a directory with helper tools. ukify will look for helper - tools in that directory first, and if not found, try to load them from $PATH in - the usual fashion. + Specify one or more directories with helper tools. ukify will look + for helper tools in those directories first, and if not found, try to load them from + $PATH in the usual fashion. diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py index e9e5d13d13..80a4e9c9ca 100755 --- a/src/ukify/ukify.py +++ b/src/ukify/ukify.py @@ -321,9 +321,10 @@ def check_inputs(opts): def find_tool(name, fallback=None, opts=None): if opts and opts.tools: - tool = opts.tools / name - if tool.exists(): - return tool + for d in opts.tools: + tool = d / name + if tool.exists(): + return tool return fallback or name @@ -656,7 +657,8 @@ usage: ukify [options…] linux initrd… p.add_argument('--tools', type=pathlib.Path, - help='a directory with systemd-measure and other tools') + nargs='+', + help='Directories to search for tools (systemd-measure, llvm-objcopy, ...)') p.add_argument('--output', '-o', type=pathlib.Path, -- cgit v1.2.1 From 789a642738d28cf2a8ad3f65df9c0c136e83af09 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 19 Dec 2022 15:29:43 +0100 Subject: ukify: Prefer using llvm-objcopy instead of objcopy llvm-objcopy works on stubs built for foreign architectures whereas objcopy doesn't so let's prefer using llvm-objcopy instead of objcopy. llvm-objcopy automatically sets the virtual address and doesn't provide an option to set it manually so we only add --change-section-vma when using objcopy The default section flags differ between llvm-objcopy and objcopy so we add a default for the section flags so we make sure all sections are read-only data unless specified otherwise. --- src/ukify/ukify.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py index 80a4e9c9ca..63e7202d3d 100755 --- a/src/ukify/ukify.py +++ b/src/ukify/ukify.py @@ -200,12 +200,12 @@ class Section: name: str content: pathlib.Path tmpfile: typing.IO | None = None - flags: list[str] | None = dataclasses.field(default=None) + flags: list[str] = dataclasses.field(default_factory=lambda: ['data', 'readonly']) offset: int | None = None measure: bool = False @classmethod - def create(cls, name, contents, flags=None, measure=False): + def create(cls, name, contents, **kwargs): if isinstance(contents, str | bytes): mode = 'wt' if isinstance(contents, str) else 'wb' tmp = tempfile.NamedTemporaryFile(mode=mode, prefix=f'tmp{name}') @@ -215,7 +215,7 @@ class Section: else: tmp = None - return cls(name, contents, tmpfile=tmp, flags=flags, measure=measure) + return cls(name, contents, tmpfile=tmp, **kwargs) @classmethod def parse_arg(cls, s): @@ -521,21 +521,22 @@ def make_uki(opts): else: output = opts.output - objcopy_tool = find_tool('objcopy', opts=opts) + objcopy_tool = find_tool('llvm-objcopy', 'objcopy', opts=opts) cmd = [ objcopy_tool, opts.stub, *itertools.chain.from_iterable( - ('--add-section', f'{s.name}={s.content}', - '--change-section-vma', f'{s.name}=0x{s.offset:x}') + ('--add-section', f'{s.name}={s.content}', + '--set-section-flags', f"{s.name}={','.join(s.flags)}") for s in uki.sections), - *itertools.chain.from_iterable( - ('--set-section-flags', f"{s.name}={','.join(s.flags)}") - for s in uki.sections - if s.flags is not None), output, ] + + if pathlib.Path(objcopy_tool).name != 'llvm-objcopy': + cmd += itertools.chain.from_iterable( + ('--change-section-vma', f'{s.name}=0x{s.offset:x}') for s in uki.sections) + print('+', shell_join(cmd)) subprocess.check_call(cmd) -- cgit v1.2.1 From 3fc1ae89e756d1624f5135aec5510a939ed01317 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Wed, 21 Dec 2022 15:18:13 +0100 Subject: ukify: Validate that there are no overlapping sections Let's make sure that after calling objcopy we have no overlapping sections in the UKI --- src/ukify/ukify.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py index 63e7202d3d..8f8537b30e 100755 --- a/src/ukify/ukify.py +++ b/src/ukify/ukify.py @@ -424,6 +424,18 @@ def join_initrds(initrds): assert False +def pe_validate(filename): + import pefile + + pe = pefile.PE(filename) + + sections = sorted(pe.sections, key=lambda s: (s.VirtualAddress, s.Misc_VirtualSize)) + + for l, r in itertools.pairwise(sections): + if l.VirtualAddress + l.Misc_VirtualSize > r.VirtualAddress + r.Misc_VirtualSize: + raise ValueError(f'Section "{l.Name.decode()}" ({l.VirtualAddress}, {l.Misc_VirtualSize}) overlaps with section "{r.Name.decode()}" ({r.VirtualAddress}, {r.Misc_VirtualSize})') + + def make_uki(opts): # kernel payload signing @@ -540,6 +552,8 @@ def make_uki(opts): print('+', shell_join(cmd)) subprocess.check_call(cmd) + pe_validate(output) + # UKI signing if opts.sb_key: -- cgit v1.2.1