From 8d0b672b63fb1c349f7e6a00de4c1fdaeab2d2ef Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Sun, 19 Nov 2017 16:53:15 +0100 Subject: [PATCH] Made infernal-intepreter more unix-like. --- Emulator.py | 20 ++++++---- Junk.py | 11 ++++-- TikzPainter.py | 6 ++- examples/fibonnaci_iterative.S | 25 ++++++++++++ examples/fibonnaci_recursive.S | 24 ++++++++++++ infernal.py | 62 +++++++++++++++++++++++++++-- tests/test_fib.py | 72 ---------------------------------- 7 files changed, 131 insertions(+), 89 deletions(-) create mode 100644 examples/fibonnaci_iterative.S create mode 100644 examples/fibonnaci_recursive.S delete mode 100644 tests/test_fib.py diff --git a/Emulator.py b/Emulator.py index c204975..b8334fd 100644 --- a/Emulator.py +++ b/Emulator.py @@ -6,7 +6,6 @@ from opcodes import OPCODES REGISTERS=["%rax", "%rbx", "%rcx", "%rdx", "%rsp", "%rbp", "%rsi", "%rdi", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"] -JUNK_VALUE = Junk.Junk() class CodeParseException (BaseException): @@ -14,16 +13,19 @@ class CodeParseException (BaseException): self.line_nr = line_nr self.string = string +class InternalExecutionException (BaseException): + pass + class Emulator: def __init__ (self, source_text, max_stack_size=1000): self.source = source_text self.registers = {} for reg_name in REGISTERS: - self.registers[reg_name] = JUNK_VALUE + self.registers[reg_name] = Junk.Junk() self.stack = {} for i in range(max_stack_size): - self.stack[i] = JUNK_VALUE + self.stack[i] = Junk.Junk() self.code = [] self.labels = {} self.changes = {} @@ -43,12 +45,12 @@ class Emulator: for element in stack_list: i -= 1 self.stack[i] = element - if (not 'set_rsp' in kwargs) or kwargs['set_rsp']: - self.setRegs(rsp=i) - self.last_sp = i-1 + self.setRegs(rsp=i) + self.last_sp = i-1 def setRegs (self, **reg_dict): for reg_name, reg_val in reg_dict.iteritems(): + assert(reg_name[0] != '%') self.registers["%"+reg_name] = reg_val def getVal (self, val_text): @@ -127,8 +129,10 @@ class Emulator: def iterate (self): old_rip = self.registers['%rip'] self.last_sp = self.registers['%rsp'] - if self.registers['%rip'] >= len(self.code): + if self.registers['%rip'] >= len(self.code) or isinstance(self.registers['%rip'], Junk.Junk): return None + if not isinstance(self.registers['%rip'], (int, long)): + raise InternalExecutionException("Register %rip should be integer, but was "+str(self.registers['%rip'])) instruct = self.code[self.registers['%rip']] opcode = instruct[0] self.changes = {} @@ -153,4 +157,4 @@ class Emulator: return output def getUsedRegisters (self): - return [reg_name for reg_name, reg_val in self.registers.iteritems() if reg_val!=JUNK_VALUE] + return [reg_name for reg_name, reg_val in self.registers.iteritems() if not isinstance(reg_val, Junk.Junk)] diff --git a/Junk.py b/Junk.py index 7ecc07c..14bd5a6 100644 --- a/Junk.py +++ b/Junk.py @@ -5,14 +5,17 @@ class JunkComparisonException (BaseException): class Junk: - def __init__ (self): - pass + def __init__ (self, represents = None): + assert(represents == None or isinstance(represents, basestring)) + self.repr = represents def __str__ (self): - return "[Junk]" + if self.repr: + return '['+self.repr+']' + return "[junk]" def __repr__ (self): - return "[Junk]" + return self.__str__() def __add__ (self, other): return self diff --git a/TikzPainter.py b/TikzPainter.py index a939ef4..ca2f5aa 100644 --- a/TikzPainter.py +++ b/TikzPainter.py @@ -33,6 +33,7 @@ class TikzPainter: pos -= 1.5 base_sp = min(emu.last_sp, emu.getVal('%rsp')) pos += 0.5 * max(0, emu.getVal('%rsp')-emu.last_sp) + assert(isinstance(emu.getVal('%rsp'), (int, long))) for index in range(base_sp, 3+min(emu.max_stack_size, emu.getVal('%rsp')+self.max_stack_size)): reg_state = emu.regState("m"+str(index)) reg_dect = "register_node, fill="+self.getRegColor(reg_state)+"!10" @@ -110,7 +111,7 @@ class TikzPainter: self.addText("\t\\node[text_node]() at ({}, {}){{TOS-{}}};\n", x, pos, index) - def __str__ (self): + def to_string (self): PRE = """ \\documentclass{standalone} \\usepackage{tikz} \\begin{document} @@ -118,3 +119,6 @@ class TikzPainter: POST = """\\end{tikzpicture} \\end{document}""" return PRE + "".join(self.text) + POST + + def __str__ (self): + return 'TikzPainter[]' diff --git a/examples/fibonnaci_iterative.S b/examples/fibonnaci_iterative.S new file mode 100644 index 0000000..743f9f8 --- /dev/null +++ b/examples/fibonnaci_iterative.S @@ -0,0 +1,25 @@ +# Interative Fibonnaci +# %rdi is the number for iterations. + +fib: push %rbp # push previous base pointer + movq %rsp,%rbp # setup new base pointer + +if: cmpq $1, %rdi # if (a-1) > 0: goto loops + jg loops + +quick: movq %rdi, %rax + jmp return + +loops: movq %rdi, %r8 # i (r8) = a + movq $1, %rdi # minus_one (rdi) = 1 + movq $0, %rax # minus_two (rax) = 0 + +loopb: movq %rdi, %9 # r = minus_one + addq %rax, %rdi # minus_one += minus_two + movq %9, %rax # minus_two = r + subq $1, %r8 # i-- + cmpq $0, %r8 # if a > 0 + jg loopb + +return: leave # Clean up + ret # return m (rax) diff --git a/examples/fibonnaci_recursive.S b/examples/fibonnaci_recursive.S new file mode 100644 index 0000000..27fa7f0 --- /dev/null +++ b/examples/fibonnaci_recursive.S @@ -0,0 +1,24 @@ +# Recursive Fibonnaci +# %rdi is the number for iterations. + +fib: push %rbp # push previous base pointer + movq %rsp,%rbp # setup new base pointer + +if: cmpq $1, %rdi # if (a-1) > 0: goto long + jg long + +quick: movq %rdi, %rax + jmp return + +long: pushq %rdi # Push a onto the stack + subq $1, %rdi + call fib # Call fib(a-1) + pushq %rax # Push fib(a-1) onto the stack + subq $1, %rdi + call fib # Call fib(a-2) + popq %rdi # pop fib(a-1) into rdi + addq %rdi, %rax # rax = fib(a-1)+fib(a-2) + popq %rdi # Guarantee that a lies in rdi. + +return: leave # Clean up (stack pointers?) + ret # return m (rax) diff --git a/infernal.py b/infernal.py index dc1026b..a4ccb09 100644 --- a/infernal.py +++ b/infernal.py @@ -1,10 +1,64 @@ import sys -import getopt -from Emulator import Emulator, CodeParseException +import re +import argparse + +from Emulator import Emulator, CodeParseException, REGISTERS from TikzPainter import TikzPainter -from Junk import Junk, JunkComparisonException +import Junk + +def parse_args (): + parser = argparse.ArgumentParser(description="For fun x86-64 emulator and stack visualizer.") + parser.add_argument('-i', '--input-file', default='-', help = '.S file to use as input', dest = 'filename') + for register in REGISTERS: + parser.add_argument('--'+register[1:], nargs = 1, type = int, default = [Junk.Junk()], dest = register) + args = vars(parser.parse_args()) + + # Determine args # + registers_init = {} + for register in REGISTERS: + registers_init[register[1:]] = args[register][0] + registers_init['rip'] = 0 + registers_init['rsp'] = 0 + registers_init['rbp'] = Junk.Junk('old bp') + + program = "" + if args['filename'] == '-': + program = sys.stdin.read() + else: + with open(args['filename']) as file: + program = file.read() + + return (program, registers_init) + +def main (): + + (program, registers_init) = parse_args() + + # Determine registers to display + registers_to_draw = ["%rip","%rbp","%rsp", ""] + for match in re.findall(r"%r[a-z0-9]{1,2}", program): + if match not in registers_to_draw: + registers_to_draw.append(match) + + # Setup emulator and drawer + emu = Emulator(program) + emu.setRegs(**registers_init) + emu.setStack(Junk.Junk("junk..."), Junk.Junk("calling eip")) + + painter = TikzPainter(registers_to_draw) + + # Iteratively draw states. + painter.drawState(emu) + for line_nr in emu: + painter.drawState(emu, line_nr) + painter.drawNames(emu) + + # Display result + print(painter.to_string()) if __name__ == "__main__": - pass + main() + + diff --git a/tests/test_fib.py b/tests/test_fib.py deleted file mode 100644 index 2c9995e..0000000 --- a/tests/test_fib.py +++ /dev/null @@ -1,72 +0,0 @@ -import os,sys,inspect -sys.path.insert(1, os.path.join(sys.path[0], '..')) -import infernal - -fib_prog = """ -fib: push %rbp # push previous base pointer - movq %rsp,%rbp # setup new base pointer -if: cmpq $1, %rdi # if (a-1) > 0: goto long - jg long -quick: movq %rdi, %rax - jmp return -long: pushq %rdi # Push a onto the stack - subq $1, %rdi - call fib # Call fib(a-1) - pushq %rax # Push fib(a-1) onto the stack - subq $1, %rdi - call fib # Call fib(a-2) - popq %rdi # pop fib(a-1) into rdi - addq %rdi, %rax # rax = fib(a-1)+fib(a-2) - popq %rdi # Garentee that a lies in rdi. -return: leave # Clean up (stack pointers?) - ret # return m (rax) -""" - -fib_iter_prog = """ -fib: push %rbp # push previous base pointer - movq %rsp,%rbp # setup new base pointer - -if: cmpq $1, %rdi # if (a-1) > 0: goto loops - jg loops - -quick: movq %rdi, %rax - jmp return - -loops: movq %rdi, %r8 # i (r8) = a - movq $1, %rdi # minus_one (rdi) = 1 - movq $0, %rax # minus_two (rax) = 0 - -loopb: movq %rdi, %9 # r = minus_one - addq %rax, %rdi # minus_one += minus_two - movq %9, %rax # minus_two = r - subq $1, %r8 # i-- - cmpq $0, %r8 # if a > 0 - jg loopb - -return: leave # Clean up - ret # return m (rax) -""" - -def printf (str, *args): - print(str.format(*args)) - -if __name__ == "__main__": - a = 3 - printf("Running for fib({})", a) - - # Setup - registers = ["%rip","%rbp","%rsp","","%rax","%rdi","%r8","%r9"] - emu = infernal.Emulator(fib_prog) - painter = infernal.TikzPainter(registers) - emu.setStack("junk...", "calling eip") - emu.setRegs( rip = 0, rbp = 'old bp', rdi = a ) - - painter.drawState(emu) - for line_nr in emu: - painter.drawState(emu, line_nr) - painter.drawNames(emu) - - printf("fib({}) = {}", a, emu.registers["%rax"]) - - with open("tikz.tex","w") as f: - f.write(str(painter))