diff --git a/fibs.asm b/fibs.asm index 4080a40..0782ce3 100644 --- a/fibs.asm +++ b/fibs.asm @@ -4,6 +4,7 @@ fibs: .space 56 .text main: +func_fibs: addi s1, zero, 0 # storage index addi s2, zero, 56 # last storage index addi t0, zero, 1 # t0 = F_{i} @@ -19,4 +20,9 @@ loop: addi a0, zero, 0 addi a7, zero, 93 ebreak # launch debugger - scall # exit with code 0 \ No newline at end of file + scall # exit with code 0 + + +.set test_val, 0xFF + +.global func_fibs \ No newline at end of file diff --git a/riscemu/Exceptions.py b/riscemu/Exceptions.py index 22558d5..38da760 100644 --- a/riscemu/Exceptions.py +++ b/riscemu/Exceptions.py @@ -44,6 +44,14 @@ def ASSERT_IN(a1, a2): raise ParseException("ASSERTION_FAILED: Expected {} to not be in {}".format(a1, a2), (a1, a2)) +class LinkerException(RiscemuBaseException): + def __init__(self, msg, data): + self.msg = msg + self.data = data + def message(self): + return FMT_PARSE + "{}(\"{}\", data={})".format(self.__class__.__name__, self.msg, self.data) + FMT_NONE + + # MMU Exceptions class MemoryAccessException(RiscemuBaseException): @@ -64,7 +72,7 @@ class MemoryAccessException(RiscemuBaseException): ) + FMT_NONE -class OutOfMemoryEsception(RiscemuBaseException): +class OutOfMemoryException(RiscemuBaseException): def __init__(self, action): self.action = action diff --git a/riscemu/Executable.py b/riscemu/Executable.py index 7926b07..0402aea 100644 --- a/riscemu/Executable.py +++ b/riscemu/Executable.py @@ -66,15 +66,17 @@ class Executable: sections: Dict[str, MemorySection] symbols: Dict[str, Tuple[str, int]] stack_pref: Optional[int] + exported_symbols: List[str] name: str def __repr__(self): - return "{}(sections = {}, symbols = {}, stack = {}, run_ptr = {})".format( + return "{}(sections = {}, symbols = {}, stack = {}, run_ptr = {}, globals={})".format( self.__class__.__name__, " ".join(self.sections.keys()), " ".join(self.symbols.keys()), self.stack_pref, - self.run_ptr + self.run_ptr, + ",".join(self.exported_symbols) ) @@ -98,8 +100,8 @@ class LoadedInstruction: raise ParseException("Instruction {} expected argument at {} (args: {})".format(self.name, num, self.args)) arg = self.args[num] # look up symbols - if arg in self.bin.symbols: - return self.bin.symbols[arg] + if self.bin.has_symb(arg): + return self.bin.lookup_symbol(arg) return parse_numeric_argument(arg) def get_imm_reg(self, num: int): @@ -111,8 +113,8 @@ class LoadedInstruction: arg = self.args[num] ASSERT_IN("(", arg) imm, reg = arg[:-1].split("(") - if imm in self.bin.symbols: - return self.bin.symbols[imm], reg + if self.bin.has_symb(imm): + return self.bin.lookup_symbol(imm), reg return parse_numeric_argument(imm), reg def get_reg(self, num: int): @@ -244,13 +246,17 @@ class LoadedExecutable: symbols: Dict[str, int] run_ptr: int stack_heap: Tuple[int, int] # pointers to stack and heap, are nullptr if no stack/heap is available + exported_symbols: Dict[str, int] + global_symbol_table: Dict[str, int] - def __init__(self, exe: Executable, base_addr: int): + def __init__(self, exe: Executable, base_addr: int, global_symbol_table: Dict[str, int]): self.name = exe.name self.base_addr = base_addr self.sections = list() self.sections_by_name = dict() self.symbols = dict() + self.exported_symbols = dict() + self.global_symbol_table = dict() # stack/heap if wanted if exe.stack_pref is not None: @@ -281,8 +287,14 @@ class LoadedExecutable: curr = align_addr(loaded_sec.size + curr) for name, (sec_name, offset) in exe.symbols.items(): - ASSERT_IN(sec_name, self.sections_by_name) - self.symbols[name] = self.sections_by_name[sec_name].base + offset + if sec_name == '_static_': + self.symbols[name] = offset + else: + ASSERT_IN(sec_name, self.sections_by_name) + self.symbols[name] = self.sections_by_name[sec_name].base + offset + + for name in exe.exported_symbols: + self.exported_symbols[name] = self.symbols[name] self.size = curr - base_addr @@ -290,6 +302,14 @@ class LoadedExecutable: run_ptr_sec, run_ptr_off = exe.run_ptr self.run_ptr = self.sections_by_name[run_ptr_sec].base + run_ptr_off + def lookup_symbol(self, name): + if name in self.symbols: + return self.symbols[name] + if name in self.global_symbol_table: + return self.global_symbol_table[name] + raise LinkerException('Symbol {} not found!'.format(name), (self,)) + + def __repr__(self): return '{}[{}](base=0x{:08X}, size={}bytes, sections={}, run_ptr=0x{:08X})'.format( self.__class__.__name__, @@ -299,3 +319,6 @@ class LoadedExecutable: " ".join(self.sections_by_name.keys()), self.run_ptr ) + + def has_symb(self, arg): + return arg in self.symbols or arg in self.global_symbol_table diff --git a/riscemu/ExecutableParser.py b/riscemu/ExecutableParser.py index 36e0ba7..eda87bf 100644 --- a/riscemu/ExecutableParser.py +++ b/riscemu/ExecutableParser.py @@ -36,7 +36,7 @@ class ExecutableParser: start_ptr = self.symbols['_start'] elif 'main' in self.symbols: start_ptr = self.symbols['main'] - return Executable(start_ptr, self.sections, self.symbols, self.stack_pref, self.tokenizer.name) + return Executable(start_ptr, self.sections, self.symbols, self.stack_pref, self.globals, self.tokenizer.name) def parse_instruction(self, ins: 'RiscVInstructionToken'): if self.active_section is None: @@ -103,7 +103,7 @@ class ExecutableParser: def op_global(self, op: 'RiscVPseudoOpToken'): ASSERT_LEN(op.args, 1) - name = op.args[1] + name = op.args[0] self.globals.append(name) def op_set(self, op: 'RiscVPseudoOpToken'): diff --git a/riscemu/MMU.py b/riscemu/MMU.py index d96ca47..8209620 100644 --- a/riscemu/MMU.py +++ b/riscemu/MMU.py @@ -1,7 +1,7 @@ from .Config import RunConfig from .Executable import Executable, LoadedExecutable, LoadedMemorySection from .helpers import align_addr -from .Exceptions import OutOfMemoryEsception +from .Exceptions import OutOfMemoryException from typing import Dict, List, Tuple, Optional @@ -20,6 +20,7 @@ class MMU: self.binaries = list() self.last_bin = None self.conf = conf + self.global_symbols = dict() def load_bin(self, bin: Executable): if self.last_bin is None: @@ -33,10 +34,10 @@ class MMU: if bin.stack_pref is None: bin.stack_pref = self.conf.preffered_stack_size - loaded_bin = LoadedExecutable(bin, addr) + loaded_bin = LoadedExecutable(bin, addr, self.global_symbols) if loaded_bin.size + addr > self.max_size: - raise OutOfMemoryEsception('load of executable') + raise OutOfMemoryException('load of executable') self.binaries.append(loaded_bin) self.last_bin = loaded_bin @@ -45,6 +46,8 @@ class MMU: for sec in loaded_bin.sections: self.sections.append(sec) + self.global_symbols.update(loaded_bin.exported_symbols) + print("Successfully loaded {}".format(loaded_bin)) return loaded_bin diff --git a/riscemu/Tokenizer.py b/riscemu/Tokenizer.py index a95baaa..7893280 100644 --- a/riscemu/Tokenizer.py +++ b/riscemu/Tokenizer.py @@ -23,7 +23,7 @@ PSEUDO_OPS = [ '.data', '.half', '.text', - '.word' + '.word', '.set', ] diff --git a/riscemu/__init__.py b/riscemu/__init__.py index e31ff0c..bb4e9d5 100644 --- a/riscemu/__init__.py +++ b/riscemu/__init__.py @@ -1,4 +1,4 @@ -from .Exceptions import ASSERT_NOT_NULL, ASSERT_LEN, ASSERT_IN, ASSERT_EQ, ASSERT_NOT_IN +from .Exceptions import * from .Tokenizer import RiscVToken, RiscVInput, RiscVTokenizer, RiscVInstructionToken, RiscVSymbolToken, \ RiscVPseudoOpToken, TokenType diff --git a/riscemu/__main__.py b/riscemu/__main__.py index 3b2eec0..b2cba8c 100644 --- a/riscemu/__main__.py +++ b/riscemu/__main__.py @@ -2,6 +2,7 @@ if __name__ == '__main__': from . import * from .helpers import * import argparse + import sys parser = argparse.ArgumentParser(description='RISC-V Userspace parser and emulator', prog='riscemu') parser.add_argument('file', metavar='file.asm', type=str, help='The assembly file to interpret and run') @@ -33,15 +34,21 @@ if __name__ == '__main__': FMT_NONE = "" FMT_PRINT = "" - tk = RiscVTokenizer(RiscVInput.from_file(args.file)) - tk.tokenize() - - if args.print_tokens: - print(FMT_PRINT + "Tokens:" + FMT_NONE) - for token in tk.tokens: - print(token) - - executable = ExecutableParser(tk).parse() + try: + tk = RiscVTokenizer(RiscVInput.from_file(args.file)) + tk.tokenize() + + if args.print_tokens: + print(FMT_PRINT + "Tokens:" + FMT_NONE) + for token in tk.tokens: + print(token) + + executable = ExecutableParser(tk).parse() + except RiscemuBaseException as e: + print("Error while parsing: {}".format(e.message())) + import traceback + traceback.print_exception(type(e), e, e.__traceback__) + sys.exit(1) cpu = CPU(cfg) le = cpu.load(executable)