]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
elf2efi: apply "ruff format" and "ruff check --fix"
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 19 Feb 2026 17:08:46 +0000 (02:08 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 17 May 2026 17:44:47 +0000 (02:44 +0900)
tools/elf2efi.py

index ed705beb3169f81030addad4f95271c2492425eb..1442d1a723616f26d4a42366b0548b11198e064a 100755 (executable)
@@ -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()