zephyr/arch/x86/zefi/zefi.py
Jamie McCrae ec7044437e treewide: Disable automatic argparse argument shortening
Disables allowing the python argparse library from automatically
shortening command line arguments, this prevents issues whereby
a new command is added and code that wrongly uses the shortened
command of an existing argument which is the same as the new
command being added will silently change script behaviour.

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
2023-01-26 20:12:36 +09:00

161 lines
5 KiB
Python
Executable file

#!/usr/bin/env python3
# Copyright (c) 2020 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import os.path
import subprocess
import elftools.elf.elffile
import argparse
ENTRY_SYM = "__start64"
def verbose(msg):
if args.verbose:
print(msg)
def build_elf(elf_file, include_dirs):
base_dir = os.path.dirname(os.path.abspath(__file__))
cfile = os.path.join(base_dir, "zefi.c")
ldscript = os.path.join(base_dir, "efi.ld")
assert os.path.isfile(cfile)
assert os.path.isfile(ldscript)
#
# Open the ELF file up and find our entry point
#
fp = open(elf_file, "rb")
ef = elftools.elf.elffile.ELFFile(fp)
symtab = ef.get_section_by_name(".symtab")
entry_addr = symtab.get_symbol_by_name(ENTRY_SYM)[0].entry.st_value
verbose("Entry point address (symbol: %s) 0x%x" % (ENTRY_SYM, entry_addr))
#
# Parse the ELF file and extract segment data
#
data_blob = b''
data_segs = []
zero_segs = []
for seg in ef.iter_segments():
h = seg.header
if h.p_type != "PT_LOAD":
continue
assert h.p_memsz >= h.p_filesz
assert len(seg.data()) == h.p_filesz
if h.p_filesz > 0:
sd = seg.data()
verbose("%d bytes of data at 0x%x, data offset %d"
% (len(sd), h.p_vaddr, len(data_blob)))
data_segs.append((h.p_vaddr, len(sd), len(data_blob)))
data_blob = data_blob + sd
if h.p_memsz > h.p_filesz:
bytesz = h.p_memsz - h.p_filesz
addr = h.p_vaddr + h.p_filesz
verbose("%d bytes of zero-fill at 0x%x" % (bytesz, addr))
zero_segs.append((addr, bytesz))
verbose(f"{len(data_blob)} bytes of data to include in image")
#
# Emit a C header containing the metadata
#
cf = open("zefi-segments.h", "w")
cf.write("/* GENERATED CODE. DO NOT EDIT. */\n\n")
cf.write("/* Sizes and offsets specified in 4-byte units.\n")
cf.write(" * All addresses 4-byte aligned.\n")
cf.write(" */\n")
cf.write("struct data_seg { uint64_t addr; uint32_t sz; uint32_t off; };\n\n")
cf.write("static struct data_seg zefi_dsegs[] = {\n")
for s in data_segs:
cf.write(" { 0x%x, %d, %d },\n"
% (s[0], s[1], s[2]))
cf.write("};\n\n")
cf.write("struct zero_seg { uint64_t addr; uint32_t sz; };\n\n")
cf.write("static struct zero_seg zefi_zsegs[] = {\n")
for s in zero_segs:
cf.write(" { 0x%x, %d },\n"
% (s[0], s[1]))
cf.write("};\n\n")
cf.write("static uintptr_t zefi_entry = 0x%xUL;\n" % (entry_addr))
cf.close()
verbose("Metadata header generated.")
#
# Build
#
# First stage ELF binary. Flag notes:
# + Stack protector is default on some distros and needs library support
# + We need pic to enforce that the linker adds no relocations
# + UEFI can take interrupts on our stack, so no red zone
# + UEFI API assumes 16-bit wchar_t
includes = []
for include_dir in include_dirs:
includes.extend(["-I", include_dir])
cmd = ([args.compiler, "-shared", "-Wall", "-Werror", "-I."] + includes +
["-fno-stack-protector", "-fpic", "-mno-red-zone", "-fshort-wchar",
"-Wl,-nostdlib", "-T", ldscript, "-o", "zefi.elf", cfile])
verbose(" ".join(cmd))
subprocess.run(cmd, check = True)
# Extract the .data segment and append our extra blob
cmd = [args.objcopy, "-O", "binary", "-j", ".data", "zefi.elf", "data.dat"]
verbose(" ".join(cmd))
subprocess.run(cmd, check = True)
assert (os.stat("data.dat").st_size % 8) == 0
df = open("data.dat", "ab")
df.write(data_blob)
df.close()
# FIXME: this generates warnings about our unused trash section having to be moved to make room. Set its address far away...
subprocess.run([args.objcopy, "--update-section", ".data=data.dat",
"zefi.elf"], check = True)
# Convert it to a PE-COFF DLL.
cmd = [args.objcopy, "--target=efi-app-x86_64",
"-j", ".text", "-j", ".reloc", "-j", ".data",
"zefi.elf", "zephyr.efi"]
verbose(" ".join(cmd))
subprocess.run(cmd, check = True)
verbose("Build complete; zephyr.efi wrapper binary is ready")
def parse_args():
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False)
parser.add_argument("-c", "--compiler", required=True, help="Compiler to be used")
parser.add_argument("-o", "--objcopy", required=True, help="objcopy to be used")
parser.add_argument("-f", "--elf-file", required=True, help="Input file")
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
parser.add_argument("-i", "--includes", required=True, nargs="+",
help="Zephyr base include directories")
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
verbose(f"Working on {args.elf_file} with {args.includes}...")
build_elf(args.elf_file, args.includes)