From: Zbigniew Jędrzejewski-Szmek Date: Wed, 13 Mar 2024 13:02:46 +0000 (+0100) Subject: tools/elf2efi: rework exception messages X-Git-Tag: v256-rc1~437^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8a753717f8734baec3abaf9b6fb4749e61fb9ac9;p=thirdparty%2Fsystemd.git tools/elf2efi: rework exception messages RuntimeError is documented as "Unspecified run-time error". It doesn't make much sense for Python. (It originated in Java, where exceptions that can be thrown by a function are declared in the function signature. All code calling such a function must either explicitly catch all possible exception types, or allow them to propagate by listing them in its own exception type list. This is nice in theory, but in practice very annoying. Especially during development, when the list of possible exception types is not finalized, we would end up adding and removing exceptions to functions signatures all the time. Also for code which is designed to call functions recursively, we would soon end up with all functions declaring all possible exception types… To avoid this, people would quite often do fake handling with a block that either prints and ignores an exception, or has just a comment like "fix me later", or even nothing. This often lead to people forgetting to adjust this later on and production code containing such constructs. An escape hatch was opened with RuntimeException and its subclasses, which do not need to be pre-declared. Various memory-related exceptions were added as subclasses of RuntimeException. But later on, people starting using this to not to have to declare all exception types everywhere.) In Python, exceptions do no have to be pre-declared, and for code which just encounters a failure, we should raise a specific exception type. The catch-all class for unexpected input is ValueError. For https://github.com/systemd/systemd/issues/31637: BadSectionError: Section '.data' @0x28000 overlaps previous section @0x28000+0x300=@0x28300 Also, exception strings should not contain trailing periods, because they are often embedded in sentences. --- diff --git a/tools/elf2efi.py b/tools/elf2efi.py index 759c7ab37ff..a37c5246bdf 100755 --- a/tools/elf2efi.py +++ b/tools/elf2efi.py @@ -250,6 +250,10 @@ def next_section_address(sections: typing.List[PeSection]) -> int: SECTION_ALIGNMENT) +class BadSectionError(ValueError): + "One of the sections is in a bad state" + + def iter_copy_sections(elf: ELFFile) -> typing.Iterator[PeSection]: pe_s = None @@ -260,7 +264,8 @@ def iter_copy_sections(elf: ELFFile) -> typing.Iterator[PeSection]: relro = None for elf_seg in elf.iter_segments(): if elf_seg["p_type"] == "PT_LOAD" and elf_seg["p_align"] != SECTION_ALIGNMENT: - raise RuntimeError("ELF segments are not properly aligned.") + raise BadSectionError(f"ELF segment {elf_seg['p_type']} is not properly aligned" + f" ({elf_seg['p_align']} != {SECTION_ALIGNMENT})") elif elf_seg["p_type"] == "PT_GNU_RELRO": relro = elf_seg @@ -272,7 +277,7 @@ def iter_copy_sections(elf: ELFFile) -> typing.Iterator[PeSection]: ): continue if elf_s["sh_type"] not in ["SHT_PROGBITS", "SHT_NOBITS"]: - raise RuntimeError(f"Unknown section {elf_s.name}.") + raise BadSectionError(f"Unknown section {elf_s.name} with type {elf_s['sh_type']}") if elf_s["sh_flags"] & SH_FLAGS.SHF_EXECINSTR: rwx = PE_CHARACTERISTICS_RX @@ -304,7 +309,7 @@ def iter_copy_sections(elf: ELFFile) -> typing.Iterator[PeSection]: def convert_sections(elf: ELFFile, opt: PeOptionalHeader) -> typing.List[PeSection]: - last_vma = 0 + last_vma = (0, 0) sections = [] for pe_s in iter_copy_sections(elf): @@ -324,10 +329,11 @@ def convert_sections(elf: ELFFile, opt: PeOptionalHeader) -> typing.List[PeSecti PE_CHARACTERISTICS_R: b".rodata", }[pe_s.Characteristics] - # This can happen if not building with `-z separate-code`. - if pe_s.VirtualAddress < last_vma: - raise RuntimeError("Overlapping PE sections.") - last_vma = pe_s.VirtualAddress + pe_s.VirtualSize + # This can happen if not building with '-z separate-code'. + if pe_s.VirtualAddress < sum(last_vma): + raise BadSectionError(f"Section {pe_s.Name.decode()!r} @0x{pe_s.VirtualAddress:x} overlaps" + f" previous section @0x{last_vma[0]:x}+0x{last_vma[1]:x}=@0x{sum(last_vma):x}") + last_vma = (pe_s.VirtualAddress, pe_s.VirtualSize) if pe_s.Name == b".text": opt.BaseOfCode = pe_s.VirtualAddress @@ -354,9 +360,9 @@ def copy_sections( if not elf_s: continue if elf_s.data_alignment > 1 and SECTION_ALIGNMENT % elf_s.data_alignment != 0: - raise RuntimeError(f"ELF section {name} is not aligned.") + raise BadSectionError(f"ELF section {name} is not aligned") if elf_s["sh_flags"] & (SH_FLAGS.SHF_EXECINSTR | SH_FLAGS.SHF_WRITE) != 0: - raise RuntimeError(f"ELF section {name} is not read-only data.") + raise BadSectionError(f"ELF section {name} is not read-only data") pe_s = PeSection() pe_s.Name = name.encode() @@ -438,7 +444,7 @@ def convert_elf_reloc_table( continue - raise RuntimeError(f"Unsupported relocation {reloc}") + raise BadSectionError(f"Unsupported relocation {reloc}") def convert_elf_relocations( @@ -449,18 +455,18 @@ def convert_elf_relocations( ) -> typing.Optional[PeSection]: dynamic = elf.get_section_by_name(".dynamic") if dynamic is None: - raise RuntimeError("ELF .dynamic section is missing.") + raise BadSectionError("ELF .dynamic section is missing") [flags_tag] = dynamic.iter_tags("DT_FLAGS_1") if not flags_tag["d_val"] & ENUM_DT_FLAGS_1["DF_1_PIE"]: - raise RuntimeError("ELF file is not a PIE.") + raise ValueError("ELF file is not a PIE") # This checks that the ELF image base is 0. symtab = elf.get_section_by_name(".symtab") if symtab: exe_start = symtab.get_symbol_by_name("__executable_start") if exe_start and exe_start[0]["st_value"] != 0: - raise RuntimeError("Unexpected ELF image base.") + raise ValueError("Unexpected ELF image base") opt.SizeOfHeaders = align_to(PE_OFFSET + len(PE_MAGIC) @@ -487,7 +493,7 @@ def convert_elf_relocations( pe_reloc_blocks: typing.Dict[int, PeRelocationBlock] = {} for reloc_type, reloc_table in dynamic.get_relocation_tables().items(): if reloc_type not in ["REL", "RELA"]: - raise RuntimeError("Unsupported relocation type {elf_reloc_type}.") + raise BadSectionError(f"Unsupported relocation type {reloc_type}") convert_elf_reloc_table(elf, reloc_table, opt.ImageBase + segment_offset, @@ -548,7 +554,8 @@ def write_pe( offset = opt.SizeOfHeaders for pe_s in sorted(sections, key=lambda s: s.VirtualAddress): if pe_s.VirtualAddress < opt.SizeOfHeaders: - raise RuntimeError(f"Section {pe_s.Name} overlapping PE headers.") + raise BadSectionError(f"Section {pe_s.Name} @0x{pe_s.VirtualAddress:x} overlaps" + " PE headers ending at 0x{opt.SizeOfHeaders:x}") pe_s.PointerToRawData = offset file.write(pe_s) @@ -566,9 +573,9 @@ def write_pe( def elf2efi(args: argparse.Namespace): elf = ELFFile(args.ELF) if not elf.little_endian: - raise RuntimeError("ELF file is not little-endian.") + raise ValueError("ELF file is not little-endian") if elf["e_type"] not in ["ET_DYN", "ET_EXEC"]: - raise RuntimeError("Unsupported ELF type.") + raise ValueError(f"Unsupported ELF type {elf['e_type']}") pe_arch = { "EM_386": 0x014C, @@ -579,7 +586,7 @@ def elf2efi(args: argparse.Namespace): "EM_X86_64": 0x8664, }.get(elf["e_machine"]) if pe_arch is None: - raise RuntimeError(f"Unsupported ELF arch {elf['e_machine']}") + raise ValueError(f"Unsupported ELF architecture {elf['e_machine']}") coff = PeCoffHeader() opt = PeOptionalHeader32() if elf.elfclass == 32 else PeOptionalHeader32Plus()