mirror of
https://github.com/AsahiLinux/m1n1
synced 2025-01-12 13:08:52 +00:00
f353017ac6
Let user specify a call method to use on symbols. Signed-off-by: Martin Povišer <povik@protonmail.com>
178 lines
6.3 KiB
Python
178 lines
6.3 KiB
Python
# SPDX-License-Identifier: MIT
|
|
from contextlib import contextmanager, ExitStack
|
|
import sys, pathlib, os
|
|
import subprocess
|
|
import tempfile
|
|
import bisect
|
|
|
|
from .asm import NM, LD, OBJCOPY, ARMAsm
|
|
|
|
__all__ = ["LinkedProgram"]
|
|
|
|
|
|
def tool_output_lines(progname, *args):
|
|
with subprocess.Popen([progname.replace("%ARCH", ARMAsm.ARCH)] + list(args),
|
|
stdout=subprocess.PIPE) as proc:
|
|
for line in proc.stdout:
|
|
yield line.decode("ascii")
|
|
proc.wait()
|
|
if proc.returncode:
|
|
raise Exception(f"{progname} (args: {args}) exited with status {proc.returncode}")
|
|
|
|
def run_tool(progname, *args, silent=False):
|
|
subprocess.check_call([progname.replace("%ARCH", ARMAsm.ARCH)] + list(args),
|
|
stdout=subprocess.DEVNULL if silent else None)
|
|
|
|
|
|
class LinkedProgram:
|
|
SOURCE_ROOT = str(pathlib.Path(__file__).resolve().parents[2])
|
|
|
|
def __init__(self, u, base_object=None):
|
|
self.u = u
|
|
self.symbols = []
|
|
self.symaddrs = dict()
|
|
self.base_object = base_object
|
|
self._alloced_bases = []
|
|
self._attrs_to_clear = []
|
|
self._load_base_symbols()
|
|
|
|
def _load_base_symbols(self):
|
|
if self.base_object is None:
|
|
suffix = "-raw" if not self.m1n1_is_macho() else ""
|
|
self.base_object = f"build/m1n1{suffix}.elf"
|
|
|
|
addrs = self._load_elf_symbols(self.base_object, self.u.proxy.get_base())
|
|
|
|
# sanity check: compare the .rela table between ELF and m1n1 image on target
|
|
rela_base = addrs["_rela_start"]
|
|
rela_length = addrs["_rela_end"] - rela_base
|
|
rela_target = self.u.iface.readmem(rela_base, rela_length)
|
|
|
|
tmp = os.path.join(tempfile.mkdtemp(), "bin")
|
|
path = os.path.join(self.SOURCE_ROOT, self.base_object)
|
|
run_tool(OBJCOPY, "-O", "binary", path, tmp, "--only-section=.rela.dyn")
|
|
rela_objfile = open(tmp, "rb").read()
|
|
|
|
if rela_objfile[:len(rela_target)] != rela_target:
|
|
raise Exception(f"Mismatch between {self.base_object} and image on target")
|
|
|
|
def m1n1_is_macho(self):
|
|
p = self.u.proxy
|
|
return p.read32(p.get_base()) == 0xfeedfacf
|
|
|
|
def _load_elf_symbols(self, relpath, offset=0,
|
|
objname=None, ignore=""):
|
|
path = pathlib.Path(self.SOURCE_ROOT, relpath)
|
|
symaddrs = dict()
|
|
|
|
for line in tool_output_lines(NM, "-g", path):
|
|
addr_str, t, name = line.split()
|
|
addr = int(addr_str, 16) + offset
|
|
if t in ignore:
|
|
continue
|
|
self.symbols.append((addr, name, objname))
|
|
symaddrs[name] = addr
|
|
if t in "T" and not hasattr(self, name):
|
|
setattr(self, name, self._wrap_call_to(addr))
|
|
if relpath != self.base_object:
|
|
self._attrs_to_clear.append(name)
|
|
self.symbols.sort()
|
|
return symaddrs
|
|
|
|
def load_obj(self, objfile, base=None):
|
|
ALLOC_SIZE = 16*4096
|
|
|
|
if base is None:
|
|
base = self.u.heap.memalign(0x4000, ALLOC_SIZE)
|
|
self._alloced_bases.append(base)
|
|
|
|
objfile = os.path.join(self.SOURCE_ROOT, objfile)
|
|
tmp = tempfile.mkdtemp() + os.sep
|
|
elffile = tmp + "elf"
|
|
ld_script = tmp + "ld"
|
|
binfile = tmp + "bin"
|
|
with open(ld_script, "w") as f:
|
|
f.write("SECTIONS {\n")
|
|
f.write(f". = 0x{base:x};\n")
|
|
f.write(".text : { *(.text .text.*) }\n")
|
|
f.write(".data : { *(.got .data .data.* .rodata .rodata.* .bss .bss.*) }\n")
|
|
f.write("}\n")
|
|
for sym in self.symbols:
|
|
f.write(f"{sym[1]} = 0x{sym[0]:x};\n")
|
|
run_tool(LD, "-EL", "-maarch64elf", "-T", ld_script, "-o", elffile, objfile)
|
|
run_tool(OBJCOPY, "-O", "binary", elffile, binfile)
|
|
#run_tool("objdump", "-d", elffile)
|
|
self._load_elf_symbols(elffile, ignore="A")
|
|
with open(binfile, "rb") as f:
|
|
buf = f.read()
|
|
assert len(buf) <= ALLOC_SIZE
|
|
self.u.iface.writemem(base, buf)
|
|
self.u.proxy.dc_cvau(base, len(buf))
|
|
self.u.proxy.ic_ivau(base, len(buf))
|
|
|
|
def clear_objs(self):
|
|
for name in self._attrs_to_clear:
|
|
delattr(self, name)
|
|
self._attrs_to_clear = []
|
|
|
|
for base in self._alloced_bases:
|
|
self.u.free(base)
|
|
self._alloced_bases = []
|
|
|
|
self.symbols = [(a, b, objname) for (a, b, objname) \
|
|
in self.symbols if objname == self.base_object]
|
|
|
|
@contextmanager
|
|
def _copy_args_to_target(self, args):
|
|
heap = self.u.heap
|
|
with ExitStack() as stack:
|
|
args_copied = []
|
|
for arg in args:
|
|
if type(arg) is str:
|
|
arg = arg.encode("ascii")
|
|
# fallthrough
|
|
if type(arg) is bytes:
|
|
p = stack.enter_context(heap.guarded_malloc(len(arg) + 1))
|
|
self.u.iface.writemem(p, arg + b"\0")
|
|
args_copied.append(p)
|
|
elif type(arg) is int:
|
|
args_copied.append(arg)
|
|
else:
|
|
raise NotImplementedError(type(arg))
|
|
yield args_copied
|
|
|
|
def _wrap_call_to(self, addr):
|
|
def call_symbol(*args, call=self.u.proxy.call):
|
|
with self._copy_args_to_target(args) as args_copied:
|
|
return call(addr, *args_copied)
|
|
return call_symbol
|
|
|
|
def lookup(self, addr):
|
|
idx = bisect.bisect_left(self.symbols, (addr + 1, "", "")) - 1
|
|
if idx < 0 or idx >= len(self.symbols):
|
|
return None, None
|
|
return self.symbols[idx]
|
|
|
|
def load_inline_c(self, source):
|
|
tmp = tempfile.mkdtemp()
|
|
cfile = tmp + ".c"
|
|
objfile = tmp + ".o"
|
|
with open(cfile, "w") as f:
|
|
f.write(source)
|
|
run_tool("make", "-C", self.SOURCE_ROOT, "invoke_cc",
|
|
f"OBJFILE={objfile}", f"CFILE={cfile}", silent=True)
|
|
self.load_obj(objfile)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
from m1n1.setup import *
|
|
lp = LinkedProgram(u)
|
|
lp.debug_printf("hello from the other side! (%d)\n", 42)
|
|
lp.load_inline_c('''
|
|
#include "utils.h"
|
|
int add(int a, int b) {
|
|
debug_printf("adding %d and %d\\n", a, b);
|
|
return a + b;
|
|
}
|
|
''')
|
|
print(f"1 + 2 = {lp.add(1, 2)}")
|