Skip to content

Commit

Permalink
tools/elf2efi: rework exception messages
Browse files Browse the repository at this point in the history
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.

(cherry picked from commit 8a75371)
  • Loading branch information
keszybz committed Apr 23, 2024
1 parent d120247 commit d1a1c0f
Showing 1 changed file with 25 additions and 18 deletions.
43 changes: 25 additions & 18 deletions tools/elf2efi.py
Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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):
Expand All @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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(
Expand All @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand All @@ -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()
Expand Down

0 comments on commit d1a1c0f

Please sign in to comment.