diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2024-03-13 14:02:46 +0100 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2024-03-14 11:08:41 +0100 |
commit | 8a753717f8734baec3abaf9b6fb4749e61fb9ac9 (patch) | |
tree | 001f9d606e46adedd75ba404cfe7ec9f634fb8ad /tools | |
parent | tools/elf2efi: split out function to create parser (diff) | |
download | systemd-8a753717f8734baec3abaf9b6fb4749e61fb9ac9.tar.xz systemd-8a753717f8734baec3abaf9b6fb4749e61fb9ac9.zip |
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.
Diffstat (limited to 'tools')
-rwxr-xr-x | tools/elf2efi.py | 43 |
1 files changed, 25 insertions, 18 deletions
diff --git a/tools/elf2efi.py b/tools/elf2efi.py index 759c7ab37f..a37c5246bd 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() |