util: bsp-tool: extract bootstrap exec constants and store them in the bsp image

This commit is contained in:
2026-02-19 19:23:28 +00:00
parent 3d28ed2cb8
commit 0e562f6a79
4 changed files with 254 additions and 6 deletions

View File

@@ -3,7 +3,9 @@ import sys
import os import os
import shutil import shutil
import tarfile import tarfile
import struct
from lib.manifest import Manifest, Component from lib.manifest import Manifest, Component
from lib.elf import ELF64Image
def reset(): def reset():
@@ -59,12 +61,13 @@ def add_binary():
def build_bsp(): def build_bsp():
if len(sys.argv) < 3: if len(sys.argv) < 4:
print("USAGE: {} build-bsp <manifest-path> <dest-path>".format(sys.argv[0])) print("USAGE: {} build-bsp <manifest-path> <bootstrap-program-path> <dest-path>".format(sys.argv[0]))
return -1 return -1
manifest_path = sys.argv[2] 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): if os.path.exists(bsp_path):
os.remove(bsp_path) os.remove(bsp_path)
@@ -82,9 +85,6 @@ def build_bsp():
header_dest = header_dest[1:] header_dest = header_dest[1:]
for f in os.listdir(header_src): 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( bsp_file.add(
os.path.join(header_src, f), os.path.join(header_src, f),
arcname=os.path.join(header_dest, f)) arcname=os.path.join(header_dest, f))
@@ -94,10 +94,54 @@ def build_bsp():
binary_dest = b['dest'] binary_dest = b['dest']
while binary_dest.startswith('/'): while binary_dest.startswith('/'):
binary_dest = binary_dest[1:] 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.add(binary_src, arcname=binary_dest)
bsp_file.close() 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 return 0

1
util/lib/__init__.py Normal file
View File

@@ -0,0 +1 @@
from . import *

102
util/lib/elf.py Normal file
View File

@@ -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

101
util/lib/manifest.py Normal file
View File

@@ -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