Skip to content

Commit

Permalink
Feat: Added support for labels and errors during preprocessing
Browse files Browse the repository at this point in the history
Co-authored-by: Joshua Mitchener <[email protected]>
  • Loading branch information
joshuamitchener and joshuamitchener authored Jul 12, 2020
1 parent a35cae6 commit 9fc564d
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 20 deletions.
10 changes: 7 additions & 3 deletions dashmips/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ def main_run(args: argparse.Namespace) -> int:

def main_debug(args: argparse.Namespace) -> int:
"""Start debug server for mips."""
from .debuggerserver import debug_mips
program = preprocess(args.FILE, args=args.mips_args)
from .debuggerserver import debug_mips, connectPreprocessFailure
try:
program = preprocess(args.FILE, args=args.mips_args)
except MipsException as err:
connectPreprocessFailure(host = args.host, port = args.port)
raise MipsException(err.message)
debug_mips(program, args.host, args.port, should_log=args.log)
return 0

Expand Down Expand Up @@ -147,7 +151,7 @@ def main() -> NoReturn:
except MipsException as ex:
# All known errors should be caught and re-raised as MipsException
# If a user encounters a traceback that should be a fatal issue
print("dashmips encountered an error!", file=sys.stderr)
print("\nDashmips encountered an error while assembling:", file=sys.stderr)
print(ex, file=sys.stderr)
except KeyboardInterrupt as ex:
# Program should be closed
Expand Down
19 changes: 19 additions & 0 deletions dashmips/debuggerserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import logging as log
import signal
import socketserver
from time import sleep

from .models import MipsProgram

Expand Down Expand Up @@ -96,3 +97,21 @@ def handle(self):
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.handle_request()


def connectPreprocessFailure(host: str = "localhost", port: int = 2390):
"""Connect to extension and fail due to preprocessing failure."""
class TCPHandler(socketserver.BaseRequestHandler):

def handle(self):
sleep(0.1)
return

# Allows server to reuse address to prevent crash
socketserver.TCPServer.allow_reuse_address = True
socketserver.TCPServer.timeout = 0.1

with socketserver.TCPServer((host, port), TCPHandler) as server:
# Activate the server; this will keep running until you

server.handle_request()
7 changes: 4 additions & 3 deletions dashmips/hardware.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""Mips Hardware."""
from typing import Dict, Union, Tuple, Any, NoReturn, TypedDict, Literal, cast

from .utils import as_twos_comp, intify
import sys
from .utils import as_twos_comp, intify, MipsException

register_names = (
# fmt: off
Expand Down Expand Up @@ -46,9 +47,9 @@ def __setitem__(self, key: str, value: int):
if key in Registers.SPECIAL:
return super().__setitem__(key, value)
if key not in Registers.resolve:
raise Exception(f'Unknown register Reg[{key}]={hex(value)}')
raise MipsException(f'Unknown register Reg[{key}]={hex(value)}')
if value > 0xFFFF_FFFF:
raise Exception(f'Registers are only 32-bits Reg[{key}]={hex(value)}')
print(f'Warning: Overflowed 32-bit Reg[{key}]={hex(value)}', file=sys.stderr)
super().__setitem__(Registers.resolve[key], value & 0xFFFF_FFFF)

def __getitem__(self, key: str) -> int:
Expand Down
6 changes: 5 additions & 1 deletion dashmips/instructions/Instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from ..mips import RE
from ..models import MipsProgram
from ..utils import MipsException


class Instruction:
Expand Down Expand Up @@ -32,7 +33,10 @@ def __init__(self, fn, regex_pattern: str, parser, label: bool = False):

def __call__(self, program: MipsProgram, args: Iterable[Any] = tuple()):
"""Callable Instruction."""
self.function(program, *args)
try:
self.function(program, *args)
except KeyError as err:
raise MipsException(f'Symbol {err} not found in symbol table')
program.registers["pc"] += 1

def __repr__(self) -> str:
Expand Down
68 changes: 56 additions & 12 deletions dashmips/preprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,62 @@ def data_labels(labels: Dict[str, Label], data_sec: List[str], memory: Memory):
Fill the .data section memory with user defined static data
"""
data_line_re = f"(?:({mipsRE.LABEL}):)?\\s*({mipsRE.DIRECTIVE})\\s+(.*)"
second_pass = []

for line in data_sec:
match = re.match(data_line_re, line)
if match:
label_name = match[1]
directive_name = match[2][1:]
raw_data = match[3]
directive = Directives[directive_name]
address = directive(raw_data, memory)

try:
address = directive(raw_data, memory)
except NameError as err:
second_pass.append((line, err.args[0][6:-16]))
continue

if label_name:
if label_name in labels:
raise MipsException(f"Label {label_name} already defined")
# Not all directives need labels
labels[label_name] = Label(name=label_name, value=address, location=mipsRE.DATA_SEC, kind=match[2][1:])
else:
raise MipsException(f"Unknown directive {line}")

for pair in second_pass:
match = re.match(data_line_re, pair[0])
if match:
label_name = match[1]
directive_name = match[2][1:]
if pair[1] in labels:
raw_data = match[3].replace(pair[1], str(labels[pair[1]].value))
else:
label_position = [i for i in range(0, len(second_pass)) if second_pass[i][0][0:len(pair[1])] == pair[1]]
if len(label_position) == 0:
raise MipsException(f'Symbol {pair[1]} not found in symbol table')
elif len(label_position) > 1000:
raise MipsException(f'Cannot make labels refer to each other')
else:
second_pass.insert(label_position[-1] + 1, pair)
continue

directive = Directives[directive_name]
try:
address = directive(raw_data, memory)
except NameError as err:
second_pass.append((pair[0].replace(match[3], raw_data), err.args[0][6:-16]))
continue

if label_name:
if label_name in labels:
raise MipsException(f"Label {label_name} already defined")
# Not all directives need labels
labels[label_name] = Label(name=label_name, value=address, location=mipsRE.DATA_SEC, kind=match[2][1:])
else:
raise MipsException(f"Unknown directive {pair[0]}")

address = Directives["space"]('4', memory) # Pad the end of data section


Expand Down Expand Up @@ -136,8 +178,7 @@ def code_labels(labels: Dict[str, Label], text_sec: List[SourceLine]) -> List[So
else:
instruction = srcline.line.split(" ")[0]
if instruction not in Instructions:
print(f"Error line {idx}: `{instruction}` invalid")
exit(1)
raise MipsException(f"Error line {idx}: `{instruction}` invalid")
# Otherwise save the line as is
text.append(srcline)

Expand Down Expand Up @@ -186,15 +227,18 @@ def preprocessor_directives(lines: List[SourceLine]) -> Tuple[List[str], Dict[st
def resolve_include(lines: List[SourceLine], includes: List[str]) -> List[SourceLine]:
"""Resolve all includes recursively."""
for idx, srcline in enumerate(lines):
match = re.match(r'\s*\.include\s+"(.*)"\s*', srcline.line)
if match:
includefilename = os.path.abspath(match[1])
if includefilename in includes:
raise MipsException("Recursive importing detected")
includes.append(includefilename)
includefile = open(includefilename)
includelines = resolve_include(process_file(includefile), includes)
lines[idx] = includelines # type: ignore
try:
match = re.match(r'\s*\.include\s+"(.*)"\s*', srcline.line)
if match:
includefilename = os.path.abspath(match[1])
if includefilename in includes:
raise MipsException("Recursive importing detected")
includes.append(includefilename)
includefile = open(includefilename)
includelines = resolve_include(process_file(includefile), includes)
lines[idx] = includelines # type: ignore
except FileNotFoundError:
raise MipsException(f"Error reading include {includefilename}")

lines = flatten(lines)

Expand Down
24 changes: 23 additions & 1 deletion dashmips/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,27 @@ def __init__(self, message: str):

def parse_int(int_str: str) -> int:
"""Take a python number literal and returns an int."""
if '\"' in int_str:
raise MipsException('"%s" is not a valid integer constant or label.' % int_str[int_str.index('\"'): int_str.replace('\"', 'x', 1).find('\"') + 1])

# WARNING: SECURITY FLAW
arg: Union[int, str] = eval(int_str)

if isinstance(arg, str):
arg = int(ord(arg))
else:
elif isinstance(arg, int):
arg = int(arg)
elif isinstance(arg, tuple):
a = []
for i in range(len(arg)):
if isinstance(arg[i], str):
try:
a.append(int(ord(arg[i])))
except TypeError:
raise MipsException(f"Invalid language element: '{arg[i]}'")
elif isinstance(arg[i], int):
a.append(int(arg[i]))
return tuple(a)

return arg

Expand Down Expand Up @@ -72,6 +87,13 @@ def bytesify(data: Union[str, int, bytes], *, size=None, null_byte=True) -> byte
if isinstance(data, int):
int_size = size if size else (data.bit_length() // 8) + 1
return (data & 0xFFFF_FFFF).to_bytes(int_size, "big")
if isinstance(data, tuple):
byte_string = bytes()
for integer in data:
int_size = size if size else (integer.bit_length() // 8) + 1
byte_string += (integer & 0xFFFF_FFFF).to_bytes(int_size, "big")
return byte_string

return bytes(data)


Expand Down

0 comments on commit 9fc564d

Please sign in to comment.