Skip to content

Commit 8a75371

Browse files
committed
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 systemd/systemd#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.
1 parent 642f991 commit 8a75371

File tree

1 file changed

+25
-18
lines changed

1 file changed

+25
-18
lines changed

tools/elf2efi.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ def next_section_address(sections: typing.List[PeSection]) -> int:
250250
SECTION_ALIGNMENT)
251251

252252

253+
class BadSectionError(ValueError):
254+
"One of the sections is in a bad state"
255+
256+
253257
def iter_copy_sections(elf: ELFFile) -> typing.Iterator[PeSection]:
254258
pe_s = None
255259

@@ -260,7 +264,8 @@ def iter_copy_sections(elf: ELFFile) -> typing.Iterator[PeSection]:
260264
relro = None
261265
for elf_seg in elf.iter_segments():
262266
if elf_seg["p_type"] == "PT_LOAD" and elf_seg["p_align"] != SECTION_ALIGNMENT:
263-
raise RuntimeError("ELF segments are not properly aligned.")
267+
raise BadSectionError(f"ELF segment {elf_seg['p_type']} is not properly aligned"
268+
f" ({elf_seg['p_align']} != {SECTION_ALIGNMENT})")
264269
elif elf_seg["p_type"] == "PT_GNU_RELRO":
265270
relro = elf_seg
266271

@@ -272,7 +277,7 @@ def iter_copy_sections(elf: ELFFile) -> typing.Iterator[PeSection]:
272277
):
273278
continue
274279
if elf_s["sh_type"] not in ["SHT_PROGBITS", "SHT_NOBITS"]:
275-
raise RuntimeError(f"Unknown section {elf_s.name}.")
280+
raise BadSectionError(f"Unknown section {elf_s.name} with type {elf_s['sh_type']}")
276281

277282
if elf_s["sh_flags"] & SH_FLAGS.SHF_EXECINSTR:
278283
rwx = PE_CHARACTERISTICS_RX
@@ -304,7 +309,7 @@ def iter_copy_sections(elf: ELFFile) -> typing.Iterator[PeSection]:
304309

305310

306311
def convert_sections(elf: ELFFile, opt: PeOptionalHeader) -> typing.List[PeSection]:
307-
last_vma = 0
312+
last_vma = (0, 0)
308313
sections = []
309314

310315
for pe_s in iter_copy_sections(elf):
@@ -324,10 +329,11 @@ def convert_sections(elf: ELFFile, opt: PeOptionalHeader) -> typing.List[PeSecti
324329
PE_CHARACTERISTICS_R: b".rodata",
325330
}[pe_s.Characteristics]
326331

327-
# This can happen if not building with `-z separate-code`.
328-
if pe_s.VirtualAddress < last_vma:
329-
raise RuntimeError("Overlapping PE sections.")
330-
last_vma = pe_s.VirtualAddress + pe_s.VirtualSize
332+
# This can happen if not building with '-z separate-code'.
333+
if pe_s.VirtualAddress < sum(last_vma):
334+
raise BadSectionError(f"Section {pe_s.Name.decode()!r} @0x{pe_s.VirtualAddress:x} overlaps"
335+
f" previous section @0x{last_vma[0]:x}+0x{last_vma[1]:x}=@0x{sum(last_vma):x}")
336+
last_vma = (pe_s.VirtualAddress, pe_s.VirtualSize)
331337

332338
if pe_s.Name == b".text":
333339
opt.BaseOfCode = pe_s.VirtualAddress
@@ -354,9 +360,9 @@ def copy_sections(
354360
if not elf_s:
355361
continue
356362
if elf_s.data_alignment > 1 and SECTION_ALIGNMENT % elf_s.data_alignment != 0:
357-
raise RuntimeError(f"ELF section {name} is not aligned.")
363+
raise BadSectionError(f"ELF section {name} is not aligned")
358364
if elf_s["sh_flags"] & (SH_FLAGS.SHF_EXECINSTR | SH_FLAGS.SHF_WRITE) != 0:
359-
raise RuntimeError(f"ELF section {name} is not read-only data.")
365+
raise BadSectionError(f"ELF section {name} is not read-only data")
360366

361367
pe_s = PeSection()
362368
pe_s.Name = name.encode()
@@ -438,7 +444,7 @@ def convert_elf_reloc_table(
438444

439445
continue
440446

441-
raise RuntimeError(f"Unsupported relocation {reloc}")
447+
raise BadSectionError(f"Unsupported relocation {reloc}")
442448

443449

444450
def convert_elf_relocations(
@@ -449,18 +455,18 @@ def convert_elf_relocations(
449455
) -> typing.Optional[PeSection]:
450456
dynamic = elf.get_section_by_name(".dynamic")
451457
if dynamic is None:
452-
raise RuntimeError("ELF .dynamic section is missing.")
458+
raise BadSectionError("ELF .dynamic section is missing")
453459

454460
[flags_tag] = dynamic.iter_tags("DT_FLAGS_1")
455461
if not flags_tag["d_val"] & ENUM_DT_FLAGS_1["DF_1_PIE"]:
456-
raise RuntimeError("ELF file is not a PIE.")
462+
raise ValueError("ELF file is not a PIE")
457463

458464
# This checks that the ELF image base is 0.
459465
symtab = elf.get_section_by_name(".symtab")
460466
if symtab:
461467
exe_start = symtab.get_symbol_by_name("__executable_start")
462468
if exe_start and exe_start[0]["st_value"] != 0:
463-
raise RuntimeError("Unexpected ELF image base.")
469+
raise ValueError("Unexpected ELF image base")
464470

465471
opt.SizeOfHeaders = align_to(PE_OFFSET
466472
+ len(PE_MAGIC)
@@ -487,7 +493,7 @@ def convert_elf_relocations(
487493
pe_reloc_blocks: typing.Dict[int, PeRelocationBlock] = {}
488494
for reloc_type, reloc_table in dynamic.get_relocation_tables().items():
489495
if reloc_type not in ["REL", "RELA"]:
490-
raise RuntimeError("Unsupported relocation type {elf_reloc_type}.")
496+
raise BadSectionError(f"Unsupported relocation type {reloc_type}")
491497
convert_elf_reloc_table(elf,
492498
reloc_table,
493499
opt.ImageBase + segment_offset,
@@ -548,7 +554,8 @@ def write_pe(
548554
offset = opt.SizeOfHeaders
549555
for pe_s in sorted(sections, key=lambda s: s.VirtualAddress):
550556
if pe_s.VirtualAddress < opt.SizeOfHeaders:
551-
raise RuntimeError(f"Section {pe_s.Name} overlapping PE headers.")
557+
raise BadSectionError(f"Section {pe_s.Name} @0x{pe_s.VirtualAddress:x} overlaps"
558+
" PE headers ending at 0x{opt.SizeOfHeaders:x}")
552559

553560
pe_s.PointerToRawData = offset
554561
file.write(pe_s)
@@ -566,9 +573,9 @@ def write_pe(
566573
def elf2efi(args: argparse.Namespace):
567574
elf = ELFFile(args.ELF)
568575
if not elf.little_endian:
569-
raise RuntimeError("ELF file is not little-endian.")
576+
raise ValueError("ELF file is not little-endian")
570577
if elf["e_type"] not in ["ET_DYN", "ET_EXEC"]:
571-
raise RuntimeError("Unsupported ELF type.")
578+
raise ValueError(f"Unsupported ELF type {elf['e_type']}")
572579

573580
pe_arch = {
574581
"EM_386": 0x014C,
@@ -579,7 +586,7 @@ def elf2efi(args: argparse.Namespace):
579586
"EM_X86_64": 0x8664,
580587
}.get(elf["e_machine"])
581588
if pe_arch is None:
582-
raise RuntimeError(f"Unsupported ELF arch {elf['e_machine']}")
589+
raise ValueError(f"Unsupported ELF architecture {elf['e_machine']}")
583590

584591
coff = PeCoffHeader()
585592
opt = PeOptionalHeader32() if elf.elfclass == 32 else PeOptionalHeader32Plus()

0 commit comments

Comments
 (0)