123 lines
3.9 KiB
Python
Executable File
123 lines
3.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
elf2opc - OpenCore Binary Converter
|
|
Converts standard 64-bit ELF executables into native .opc (OpenCore Binary) format.
|
|
"""
|
|
|
|
import sys
|
|
import struct
|
|
import os
|
|
|
|
OPC_MAGIC = 0x0143504F # 'O', 'P', 'C', '\x01'
|
|
OPC_VERSION = 1
|
|
OPC_ARCH_X86_64 = 0x8664
|
|
|
|
PT_LOAD = 1
|
|
|
|
def convert_elf_to_opc(elf_path, opc_path, preferred_core=0xFF, caps=0xFFFFFFFF):
|
|
with open(elf_path, "rb") as f:
|
|
elf_data = f.read()
|
|
|
|
if len(elf_data) < 64:
|
|
raise ValueError("ELF file too small")
|
|
|
|
if elf_data[0:4] != b"\x7fELF":
|
|
raise ValueError("Invalid ELF magic")
|
|
|
|
if elf_data[4] != 2: # 64-bit
|
|
raise ValueError("Not a 64-bit ELF")
|
|
|
|
# Parse ELF64 Header
|
|
# 0x10: e_type(2), e_machine(2), e_version(4), e_entry(8), e_phoff(8), e_shoff(8)
|
|
e_type, e_machine, e_version, e_entry, e_phoff, e_shoff = struct.unpack_from("<HHIQQQ", elf_data, 0x10)
|
|
e_flags, e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx = struct.unpack_from("<IHHHHHH", elf_data, 0x30)
|
|
|
|
if e_machine != 0x3E:
|
|
raise ValueError(f"Unsupported architecture machine {e_machine:#x} (expected x86_64: 0x3e)")
|
|
|
|
# Read Program Headers
|
|
load_segments = []
|
|
for i in range(e_phnum):
|
|
ph_offset = e_phoff + i * e_phentsize
|
|
p_type, p_flags, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_align = struct.unpack_from(
|
|
"<IIQQQQQQ", elf_data, ph_offset
|
|
)
|
|
|
|
if p_type == PT_LOAD and p_memsz > 0:
|
|
seg_bytes = elf_data[p_offset : p_offset + p_filesz]
|
|
load_segments.append({
|
|
"flags": p_flags,
|
|
"vaddr": p_vaddr,
|
|
"filesz": p_filesz,
|
|
"memsz": p_memsz,
|
|
"align": p_align,
|
|
"data": seg_bytes
|
|
})
|
|
|
|
if not load_segments:
|
|
raise ValueError("No PT_LOAD segments found in ELF")
|
|
|
|
header_fmt = "<IHHQQIBBH"
|
|
header_size = struct.calcsize(header_fmt)
|
|
|
|
seg_fmt = "<IIQQQQQ"
|
|
seg_header_size = struct.calcsize(seg_fmt)
|
|
|
|
total_headers_size = header_size + len(load_segments) * seg_header_size
|
|
|
|
# Calculate file offsets for segment bodies
|
|
current_data_offset = total_headers_size
|
|
for seg in load_segments:
|
|
# Align offset if needed
|
|
seg["out_offset"] = current_data_offset
|
|
current_data_offset += len(seg["data"])
|
|
|
|
# Build OPC Header
|
|
opc_header = struct.pack(
|
|
header_fmt,
|
|
OPC_MAGIC,
|
|
OPC_VERSION,
|
|
OPC_ARCH_X86_64,
|
|
e_entry,
|
|
64 * 1024, # 64 KiB stack
|
|
caps, # Capabilities mask
|
|
preferred_core, # Preferred core
|
|
len(load_segments), # num_segments
|
|
0 # reserved
|
|
)
|
|
|
|
# Build Segments Table
|
|
opc_segments = bytearray()
|
|
for seg in load_segments:
|
|
# Map ELF flags (PF_X=1, PF_W=2, PF_R=4) directly
|
|
opc_segments += struct.pack(
|
|
seg_fmt,
|
|
1, # seg_type = LOAD
|
|
seg["flags"], # flags
|
|
seg["out_offset"], # file_offset
|
|
seg["vaddr"], # vaddr
|
|
seg["filesz"], # filesz
|
|
seg["memsz"], # memsz
|
|
seg["align"] # align
|
|
)
|
|
|
|
# Write output .opc file
|
|
with open(opc_path, "wb") as out:
|
|
out.write(opc_header)
|
|
out.write(opc_segments)
|
|
for seg in load_segments:
|
|
out.write(seg["data"])
|
|
|
|
print(f"[elf2opc] Converted '{elf_path}' -> '{opc_path}'")
|
|
print(f" Entry RIP: {e_entry:#x}, Segments: {len(load_segments)}, Size: {current_data_offset} bytes")
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) < 3:
|
|
print("Usage: elf2opc <input.elf> <output.opc> [preferred_core] [caps_mask]")
|
|
sys.exit(1)
|
|
|
|
pref_core = int(sys.argv[3]) if len(sys.argv) > 3 else 0xFF
|
|
caps_val = int(sys.argv[4], 0) if len(sys.argv) > 4 else 0xFFFFFFFF
|
|
|
|
convert_elf_to_opc(sys.argv[1], sys.argv[2], pref_core, caps_val)
|