diff --git a/src/verity_squash_root/efi.py b/src/verity_squash_root/efi.py index e5c091f..66735a0 100644 --- a/src/verity_squash_root/efi.py +++ b/src/verity_squash_root/efi.py @@ -1,4 +1,5 @@ import logging +import math from collections import OrderedDict from configparser import ConfigParser from pathlib import Path @@ -47,9 +48,22 @@ def calculate_efi_stub_end(stub: Path) -> int: return int(words[2], 16) + int(words[3], 16) +def calculate_efi_stub_alignment(stub: Path) -> int: + result = exec_binary(["objdump", "-p", str(stub)]) + lines = result[0].decode().split("\n") + for line in lines: + words = line.split() + if len(words) == 2: + if words[0] == "SectionAlignment": + return int(words[1], 16) + raise ValueError("Efistub SectionAlignment not found") + + +# TODO: use systemd-ukify when Debian 13 is stable def create_efi_executable(stub: Path, cmdline_file: Path, linux: Path, initrd: Path, dest: Path): offset = calculate_efi_stub_end(stub) + alignment = calculate_efi_stub_alignment(stub) sections = OrderedDict() sections["osrel"] = Path("/etc/os-release") sections["cmdline"] = cmdline_file @@ -62,6 +76,7 @@ def create_efi_executable(stub: Path, cmdline_file: Path, linux: Path, cmd = ["objcopy"] for name, file in sections.items(): + offset = alignment * math.ceil(offset / alignment) size = file.stat().st_size cmd += ["--add-section", ".{}={}".format(name, str(file)), "--change-section-vma", ".{}={}".format(name, hex(offset))] diff --git a/tests/unit/efi.py b/tests/unit/efi.py index 14b2077..b81f952 100644 --- a/tests/unit/efi.py +++ b/tests/unit/efi.py @@ -1,3 +1,4 @@ +import math import unittest from .test_helper import get_test_files_path from pathlib import Path @@ -6,7 +7,7 @@ from verity_squash_root.file_op import read_from from verity_squash_root.efi import file_matches_slot_or_is_broken, sign, \ create_efi_executable, build_and_sign_kernel, get_cmdline, \ - calculate_efi_stub_end + calculate_efi_stub_end, calculate_efi_stub_alignment TEST_FILES_DIR = get_test_files_path("efi") @@ -62,9 +63,25 @@ def test__calculate_efi_stub_end(self): calculate_efi_stub_end(TEST_FILES_DIR / "stub_slot_a.efi"), 74064) + def test__calculate_efi_stub_alignment(self): + self.assertEqual( + calculate_efi_stub_alignment(TEST_FILES_DIR / "stub_slot_a.efi"), + 512) + + def test__calculate_efi_stub_alignment__not_found(self): + with self.assertRaises(ValueError) as e_ctx: + calculate_efi_stub_alignment(Path("/usr/lib/libc.a")) + self.assertEqual( + str(e_ctx.exception), "Efistub SectionAlignment not found") + + @mock.patch("verity_squash_root.efi.calculate_efi_stub_alignment") @mock.patch("verity_squash_root.efi.exec_binary") @mock.patch("verity_squash_root.efi.calculate_efi_stub_end") - def test__create_efi_executable(self, calc_mock, exec_mock): + def test__create_efi_executable(self, calc_mock, exec_mock, align_mock): + def align(x): + return 2048 * math.ceil(x / 2048) + + align_mock.return_value = 2048 calc_mock.return_value = 74064 create_efi_executable( TEST_FILES_DIR / "stub_slot_a.efi", @@ -72,21 +89,23 @@ def test__create_efi_executable(self, calc_mock, exec_mock): TEST_FILES_DIR / "vmlinuz", TEST_FILES_DIR / "initrd", Path("/tmp/file.efi")) + align_mock.assert_called_once_with(TEST_FILES_DIR / "stub_slot_a.efi") calc_mock.assert_called_once_with(TEST_FILES_DIR / "stub_slot_a.efi") osrel_size = Path("/etc/os-release").stat().st_size + exec_mock.assert_called_once_with([ 'objcopy', '--add-section', '.osrel=/etc/os-release', - '--change-section-vma', '.osrel=0x12150', + '--change-section-vma', '.osrel=0x12800', '--add-section', '.cmdline={}'.format(TEST_FILES_DIR / "cmdline"), '--change-section-vma', '.cmdline={}'.format( - hex(74064 + osrel_size)), + hex(align(0x12800 + osrel_size))), '--add-section', '.initrd={}'.format(TEST_FILES_DIR / "initrd"), '--change-section-vma', '.initrd={}'.format( - hex(74232 + osrel_size)), + hex(align(0x13000 + osrel_size))), '--add-section', '.linux={}'.format(TEST_FILES_DIR / "vmlinuz"), '--change-section-vma', '.linux={}'.format( - hex(76280 + osrel_size)), + hex(align(0x13800 + osrel_size))), str(TEST_FILES_DIR / "stub_slot_a.efi"), str(Path("/tmp/file.efi"))])