mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-12 04:38:44 +00:00
Originally the EFI boot code was written to assume that all sections in the ELF file were 8-byte aligned and sized (because I thought this was part of some platform spec somewhere). This turned out to be wrong in practice (at least for section sizes), so the requirement was reduced to 4 bytes. But now we have a section being generated somewhere that turns out to violate even that. There's no particular value in doing those copies in big chunks. There's at best a mild performance benefit, but if we really cared we'd be using a more complicated memcpy() implementation anyway. Replace the loop in the C code with a bytewise copy, change the size field in the generated header to store bytes, and remove the assertions (which were the failuers actually being seen in practice) in the script that were there to detect this misalignment. Fixes #29095 Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
156 lines
4.5 KiB
Python
Executable File
156 lines
4.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"
|
|
GCC = "gcc"
|
|
OBJCOPY = "objcopy"
|
|
|
|
def verbose(msg):
|
|
if args.verbose:
|
|
print(msg)
|
|
|
|
def build_elf(elf_file):
|
|
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
|
|
cmd = [GCC, "-shared", "-Wall", "-Werror", "-I.",
|
|
"-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 = [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([OBJCOPY, "--update-section", ".data=data.dat",
|
|
"zefi.elf"], check = True)
|
|
|
|
# Convert it to a PE-COFF DLL.
|
|
cmd = [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)
|
|
|
|
parser.add_argument("-f", "--elf-file", required=True, help="Input file")
|
|
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
|
|
|
|
return parser.parse_args()
|
|
|
|
if __name__ == "__main__":
|
|
|
|
args = parse_args()
|
|
verbose(f"Working on {args.elf_file}...")
|
|
build_elf(args.elf_file)
|