From 0e562f6a793b3e305c1776845d538bd2ca75ae92 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Thu, 19 Feb 2026 19:23:28 +0000 Subject: [PATCH] util: bsp-tool: extract bootstrap exec constants and store them in the bsp image --- util/bsp-tool.py | 56 +++++++++++++++++++++--- util/lib/__init__.py | 1 + util/lib/elf.py | 102 +++++++++++++++++++++++++++++++++++++++++++ util/lib/manifest.py | 101 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 254 insertions(+), 6 deletions(-) create mode 100644 util/lib/__init__.py create mode 100644 util/lib/elf.py create mode 100644 util/lib/manifest.py diff --git a/util/bsp-tool.py b/util/bsp-tool.py index 0a2e51e..3372111 100755 --- a/util/bsp-tool.py +++ b/util/bsp-tool.py @@ -3,7 +3,9 @@ import sys import os import shutil import tarfile +import struct from lib.manifest import Manifest, Component +from lib.elf import ELF64Image def reset(): @@ -59,12 +61,13 @@ def add_binary(): def build_bsp(): - if len(sys.argv) < 3: - print("USAGE: {} build-bsp ".format(sys.argv[0])) + if len(sys.argv) < 4: + print("USAGE: {} build-bsp ".format(sys.argv[0])) return -1 manifest_path = sys.argv[2] - bsp_path = sys.argv[3] + bootstrap_path = sys.argv[3] + bsp_path = sys.argv[4] if os.path.exists(bsp_path): os.remove(bsp_path) @@ -82,9 +85,6 @@ def build_bsp(): header_dest = header_dest[1:] for f in os.listdir(header_src): - print('ADD {} -> {}'.format( - os.path.join(header_src, f), - os.path.join(header_dest, f))) bsp_file.add( os.path.join(header_src, f), arcname=os.path.join(header_dest, f)) @@ -94,10 +94,54 @@ def build_bsp(): binary_dest = b['dest'] while binary_dest.startswith('/'): binary_dest = binary_dest[1:] + binary_dest = os.path.join(binary_dest, os.path.basename(binary_src)) bsp_file.add(binary_src, arcname=binary_dest) bsp_file.close() + bootstrap_offset = os.path.getsize(bsp_path) + + bsp = open(bsp_path, mode='ab') + prog = open(bootstrap_path, mode='rb') + + padding = bootstrap_offset % 0x1000 + bsp.write(b'\0' * padding) + bootstrap_offset += padding + prog_len = 0 + + while True: + data = prog.read(1024) + bsp.write(data) + prog_len += len(data) + + if len(data) < 1024: + break + + prog.close + + prog_elf = ELF64Image() + if prog_elf.load(bootstrap_path) != 0: + print('failed to parse bootstrap program') + + for ph in prog_elf.ph_list: + print('{}: {}/{} ({}/{} bytes) {}'.format( + ph.type, + ph.offset, ph.vaddr, + ph.filesz, ph.memsz, ph.flags)) + + trailer = struct.pack('>IQIQIQQQQQQQ', + 0xcafebabe, + 0, bootstrap_offset, + bootstrap_offset, prog_len, + prog_elf.text.offset, prog_elf.text.vaddr, prog_elf.text.memsz, + prog_elf.data.offset, prog_elf.data.vaddr, prog_elf.data.memsz, + prog_elf.entry) + + bsp.write(trailer) + + bsp.close() + prog.close() + return 0 diff --git a/util/lib/__init__.py b/util/lib/__init__.py new file mode 100644 index 0000000..b6e690f --- /dev/null +++ b/util/lib/__init__.py @@ -0,0 +1 @@ +from . import * diff --git a/util/lib/elf.py b/util/lib/elf.py new file mode 100644 index 0000000..f5011c4 --- /dev/null +++ b/util/lib/elf.py @@ -0,0 +1,102 @@ +from enum import Enum, Flag +import struct + +class ELFPType(Enum): + NULL = 0 + LOAD = 1 + DYNAMIC = 2 + INTERP = 3 + NOTE = 4 + SHLIB = 5 + PHDR = 6 + TLS = 7 + + +class ELFPFlag(Flag): + NONE = 0 + EXEC = 1 + WRITE = 2 + READ = 4 + + +class ELFEndian(Enum): + LITTLE = 1 + BIG = 2 + + +class ELF64Phdr: + def __init__(self): + self.type = ELFPType.NULL + self.flags = 0 + self.offset = 0 + self.vaddr = 0 + self.paddr = 0 + self.filesz = 0 + self.memsz = 0 + self.align = 0 + + return + + def parse(self, buf, endian): + endian_marker = '>' if endian == ELFEndian.BIG else '<' + entry = struct.unpack('{}IIQQQQQQ'.format(endian_marker), buf) + + self.type = ELFPType(entry[0]) + self.flags = ELFPFlag.NONE + self.offset = entry[2] + self.vaddr = entry[3] + self.paddr = entry[4] + self.filesz = entry[5] + self.memsz = entry[6] + self.align = entry[7] + + if entry[1] & ELFPFlag.READ.value: self.flags |= ELFPFlag.READ + if entry[1] & ELFPFlag.WRITE.value: self.flags |= ELFPFlag.WRITE + if entry[1] & ELFPFlag.EXEC.value: self.flags |= ELFPFlag.EXEC + + +class ELF64Image: + def __init__(self): + self.ph_list = [] + self.text = None + self.data = None + self.entry = None + + def load(self, path): + f = open(path, mode='rb') + ident_bytes = f.read(16) + + ident = struct.unpack('BBBBBBBBBBBBBBBB', ident_bytes) + + if ident[0] != 0x7F or ident[1] != ord('E') or ident[2] != ord('L') or ident[3] != ord('F'): + f.close() + return -1 + + endian = ELFEndian(ident[5]) + endian_marker = '>' if endian == ELFEndian.BIG else '<' + + hdr_bytes = f.read(48) + hdr = struct.unpack('{}HHIQQQIHHHHHH'.format(endian_marker), hdr_bytes) + + self.entry = hdr[3] + phoff = hdr[4] + phnum = hdr[9] + phentsize = hdr[8] + + for i in range(0, phnum): + f.seek(phoff + (i * phentsize)) + phdr_bytes = f.read(phentsize) + + phdr = ELF64Phdr() + phdr.parse(phdr_bytes, endian) + + if phdr.flags == ELFPFlag.READ | ELFPFlag.EXEC: + self.text = phdr + if phdr.flags == ELFPFlag.READ | ELFPFlag.WRITE: + self.data = phdr + + self.ph_list.append(phdr) + + f.close() + return 0 + diff --git a/util/lib/manifest.py b/util/lib/manifest.py new file mode 100644 index 0000000..6881070 --- /dev/null +++ b/util/lib/manifest.py @@ -0,0 +1,101 @@ +import json + + +class Component: + def __init__(self): + self.headers = [] + self.binaries = [] + return + + def add_headers(self, src, dest): + for h in self.headers: + if h['src'] == src and h['dest'] == dest: + return + + headers = {} + headers['src'] = src + headers['dest'] = dest + self.headers.append(headers) + + + def add_binary(self, src, dest): + for b in self.binaries: + if b['src'] == src and b['dest'] == dest: + return + + binary = {} + binary['src'] = src + binary['dest'] = dest + self.binaries.append(binary) + + + def serialise(self): + data = {} + if len(self.headers) > 0: + data['headers'] = self.headers + + if len(self.binaries) > 0: + data['binaries'] = self.binaries + + return data + + + def deserialise(self, data): + if 'headers' in data: + self.headers = data['headers'] + if 'binaries' in data: + self.binaries = data['binaries'] + + + def get_headers(self): + return self.headers + + + def get_binaries(self): + return self.binaries + + +class Manifest: + def __init__(self, path): + self.path = path + self.components = {} + + def load(self): + with open(self.path, 'r') as f: + self.data = json.load(f) + + if 'components' not in self.data: + return 0 + + for n, t in self.data['components'].items(): + component = Component() + component.deserialise(t) + self.components[n] = component + + + def save(self): + component_data = {} + for n, t in self.components.items(): + d = t.serialise() + component_data[n] = d + + self.data['components'] = component_data + + with open(self.path, 'w') as f: + json.dump(self.data, f, indent=4) + + + def reset(self): + self.data = {} + + + def get_component(self, name): + if name in self.components: + return self.components[name] + + component = Component() + self.components[name] = component + return component + + def get_all_components(self): + return self.components