From: Yu Watanabe Date: Thu, 19 Feb 2026 17:08:46 +0000 (+0900) Subject: elf2efi: apply "ruff format" and "ruff check --fix" X-Git-Tag: v261-rc1~126^2~21 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0df17e665b4efdb1f73a154d477a20cf806d61d6;p=thirdparty%2Fsystemd.git elf2efi: apply "ruff format" and "ruff check --fix" --- diff --git a/tools/elf2efi.py b/tools/elf2efi.py index ed705beb316..1442d1a7236 100755 --- a/tools/elf2efi.py +++ b/tools/elf2efi.py @@ -31,12 +31,12 @@ import sys import time import typing from ctypes import ( + LittleEndianStructure, c_char, c_uint8, c_uint16, c_uint32, c_uint64, - LittleEndianStructure, sizeof, ) @@ -46,28 +46,28 @@ from elftools.elf import elffile class PeCoffHeader(LittleEndianStructure): _fields_ = ( - ("Machine", c_uint16), - ("NumberOfSections", c_uint16), - ("TimeDateStamp", c_uint32), - ("PointerToSymbolTable", c_uint32), - ("NumberOfSymbols", c_uint32), - ("SizeOfOptionalHeader", c_uint16), - ("Characteristics", c_uint16), - ) + ('Machine', c_uint16), + ('NumberOfSections', c_uint16), + ('TimeDateStamp', c_uint32), + ('PointerToSymbolTable', c_uint32), + ('NumberOfSymbols', c_uint32), + ('SizeOfOptionalHeader', c_uint16), + ('Characteristics', c_uint16), + ) # fmt: skip class PeDataDirectory(LittleEndianStructure): _fields_ = ( - ("VirtualAddress", c_uint32), - ("Size", c_uint32), - ) + ('VirtualAddress', c_uint32), + ('Size', c_uint32), + ) # fmt: skip class PeRelocationBlock(LittleEndianStructure): _fields_ = ( - ("PageRVA", c_uint32), - ("BlockSize", c_uint32), - ) + ('PageRVA', c_uint32), + ('BlockSize', c_uint32), + ) # fmt: skip def __init__(self, PageRVA: int): super().__init__(PageRVA) @@ -76,64 +76,64 @@ class PeRelocationBlock(LittleEndianStructure): class PeRelocationEntry(LittleEndianStructure): _fields_ = ( - ("Offset", c_uint16, 12), - ("Type", c_uint16, 4), - ) + ('Offset', c_uint16, 12), + ('Type', c_uint16, 4), + ) # fmt: skip class PeOptionalHeaderStart(LittleEndianStructure): _fields_ = ( - ("Magic", c_uint16), - ("MajorLinkerVersion", c_uint8), - ("MinorLinkerVersion", c_uint8), - ("SizeOfCode", c_uint32), - ("SizeOfInitializedData", c_uint32), - ("SizeOfUninitializedData", c_uint32), - ("AddressOfEntryPoint", c_uint32), - ("BaseOfCode", c_uint32), - ) + ('Magic', c_uint16), + ('MajorLinkerVersion', c_uint8), + ('MinorLinkerVersion', c_uint8), + ('SizeOfCode', c_uint32), + ('SizeOfInitializedData', c_uint32), + ('SizeOfUninitializedData', c_uint32), + ('AddressOfEntryPoint', c_uint32), + ('BaseOfCode', c_uint32), + ) # fmt: skip class PeOptionalHeaderMiddle(LittleEndianStructure): _fields_ = ( - ("SectionAlignment", c_uint32), - ("FileAlignment", c_uint32), - ("MajorOperatingSystemVersion", c_uint16), - ("MinorOperatingSystemVersion", c_uint16), - ("MajorImageVersion", c_uint16), - ("MinorImageVersion", c_uint16), - ("MajorSubsystemVersion", c_uint16), - ("MinorSubsystemVersion", c_uint16), - ("Win32VersionValue", c_uint32), - ("SizeOfImage", c_uint32), - ("SizeOfHeaders", c_uint32), - ("CheckSum", c_uint32), - ("Subsystem", c_uint16), - ("DllCharacteristics", c_uint16), - ) + ('SectionAlignment', c_uint32), + ('FileAlignment', c_uint32), + ('MajorOperatingSystemVersion', c_uint16), + ('MinorOperatingSystemVersion', c_uint16), + ('MajorImageVersion', c_uint16), + ('MinorImageVersion', c_uint16), + ('MajorSubsystemVersion', c_uint16), + ('MinorSubsystemVersion', c_uint16), + ('Win32VersionValue', c_uint32), + ('SizeOfImage', c_uint32), + ('SizeOfHeaders', c_uint32), + ('CheckSum', c_uint32), + ('Subsystem', c_uint16), + ('DllCharacteristics', c_uint16), + ) # fmt: skip class PeOptionalHeaderEnd(LittleEndianStructure): _fields_ = ( - ("LoaderFlags", c_uint32), - ("NumberOfRvaAndSizes", c_uint32), - ("ExportTable", PeDataDirectory), - ("ImportTable", PeDataDirectory), - ("ResourceTable", PeDataDirectory), - ("ExceptionTable", PeDataDirectory), - ("CertificateTable", PeDataDirectory), - ("BaseRelocationTable", PeDataDirectory), - ("Debug", PeDataDirectory), - ("Architecture", PeDataDirectory), - ("GlobalPtr", PeDataDirectory), - ("TLSTable", PeDataDirectory), - ("LoadConfigTable", PeDataDirectory), - ("BoundImport", PeDataDirectory), - ("IAT", PeDataDirectory), - ("DelayImportDescriptor", PeDataDirectory), - ("CLRRuntimeHeader", PeDataDirectory), - ("Reserved", PeDataDirectory), - ) + ('LoaderFlags', c_uint32), + ('NumberOfRvaAndSizes', c_uint32), + ('ExportTable', PeDataDirectory), + ('ImportTable', PeDataDirectory), + ('ResourceTable', PeDataDirectory), + ('ExceptionTable', PeDataDirectory), + ('CertificateTable', PeDataDirectory), + ('BaseRelocationTable', PeDataDirectory), + ('Debug', PeDataDirectory), + ('Architecture', PeDataDirectory), + ('GlobalPtr', PeDataDirectory), + ('TLSTable', PeDataDirectory), + ('LoadConfigTable', PeDataDirectory), + ('BoundImport', PeDataDirectory), + ('IAT', PeDataDirectory), + ('DelayImportDescriptor', PeDataDirectory), + ('CLRRuntimeHeader', PeDataDirectory), + ('Reserved', PeDataDirectory), + ) # fmt: skip class PeOptionalHeader(LittleEndianStructure): @@ -141,47 +141,47 @@ class PeOptionalHeader(LittleEndianStructure): class PeOptionalHeader32(PeOptionalHeader): - _anonymous_ = ("Start", "Middle", "End") + _anonymous_ = ('Start', 'Middle', 'End') _fields_ = ( - ("Start", PeOptionalHeaderStart), - ("BaseOfData", c_uint32), - ("ImageBase", c_uint32), - ("Middle", PeOptionalHeaderMiddle), - ("SizeOfStackReserve", c_uint32), - ("SizeOfStackCommit", c_uint32), - ("SizeOfHeapReserve", c_uint32), - ("SizeOfHeapCommit", c_uint32), - ("End", PeOptionalHeaderEnd), - ) + ('Start', PeOptionalHeaderStart), + ('BaseOfData', c_uint32), + ('ImageBase', c_uint32), + ('Middle', PeOptionalHeaderMiddle), + ('SizeOfStackReserve', c_uint32), + ('SizeOfStackCommit', c_uint32), + ('SizeOfHeapReserve', c_uint32), + ('SizeOfHeapCommit', c_uint32), + ('End', PeOptionalHeaderEnd), + ) # fmt: skip class PeOptionalHeader32Plus(PeOptionalHeader): - _anonymous_ = ("Start", "Middle", "End") + _anonymous_ = ('Start', 'Middle', 'End') _fields_ = ( - ("Start", PeOptionalHeaderStart), - ("ImageBase", c_uint64), - ("Middle", PeOptionalHeaderMiddle), - ("SizeOfStackReserve", c_uint64), - ("SizeOfStackCommit", c_uint64), - ("SizeOfHeapReserve", c_uint64), - ("SizeOfHeapCommit", c_uint64), - ("End", PeOptionalHeaderEnd), - ) + ('Start', PeOptionalHeaderStart), + ('ImageBase', c_uint64), + ('Middle', PeOptionalHeaderMiddle), + ('SizeOfStackReserve', c_uint64), + ('SizeOfStackCommit', c_uint64), + ('SizeOfHeapReserve', c_uint64), + ('SizeOfHeapCommit', c_uint64), + ('End', PeOptionalHeaderEnd), + ) # fmt: skip class PeSection(LittleEndianStructure): _fields_ = ( - ("Name", c_char * 8), - ("VirtualSize", c_uint32), - ("VirtualAddress", c_uint32), - ("SizeOfRawData", c_uint32), - ("PointerToRawData", c_uint32), - ("PointerToRelocations", c_uint32), - ("PointerToLinenumbers", c_uint32), - ("NumberOfRelocations", c_uint16), - ("NumberOfLinenumbers", c_uint16), - ("Characteristics", c_uint32), - ) + ('Name', c_char * 8), + ('VirtualSize', c_uint32), + ('VirtualAddress', c_uint32), + ('SizeOfRawData', c_uint32), + ('PointerToRawData', c_uint32), + ('PointerToRelocations', c_uint32), + ('PointerToLinenumbers', c_uint32), + ('NumberOfRelocations', c_uint16), + ('NumberOfLinenumbers', c_uint16), + ('Characteristics', c_uint32), + ) # fmt: skip def __init__(self) -> None: super().__init__() @@ -195,30 +195,32 @@ assert sizeof(PeCoffHeader) == 20 assert sizeof(PeOptionalHeader32) == 224 assert sizeof(PeOptionalHeader32Plus) == 240 +# fmt: off PE_CHARACTERISTICS_RX = 0x60000020 # CNT_CODE|MEM_READ|MEM_EXECUTE PE_CHARACTERISTICS_RW = 0xC0000040 # CNT_INITIALIZED_DATA|MEM_READ|MEM_WRITE PE_CHARACTERISTICS_R = 0x40000040 # CNT_INITIALIZED_DATA|MEM_READ +# fmt: on IGNORE_SECTIONS = [ - ".eh_frame", - ".eh_frame_hdr", - ".ARM.exidx", - ".relro_padding", - ".sframe", + '.eh_frame', + '.eh_frame_hdr', + '.ARM.exidx', + '.relro_padding', + '.sframe', ] IGNORE_SECTION_TYPES = [ - "SHT_DYNAMIC", - "SHT_DYNSYM", - "SHT_GNU_ATTRIBUTES", - "SHT_GNU_HASH", - "SHT_HASH", - "SHT_NOTE", - "SHT_REL", - "SHT_RELA", - "SHT_RELR", - "SHT_STRTAB", - "SHT_SYMTAB", + 'SHT_DYNAMIC', + 'SHT_DYNSYM', + 'SHT_GNU_ATTRIBUTES', + 'SHT_GNU_HASH', + 'SHT_HASH', + 'SHT_NOTE', + 'SHT_REL', + 'SHT_RELA', + 'SHT_RELR', + 'SHT_STRTAB', + 'SHT_SYMTAB', ] # EFI mandates 4KiB memory pages. @@ -227,7 +229,7 @@ FILE_ALIGNMENT = 512 # Nobody cares about DOS headers, so put the PE header right after. PE_OFFSET = 64 -PE_MAGIC = b"PE\0\0" +PE_MAGIC = b'PE\0\0' def align_to(x: int, align: int) -> int: @@ -239,8 +241,7 @@ def align_down(x: int, align: int) -> int: def next_section_address(sections: list[PeSection]) -> int: - return align_to(sections[-1].VirtualAddress + sections[-1].VirtualSize, - SECTION_ALIGNMENT) + return align_to(sections[-1].VirtualAddress + sections[-1].VirtualSize, SECTION_ALIGNMENT) class BadSectionError(ValueError): @@ -256,29 +257,30 @@ def iter_copy_sections(file: elffile.ELFFile) -> typing.Iterator[PeSection]: relro = None for elf_seg in file.iter_segments(): - if elf_seg["p_type"] == "PT_LOAD" and elf_seg["p_align"] != SECTION_ALIGNMENT: - raise BadSectionError(f"ELF segment {elf_seg['p_type']} is not properly aligned" - f" ({elf_seg['p_align']} != {SECTION_ALIGNMENT})") - if elf_seg["p_type"] == "PT_GNU_RELRO": + if elf_seg['p_type'] == 'PT_LOAD' and elf_seg['p_align'] != SECTION_ALIGNMENT: + raise BadSectionError( + f'ELF segment {elf_seg["p_type"]} is not properly aligned ({elf_seg["p_align"]} != {SECTION_ALIGNMENT})' + ) + if elf_seg['p_type'] == 'PT_GNU_RELRO': relro = elf_seg for elf_s in file.iter_sections(): if ( - elf_s["sh_flags"] & elf.constants.SH_FLAGS.SHF_ALLOC == 0 - or elf_s["sh_type"] in IGNORE_SECTION_TYPES + elf_s['sh_flags'] & elf.constants.SH_FLAGS.SHF_ALLOC == 0 + or elf_s['sh_type'] in IGNORE_SECTION_TYPES or elf_s.name in IGNORE_SECTIONS - or elf_s["sh_size"] == 0 + or elf_s['sh_size'] == 0 ): continue - if elf_s["sh_type"] not in ["SHT_PROGBITS", "SHT_NOBITS"]: - raise BadSectionError(f"Unknown section {elf_s.name} with type {elf_s['sh_type']}") + if elf_s['sh_type'] not in ['SHT_PROGBITS', 'SHT_NOBITS']: + raise BadSectionError(f'Unknown section {elf_s.name} with type {elf_s["sh_type"]}') if elf_s.name == '.got': # FIXME: figure out why those sections are inserted - print("WARNING: Non-empty .got section", file=sys.stderr) + print('WARNING: Non-empty .got section', file=sys.stderr) - if elf_s["sh_flags"] & elf.constants.SH_FLAGS.SHF_EXECINSTR: + if elf_s['sh_flags'] & elf.constants.SH_FLAGS.SHF_EXECINSTR: rwx = PE_CHARACTERISTICS_RX - elif elf_s["sh_flags"] & elf.constants.SH_FLAGS.SHF_WRITE: + elif elf_s['sh_flags'] & elf.constants.SH_FLAGS.SHF_WRITE: rwx = PE_CHARACTERISTICS_RW else: rwx = PE_CHARACTERISTICS_R @@ -293,11 +295,11 @@ def iter_copy_sections(file: elffile.ELFFile) -> typing.Iterator[PeSection]: if pe_s: # Insert padding to properly align the section. - pad_len = elf_s["sh_addr"] - pe_s.VirtualAddress - len(pe_s.data) + pad_len = elf_s['sh_addr'] - pe_s.VirtualAddress - len(pe_s.data) pe_s.data += bytearray(pad_len) + elf_s.data() else: pe_s = PeSection() - pe_s.VirtualAddress = elf_s["sh_addr"] + pe_s.VirtualAddress = elf_s['sh_addr'] pe_s.Characteristics = rwx pe_s.data = elf_s.data() @@ -306,8 +308,8 @@ def iter_copy_sections(file: elffile.ELFFile) -> typing.Iterator[PeSection]: def convert_sections( - file: elffile.ELFFile, - opt: PeOptionalHeader, + file: elffile.ELFFile, + opt: PeOptionalHeader, ) -> list[PeSection]: last_vma = (0, 0) sections = [] @@ -324,24 +326,25 @@ def convert_sections( pe_s.VirtualSize = len(pe_s.data) pe_s.SizeOfRawData = align_to(len(pe_s.data), FILE_ALIGNMENT) pe_s.Name = { - PE_CHARACTERISTICS_RX: b".text", - PE_CHARACTERISTICS_RW: b".data", - PE_CHARACTERISTICS_R: b".rodata", + PE_CHARACTERISTICS_RX: b'.text', + PE_CHARACTERISTICS_RW: b'.data', + PE_CHARACTERISTICS_R: b'.rodata', }[pe_s.Characteristics] # 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}") + raise BadSectionError( + f'Section {pe_s.Name.decode()!r} @{pe_s.VirtualAddress:#x} overlaps previous section @{last_vma[0]:#x}+{last_vma[1]:#x}=@{sum(last_vma):#x}' + ) last_vma = (pe_s.VirtualAddress, pe_s.VirtualSize) - if pe_s.Name == b".text": + if pe_s.Name == b'.text': opt.BaseOfCode = pe_s.VirtualAddress opt.SizeOfCode += pe_s.VirtualSize else: opt.SizeOfInitializedData += pe_s.VirtualSize - if pe_s.Name == b".data" and isinstance(opt, PeOptionalHeader32): + if pe_s.Name == b'.data' and isinstance(opt, PeOptionalHeader32): opt.BaseOfData = pe_s.VirtualAddress sections.append(pe_s) @@ -355,14 +358,14 @@ def copy_sections( input_names: str, sections: list[PeSection], ) -> None: - for name in input_names.split(","): + for name in input_names.split(','): elf_s = file.get_section_by_name(name) if not elf_s: continue if elf_s.data_alignment > 1 and SECTION_ALIGNMENT % elf_s.data_alignment != 0: - raise BadSectionError(f"ELF section {name} is not aligned") - if elf_s["sh_flags"] & (elf.constants.SH_FLAGS.SHF_EXECINSTR | elf.constants.SH_FLAGS.SHF_WRITE) != 0: - raise BadSectionError(f"ELF section {name} is not read-only data") + raise BadSectionError(f'ELF section {name} is not aligned') + if elf_s['sh_flags'] & (elf.constants.SH_FLAGS.SHF_EXECINSTR | elf.constants.SH_FLAGS.SHF_WRITE) != 0: # fmt: skip + raise BadSectionError(f'ELF section {name} is not read-only data') pe_s = PeSection() pe_s.Name = name.encode() @@ -381,18 +384,21 @@ def apply_elf_relative_relocation( sections: list[PeSection], addend_size: int, ) -> None: - [target] = [pe_s for pe_s in sections - if pe_s.VirtualAddress <= reloc["r_offset"] < pe_s.VirtualAddress + len(pe_s.data)] + [target] = [ + pe_s + for pe_s in sections + if pe_s.VirtualAddress <= reloc['r_offset'] < pe_s.VirtualAddress + len(pe_s.data) + ] - addend_offset = reloc["r_offset"] - target.VirtualAddress + addend_offset = reloc['r_offset'] - target.VirtualAddress if reloc.is_RELA(): - addend = reloc["r_addend"] + addend = reloc['r_addend'] else: addend = target.data[addend_offset : addend_offset + addend_size] - addend = int.from_bytes(addend, byteorder="little") + addend = int.from_bytes(addend, byteorder='little') - value = (image_base + addend).to_bytes(addend_size, byteorder="little") + value = (image_base + addend).to_bytes(addend_size, byteorder='little') target.data[addend_offset : addend_offset + addend_size] = value @@ -404,47 +410,44 @@ def convert_elf_reloc_table( pe_reloc_blocks: dict[int, PeRelocationBlock], ) -> None: NONE_RELOC = { - "EM_386": elf.enums.ENUM_RELOC_TYPE_i386["R_386_NONE"], - "EM_AARCH64": elf.enums.ENUM_RELOC_TYPE_AARCH64["R_AARCH64_NONE"], - "EM_ARM": elf.enums.ENUM_RELOC_TYPE_ARM["R_ARM_NONE"], - "EM_LOONGARCH": 0, - "EM_RISCV": 0, - "EM_X86_64": elf.enums.ENUM_RELOC_TYPE_x64["R_X86_64_NONE"], - }[file["e_machine"]] + 'EM_386': elf.enums.ENUM_RELOC_TYPE_i386['R_386_NONE'], + 'EM_AARCH64': elf.enums.ENUM_RELOC_TYPE_AARCH64['R_AARCH64_NONE'], + 'EM_ARM': elf.enums.ENUM_RELOC_TYPE_ARM['R_ARM_NONE'], + 'EM_LOONGARCH': 0, + 'EM_RISCV': 0, + 'EM_X86_64': elf.enums.ENUM_RELOC_TYPE_x64['R_X86_64_NONE'], + }[file['e_machine']] # fmt: skip RELATIVE_RELOC = { - "EM_386": elf.enums.ENUM_RELOC_TYPE_i386["R_386_RELATIVE"], - "EM_AARCH64": elf.enums.ENUM_RELOC_TYPE_AARCH64["R_AARCH64_RELATIVE"], - "EM_ARM": elf.enums.ENUM_RELOC_TYPE_ARM["R_ARM_RELATIVE"], - "EM_LOONGARCH": 3, - "EM_RISCV": 3, - "EM_X86_64": elf.enums.ENUM_RELOC_TYPE_x64["R_X86_64_RELATIVE"], - }[file["e_machine"]] + 'EM_386': elf.enums.ENUM_RELOC_TYPE_i386['R_386_RELATIVE'], + 'EM_AARCH64': elf.enums.ENUM_RELOC_TYPE_AARCH64['R_AARCH64_RELATIVE'], + 'EM_ARM': elf.enums.ENUM_RELOC_TYPE_ARM['R_ARM_RELATIVE'], + 'EM_LOONGARCH': 3, + 'EM_RISCV': 3, + 'EM_X86_64': elf.enums.ENUM_RELOC_TYPE_x64['R_X86_64_RELATIVE'], + }[file['e_machine']] # fmt: skip for reloc in elf_reloc_table.iter_relocations(): - if reloc["r_info_type"] == NONE_RELOC: + if reloc['r_info_type'] == NONE_RELOC: continue - if reloc["r_info_type"] == RELATIVE_RELOC: - apply_elf_relative_relocation(reloc, - elf_image_base, - sections, - file.elfclass // 8) + if reloc['r_info_type'] == RELATIVE_RELOC: + apply_elf_relative_relocation(reloc, elf_image_base, sections, file.elfclass // 8) # Now that the ELF relocation has been applied, we can create a PE relocation. - block_rva = reloc["r_offset"] & ~0xFFF + block_rva = reloc['r_offset'] & ~0xFFF if block_rva not in pe_reloc_blocks: pe_reloc_blocks[block_rva] = PeRelocationBlock(block_rva) entry = PeRelocationEntry() - entry.Offset = reloc["r_offset"] & 0xFFF + entry.Offset = reloc['r_offset'] & 0xFFF # REL_BASED_HIGHLOW or REL_BASED_DIR64 entry.Type = 3 if file.elfclass == 32 else 10 pe_reloc_blocks[block_rva].entries.append(entry) continue - raise BadSectionError(f"Unsupported relocation {reloc}") + raise BadSectionError(f'Unsupported relocation {reloc}') def convert_elf_relocations( @@ -453,27 +456,29 @@ def convert_elf_relocations( sections: list[PeSection], minimum_sections: int, ) -> typing.Optional[PeSection]: - dynamic = file.get_section_by_name(".dynamic") + dynamic = file.get_section_by_name('.dynamic') if dynamic is None: - raise BadSectionError("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"] & elf.enums.ENUM_DT_FLAGS_1["DF_1_PIE"]: - raise ValueError("ELF file is not a PIE") + [flags_tag] = dynamic.iter_tags('DT_FLAGS_1') + if not flags_tag['d_val'] & elf.enums.ENUM_DT_FLAGS_1['DF_1_PIE']: + raise ValueError('ELF file is not a PIE') # This checks that the ELF image base is 0. - symtab = file.get_section_by_name(".symtab") + symtab = file.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 ValueError("Unexpected ELF image base") - - opt.SizeOfHeaders = align_to(PE_OFFSET - + len(PE_MAGIC) - + sizeof(PeCoffHeader) - + sizeof(opt) - + sizeof(PeSection) * max(len(sections) + 1, minimum_sections), - FILE_ALIGNMENT) + exe_start = symtab.get_symbol_by_name('__executable_start') + if exe_start and exe_start[0]['st_value'] != 0: + raise ValueError('Unexpected ELF image base') + + opt.SizeOfHeaders = align_to( + PE_OFFSET + + len(PE_MAGIC) + + sizeof(PeCoffHeader) + + sizeof(opt) + + sizeof(PeSection) * max(len(sections) + 1, minimum_sections), + FILE_ALIGNMENT, + ) # We use the basic VMA layout from the ELF image in the PE image. This could cause the first # section to overlap the PE image headers during runtime at VMA 0. We can simply apply a fixed @@ -482,23 +487,18 @@ def convert_elf_relocations( # the ELF portions of the image. segment_offset = 0 if sections[0].VirtualAddress < opt.SizeOfHeaders: - segment_offset = align_to(opt.SizeOfHeaders - sections[0].VirtualAddress, - SECTION_ALIGNMENT) + segment_offset = align_to(opt.SizeOfHeaders - sections[0].VirtualAddress, SECTION_ALIGNMENT) - opt.AddressOfEntryPoint = file["e_entry"] + segment_offset + opt.AddressOfEntryPoint = file['e_entry'] + segment_offset opt.BaseOfCode += segment_offset if isinstance(opt, PeOptionalHeader32): opt.BaseOfData += segment_offset pe_reloc_blocks: dict[int, PeRelocationBlock] = {} for reloc_type, reloc_table in dynamic.get_relocation_tables().items(): - if reloc_type not in ["REL", "RELA"]: - raise BadSectionError(f"Unsupported relocation type {reloc_type}") - convert_elf_reloc_table(file, - reloc_table, - opt.ImageBase + segment_offset, - sections, - pe_reloc_blocks) + if reloc_type not in ['REL', 'RELA']: + raise BadSectionError(f'Unsupported relocation type {reloc_type}') + convert_elf_reloc_table(file, reloc_table, opt.ImageBase + segment_offset, sections, pe_reloc_blocks) for pe_s in sections: pe_s.VirtualAddress += segment_offset @@ -524,7 +524,7 @@ def convert_elf_relocations( data += entry pe_reloc_s = PeSection() - pe_reloc_s.Name = b".reloc" + pe_reloc_s.Name = b'.reloc' pe_reloc_s.data = data pe_reloc_s.VirtualAddress = next_section_address(sections) pe_reloc_s.VirtualSize = len(data) @@ -543,9 +543,9 @@ def write_pe( opt: PeOptionalHeader, sections: list[PeSection], ) -> None: - file.write(b"MZ") + file.write(b'MZ') file.seek(0x3C, io.SEEK_SET) - file.write(PE_OFFSET.to_bytes(2, byteorder="little")) + file.write(PE_OFFSET.to_bytes(2, byteorder='little')) file.seek(PE_OFFSET, io.SEEK_SET) file.write(PE_MAGIC) file.write(coff) @@ -554,8 +554,9 @@ def write_pe( offset = opt.SizeOfHeaders for pe_s in sorted(sections, key=lambda s: s.VirtualAddress): if pe_s.VirtualAddress < opt.SizeOfHeaders: - raise BadSectionError(f"Section {pe_s.Name} @0x{pe_s.VirtualAddress:x} overlaps" - " PE headers ending at 0x{opt.SizeOfHeaders:x}") + raise BadSectionError( + f'Section {pe_s.Name} @{pe_s.VirtualAddress:#x} overlaps PE headers ending at {opt.SizeOfHeaders:#x}' + ) pe_s.PointerToRawData = offset file.write(pe_s) @@ -573,20 +574,20 @@ def write_pe( def elf2efi(args: argparse.Namespace) -> None: file = elffile.ELFFile(args.ELF) if not file.little_endian: - raise ValueError("ELF file is not little-endian") - if file["e_type"] not in ["ET_DYN", "ET_EXEC"]: - raise ValueError(f"Unsupported ELF type {file['e_type']}") + raise ValueError('ELF file is not little-endian') + if file['e_type'] not in ['ET_DYN', 'ET_EXEC']: + raise ValueError(f'Unsupported ELF type {file["e_type"]}') pe_arch = { - "EM_386": 0x014C, - "EM_AARCH64": 0xAA64, - "EM_ARM": 0x01C2, - "EM_LOONGARCH": 0x6232 if file.elfclass == 32 else 0x6264, - "EM_RISCV": 0x5032 if file.elfclass == 32 else 0x5064, - "EM_X86_64": 0x8664, - }.get(file["e_machine"]) + 'EM_386': 0x014C, + 'EM_AARCH64': 0xAA64, + 'EM_ARM': 0x01C2, + 'EM_LOONGARCH': 0x6232 if file.elfclass == 32 else 0x6264, + 'EM_RISCV': 0x5032 if file.elfclass == 32 else 0x5064, + 'EM_X86_64': 0x8664, + }.get(file['e_machine']) # fmt: skip if pe_arch is None: - raise ValueError(f"Unsupported ELF architecture {file['e_machine']}") + raise ValueError(f'Unsupported ELF architecture {file["e_machine"]}') coff = PeCoffHeader() opt = PeOptionalHeader32() if file.elfclass == 32 else PeOptionalHeader32Plus() @@ -605,7 +606,7 @@ def elf2efi(args: argparse.Namespace) -> None: coff.Machine = pe_arch coff.NumberOfSections = len(sections) - coff.TimeDateStamp = int(os.environ.get("SOURCE_DATE_EPOCH") or time.time()) + coff.TimeDateStamp = int(os.environ.get('SOURCE_DATE_EPOCH') or time.time()) coff.SizeOfOptionalHeader = sizeof(opt) # EXECUTABLE_IMAGE|LINE_NUMS_STRIPPED|LOCAL_SYMS_STRIPPED|DEBUG_STRIPPED # and (32BIT_MACHINE or LARGE_ADDRESS_AWARE) @@ -632,66 +633,64 @@ def elf2efi(args: argparse.Namespace) -> None: opt.NumberOfRvaAndSizes = N_DATA_DIRECTORY_ENTRIES if pe_reloc_s: - opt.BaseRelocationTable = PeDataDirectory( - pe_reloc_s.VirtualAddress, pe_reloc_s.VirtualSize - ) + opt.BaseRelocationTable = PeDataDirectory(pe_reloc_s.VirtualAddress, pe_reloc_s.VirtualSize) write_pe(args.PE, coff, opt, sections) def create_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser(description="Convert ELF binaries to PE/EFI") + parser = argparse.ArgumentParser(description='Convert ELF binaries to PE/EFI') parser.add_argument( - "--version-major", + '--version-major', type=int, default=0, - help="Major image version of EFI image", + help='Major image version of EFI image', ) parser.add_argument( - "--version-minor", + '--version-minor', type=int, default=0, - help="Minor image version of EFI image", + help='Minor image version of EFI image', ) parser.add_argument( - "--efi-major", + '--efi-major', type=int, default=0, - help="Minimum major EFI subsystem version", + help='Minimum major EFI subsystem version', ) parser.add_argument( - "--efi-minor", + '--efi-minor', type=int, default=0, - help="Minimum minor EFI subsystem version", + help='Minimum minor EFI subsystem version', ) parser.add_argument( - "--subsystem", + '--subsystem', type=int, default=10, - help="PE subsystem", + help='PE subsystem', ) parser.add_argument( - "ELF", - type=argparse.FileType("rb"), - help="Input ELF file", + 'ELF', + type=argparse.FileType('rb'), + help='Input ELF file', ) parser.add_argument( - "PE", - type=argparse.FileType("wb"), - help="Output PE/EFI file", + 'PE', + type=argparse.FileType('wb'), + help='Output PE/EFI file', ) parser.add_argument( - "--minimum-sections", + '--minimum-sections', type=int, default=0, - help="Minimum number of sections to leave space for", + help='Minimum number of sections to leave space for', ) parser.add_argument( - "--copy-sections", + '--copy-sections', type=str, - default="", - help="Copy these sections if found", + default='', + help='Copy these sections if found', ) return parser @@ -701,5 +700,5 @@ def main() -> None: elf2efi(parser.parse_args()) -if __name__ == "__main__": +if __name__ == '__main__': main()