elf2efi: apply "ruff format" and "ruff check --fix"
This commit is contained in:
+235
-236
@@ -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")
|
||||
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)
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user