1
0

Made infernal-intepreter more unix-like.

This commit is contained in:
Jon Michael Aanes 2017-11-19 16:53:15 +01:00
parent 57049e1377
commit 8d0b672b63
7 changed files with 131 additions and 89 deletions

View File

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

11
Junk.py
View File

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

View File

@ -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[]'

View File

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

View File

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

View File

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

View File

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