diff options
Diffstat (limited to 'tools/elf2efi.py')
-rwxr-xr-x | tools/elf2efi.py | 699 |
1 files changed, 699 insertions, 0 deletions
diff --git a/tools/elf2efi.py b/tools/elf2efi.py new file mode 100755 index 0000000..54f64fa --- /dev/null +++ b/tools/elf2efi.py @@ -0,0 +1,699 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +# Convert ELF static PIE to PE/EFI image. + +# To do so we simply copy desired ELF sections while preserving their memory layout to ensure that +# code still runs as expected. We then translate ELF relocations to PE relocations so that the EFI +# loader/firmware can properly load the binary to any address at runtime. +# +# To make this as painless as possible we only operate on static PIEs as they should only contain +# base relocations that are easy to handle as they have a one-to-one mapping to PE relocations. +# +# EDK2 does a similar process using their GenFw tool. The main difference is that they use the +# --emit-relocs linker flag, which emits a lot of different (static) ELF relocation types that have +# to be handled differently for each architecture and is overall more work than its worth. +# +# Note that on arches where binutils has PE support (x86/x86_64 mostly, aarch64 only recently) +# objcopy can be used to convert ELF to PE. But this will still not convert ELF relocations, making +# the resulting binary useless. gnu-efi relies on this method and contains a stub that performs the +# ELF dynamic relocations at runtime. + +# pylint: disable=attribute-defined-outside-init + +import argparse +import hashlib +import io +import os +import pathlib +import time +import typing +from ctypes import ( + c_char, + c_uint8, + c_uint16, + c_uint32, + c_uint64, + LittleEndianStructure, + sizeof, +) + +from elftools.elf.constants import SH_FLAGS +from elftools.elf.elffile import ELFFile +from elftools.elf.enums import ( + ENUM_DT_FLAGS_1, + ENUM_RELOC_TYPE_AARCH64, + ENUM_RELOC_TYPE_ARM, + ENUM_RELOC_TYPE_i386, + ENUM_RELOC_TYPE_x64, +) +from elftools.elf.relocation import ( + Relocation as ElfRelocation, + RelocationTable as ElfRelocationTable, +) + + +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), + ) + + +class PeDataDirectory(LittleEndianStructure): + _fields_ = ( + ("VirtualAddress", c_uint32), + ("Size", c_uint32), + ) + + +class PeRelocationBlock(LittleEndianStructure): + _fields_ = ( + ("PageRVA", c_uint32), + ("BlockSize", c_uint32), + ) + + def __init__(self, PageRVA: int): + super().__init__(PageRVA) + self.entries: typing.List[PeRelocationEntry] = [] + + +class PeRelocationEntry(LittleEndianStructure): + _fields_ = ( + ("Offset", c_uint16, 12), + ("Type", c_uint16, 4), + ) + + +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), + ) + + +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), + ) + + +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), + ) + + +class PeOptionalHeader(LittleEndianStructure): + pass + + +class PeOptionalHeader32(PeOptionalHeader): + _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), + ) + + +class PeOptionalHeader32Plus(PeOptionalHeader): + _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), + ) + + +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), + ) + + def __init__(self): + super().__init__() + self.data = bytearray() + + +N_DATA_DIRECTORY_ENTRIES = 16 + +assert sizeof(PeSection) == 40 +assert sizeof(PeCoffHeader) == 20 +assert sizeof(PeOptionalHeader32) == 224 +assert sizeof(PeOptionalHeader32Plus) == 240 + +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 + +IGNORE_SECTIONS = [ + ".eh_frame", + ".eh_frame_hdr", + ".ARM.exidx", +] + +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", +] + +# EFI mandates 4KiB memory pages. +SECTION_ALIGNMENT = 4096 +FILE_ALIGNMENT = 512 + +# Nobody cares about DOS headers, so put the PE header right after. +PE_OFFSET = 64 +PE_MAGIC = b"PE\0\0" + + +def align_to(x: int, align: int) -> int: + return (x + align - 1) & ~(align - 1) + + +def align_down(x: int, align: int) -> int: + return x & ~(align - 1) + + +def next_section_address(sections: typing.List[PeSection]) -> int: + return align_to( + sections[-1].VirtualAddress + sections[-1].VirtualSize, SECTION_ALIGNMENT + ) + + +def iter_copy_sections(elf: ELFFile) -> typing.Iterator[PeSection]: + pe_s = None + + # This is essentially the same as copying by ELF load segments, except that we assemble them + # manually, so that we can easily strip unwanted sections. We try to only discard things we know + # about so that there are no surprises. + + 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.") + elif elf_seg["p_type"] == "PT_GNU_RELRO": + relro = elf_seg + + for elf_s in elf.iter_sections(): + if ( + elf_s["sh_flags"] & SH_FLAGS.SHF_ALLOC == 0 + or elf_s["sh_type"] in IGNORE_SECTION_TYPES + or elf_s.name in IGNORE_SECTIONS + ): + continue + if elf_s["sh_type"] not in ["SHT_PROGBITS", "SHT_NOBITS"]: + raise RuntimeError(f"Unknown section {elf_s.name}.") + + if elf_s["sh_flags"] & SH_FLAGS.SHF_EXECINSTR: + rwx = PE_CHARACTERISTICS_RX + elif elf_s["sh_flags"] & SH_FLAGS.SHF_WRITE: + rwx = PE_CHARACTERISTICS_RW + else: + rwx = PE_CHARACTERISTICS_R + + # PE images are always relro. + if relro and relro.section_in_segment(elf_s): + rwx = PE_CHARACTERISTICS_R + + if pe_s and pe_s.Characteristics != rwx: + yield pe_s + pe_s = None + + if pe_s: + # Insert padding to properly align the section. + 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.Characteristics = rwx + pe_s.data = elf_s.data() + + if pe_s: + yield pe_s + + +def convert_sections(elf: ELFFile, opt: PeOptionalHeader) -> typing.List[PeSection]: + last_vma = 0 + sections = [] + + for pe_s in iter_copy_sections(elf): + # Truncate the VMA to the nearest page and insert appropriate padding. This should not + # cause any overlap as this is pretty much how ELF *segments* are loaded/mmapped anyways. + # The ELF sections inside should also be properly aligned as we reuse the ELF VMA layout + # for the PE image. + vma = pe_s.VirtualAddress + pe_s.VirtualAddress = align_down(vma, SECTION_ALIGNMENT) + pe_s.data = bytearray(vma - pe_s.VirtualAddress) + pe_s.data + + 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_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 + + 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): + opt.BaseOfData = pe_s.VirtualAddress + + sections.append(pe_s) + + return sections + + +def copy_sections( + elf: ELFFile, + opt: PeOptionalHeader, + input_names: str, + sections: typing.List[PeSection], +): + for name in input_names.split(","): + elf_s = elf.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 RuntimeError(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.") + + pe_s = PeSection() + pe_s.Name = name.encode() + pe_s.data = elf_s.data() + pe_s.VirtualAddress = next_section_address(sections) + pe_s.VirtualSize = len(elf_s.data()) + pe_s.SizeOfRawData = align_to(len(elf_s.data()), FILE_ALIGNMENT) + pe_s.Characteristics = PE_CHARACTERISTICS_R + opt.SizeOfInitializedData += pe_s.VirtualSize + sections.append(pe_s) + + +def apply_elf_relative_relocation( + reloc: ElfRelocation, + image_base: int, + sections: typing.List[PeSection], + addend_size: int, +): + # fmt: off + [target] = [ + pe_s for pe_s in sections + if pe_s.VirtualAddress <= reloc["r_offset"] < pe_s.VirtualAddress + len(pe_s.data) + ] + # fmt: on + + addend_offset = reloc["r_offset"] - target.VirtualAddress + + if reloc.is_RELA(): + addend = reloc["r_addend"] + else: + addend = target.data[addend_offset : addend_offset + addend_size] + addend = int.from_bytes(addend, byteorder="little") + + value = (image_base + addend).to_bytes(addend_size, byteorder="little") + target.data[addend_offset : addend_offset + addend_size] = value + + +def convert_elf_reloc_table( + elf: ELFFile, + elf_reloc_table: ElfRelocationTable, + elf_image_base: int, + sections: typing.List[PeSection], + pe_reloc_blocks: typing.Dict[int, PeRelocationBlock], +): + NONE_RELOC = { + "EM_386": ENUM_RELOC_TYPE_i386["R_386_NONE"], + "EM_AARCH64": ENUM_RELOC_TYPE_AARCH64["R_AARCH64_NONE"], + "EM_ARM": ENUM_RELOC_TYPE_ARM["R_ARM_NONE"], + "EM_LOONGARCH": 0, + "EM_RISCV": 0, + "EM_X86_64": ENUM_RELOC_TYPE_x64["R_X86_64_NONE"], + }[elf["e_machine"]] + + RELATIVE_RELOC = { + "EM_386": ENUM_RELOC_TYPE_i386["R_386_RELATIVE"], + "EM_AARCH64": ENUM_RELOC_TYPE_AARCH64["R_AARCH64_RELATIVE"], + "EM_ARM": ENUM_RELOC_TYPE_ARM["R_ARM_RELATIVE"], + "EM_LOONGARCH": 3, + "EM_RISCV": 3, + "EM_X86_64": ENUM_RELOC_TYPE_x64["R_X86_64_RELATIVE"], + }[elf["e_machine"]] + + for reloc in elf_reloc_table.iter_relocations(): + if reloc["r_info_type"] == NONE_RELOC: + continue + + if reloc["r_info_type"] == RELATIVE_RELOC: + apply_elf_relative_relocation( + reloc, elf_image_base, sections, elf.elfclass // 8 + ) + + # Now that the ELF relocation has been applied, we can create a PE relocation. + 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 + # REL_BASED_HIGHLOW or REL_BASED_DIR64 + entry.Type = 3 if elf.elfclass == 32 else 10 + pe_reloc_blocks[block_rva].entries.append(entry) + + continue + + raise RuntimeError(f"Unsupported relocation {reloc}") + + +def convert_elf_relocations( + elf: ELFFile, + opt: PeOptionalHeader, + sections: typing.List[PeSection], + minimum_sections: int, +) -> typing.Optional[PeSection]: + dynamic = elf.get_section_by_name(".dynamic") + if dynamic is None: + raise RuntimeError("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.") + + # 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.") + + 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 + # offset relative to the PE image base when applying/converting ELF relocations. Afterwards we + # just have to apply the offset to the PE addresses so that the PE relocations work correctly on + # 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 + ) + + opt.AddressOfEntryPoint = elf["e_entry"] + segment_offset + opt.BaseOfCode += segment_offset + if isinstance(opt, PeOptionalHeader32): + opt.BaseOfData += segment_offset + + 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}.") + convert_elf_reloc_table( + elf, reloc_table, opt.ImageBase + segment_offset, sections, pe_reloc_blocks + ) + + for pe_s in sections: + pe_s.VirtualAddress += segment_offset + + if len(pe_reloc_blocks) == 0: + return None + + data = bytearray() + for rva in sorted(pe_reloc_blocks): + block = pe_reloc_blocks[rva] + n_relocs = len(block.entries) + + # Each block must start on a 32-bit boundary. Because each entry is 16 bits + # the len has to be even. We pad by adding a none relocation. + if n_relocs % 2 != 0: + n_relocs += 1 + block.entries.append(PeRelocationEntry()) + + block.PageRVA += segment_offset + block.BlockSize = ( + sizeof(PeRelocationBlock) + sizeof(PeRelocationEntry) * n_relocs + ) + data += block + for entry in sorted(block.entries, key=lambda e: e.Offset): + data += entry + + pe_reloc_s = PeSection() + 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) + pe_reloc_s.SizeOfRawData = align_to(len(data), FILE_ALIGNMENT) + # CNT_INITIALIZED_DATA|MEM_READ|MEM_DISCARDABLE + pe_reloc_s.Characteristics = 0x42000040 + + sections.append(pe_reloc_s) + opt.SizeOfInitializedData += pe_reloc_s.VirtualSize + return pe_reloc_s + + +def write_pe( + file, coff: PeCoffHeader, opt: PeOptionalHeader, sections: typing.List[PeSection] +): + file.write(b"MZ") + file.seek(0x3C, io.SEEK_SET) + file.write(PE_OFFSET.to_bytes(2, byteorder="little")) + file.seek(PE_OFFSET, io.SEEK_SET) + file.write(PE_MAGIC) + file.write(coff) + file.write(opt) + + offset = opt.SizeOfHeaders + for pe_s in sorted(sections, key=lambda s: s.VirtualAddress): + if pe_s.VirtualAddress < opt.SizeOfHeaders: + # Linker script should make sure this does not happen. + raise RuntimeError(f"Section {pe_s.Name} overlapping PE headers.") + + pe_s.PointerToRawData = offset + file.write(pe_s) + offset = align_to(offset + len(pe_s.data), FILE_ALIGNMENT) + + assert file.tell() <= opt.SizeOfHeaders + + for pe_s in sections: + file.seek(pe_s.PointerToRawData, io.SEEK_SET) + file.write(pe_s.data) + + file.truncate(offset) + + +def elf2efi(args: argparse.Namespace): + elf = ELFFile(args.ELF) + if not elf.little_endian: + raise RuntimeError("ELF file is not little-endian.") + if elf["e_type"] not in ["ET_DYN", "ET_EXEC"]: + raise RuntimeError("Unsupported ELF type.") + + pe_arch = { + "EM_386": 0x014C, + "EM_AARCH64": 0xAA64, + "EM_ARM": 0x01C2, + "EM_LOONGARCH": 0x6232 if elf.elfclass == 32 else 0x6264, + "EM_RISCV": 0x5032 if elf.elfclass == 32 else 0x5064, + "EM_X86_64": 0x8664, + }.get(elf["e_machine"]) + if pe_arch is None: + raise RuntimeError(f"Unsupported ELF arch {elf['e_machine']}") + + coff = PeCoffHeader() + opt = PeOptionalHeader32() if elf.elfclass == 32 else PeOptionalHeader32Plus() + + # We relocate to a unique image base to reduce the chances for runtime relocation to occur. + base_name = pathlib.Path(args.PE.name).name.encode() + opt.ImageBase = int(hashlib.sha1(base_name).hexdigest()[0:8], 16) + if elf.elfclass == 32: + opt.ImageBase = (0x400000 + opt.ImageBase) & 0xFFFF0000 + else: + opt.ImageBase = (0x100000000 + opt.ImageBase) & 0x1FFFF0000 + + sections = convert_sections(elf, opt) + copy_sections(elf, opt, args.copy_sections, sections) + pe_reloc_s = convert_elf_relocations(elf, opt, sections, args.minimum_sections) + + coff.Machine = pe_arch + coff.NumberOfSections = len(sections) + coff.TimeDateStamp = int(os.environ.get("SOURCE_DATE_EPOCH", time.time())) + coff.SizeOfOptionalHeader = sizeof(opt) + # EXECUTABLE_IMAGE|LINE_NUMS_STRIPPED|LOCAL_SYMS_STRIPPED|DEBUG_STRIPPED + # and (32BIT_MACHINE or LARGE_ADDRESS_AWARE) + coff.Characteristics = 0x30E if elf.elfclass == 32 else 0x22E + + opt.SectionAlignment = SECTION_ALIGNMENT + opt.FileAlignment = FILE_ALIGNMENT + opt.MajorImageVersion = args.version_major + opt.MinorImageVersion = args.version_minor + opt.MajorSubsystemVersion = args.efi_major + opt.MinorSubsystemVersion = args.efi_minor + opt.Subsystem = args.subsystem + opt.Magic = 0x10B if elf.elfclass == 32 else 0x20B + opt.SizeOfImage = next_section_address(sections) + + # DYNAMIC_BASE|NX_COMPAT|HIGH_ENTROPY_VA or DYNAMIC_BASE|NX_COMPAT + opt.DllCharacteristics = 0x160 if elf.elfclass == 64 else 0x140 + + # These values are taken from a natively built PE binary (although, unused by EDK2/EFI). + opt.SizeOfStackReserve = 0x100000 + opt.SizeOfStackCommit = 0x001000 + opt.SizeOfHeapReserve = 0x100000 + opt.SizeOfHeapCommit = 0x001000 + + opt.NumberOfRvaAndSizes = N_DATA_DIRECTORY_ENTRIES + if pe_reloc_s: + opt.BaseRelocationTable = PeDataDirectory( + pe_reloc_s.VirtualAddress, pe_reloc_s.VirtualSize + ) + + write_pe(args.PE, coff, opt, sections) + + +def main(): + parser = argparse.ArgumentParser(description="Convert ELF binaries to PE/EFI") + parser.add_argument( + "--version-major", + type=int, + default=0, + help="Major image version of EFI image", + ) + parser.add_argument( + "--version-minor", + type=int, + default=0, + help="Minor image version of EFI image", + ) + parser.add_argument( + "--efi-major", + type=int, + default=0, + help="Minimum major EFI subsystem version", + ) + parser.add_argument( + "--efi-minor", + type=int, + default=0, + help="Minimum minor EFI subsystem version", + ) + parser.add_argument( + "--subsystem", + type=int, + default=10, + help="PE subsystem", + ) + parser.add_argument( + "ELF", + type=argparse.FileType("rb"), + help="Input ELF file", + ) + parser.add_argument( + "PE", + type=argparse.FileType("wb"), + help="Output PE/EFI file", + ) + parser.add_argument( + "--minimum-sections", + type=int, + default=0, + help="Minimum number of sections to leave space for", + ) + parser.add_argument( + "--copy-sections", + type=str, + default="", + help="Copy these sections if found", + ) + + elf2efi(parser.parse_args()) + + +if __name__ == "__main__": + main() |