1
0

Fixed naming of the module.

Moved the test case to its own file.
Allowed the emulator to be iterated over.
Minor fixes and such.
This commit is contained in:
Jon Michael Aanes 2015-12-16 00:43:30 +01:00
parent 63175f36d0
commit 6334d183dd
4 changed files with 192 additions and 164 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# pdfLatex related stuff
*.tex
*.pdf
*.aux
*.log
# Python related stuff
*.pyc

View File

@ -3,3 +3,6 @@
A very simple interpreter and stack tracer for the AMD x86_64 ABI written in A very simple interpreter and stack tracer for the AMD x86_64 ABI written in
Python. Does not support all commands as of yet (or anytime). Python. Does not support all commands as of yet (or anytime).
It produces output in latex/tikz format. Requires `pdflatex` and the latex
packages `preview` and `standalone`.

View File

@ -3,9 +3,7 @@ import re
import sys import sys
REGISTERS=["%rip","%rbp","%rsp","","%rax","%rdi","%r8","%r9"] REGISTERS=["%rip","%rbp","%rsp","","%rax","%rdi","%r8","%r9"]
OPTIONS = """register_node/.style={rectangle, draw=black!30, REG_STATUS_TO_COLOR = {"insert":"green","change":"yellow","remove":"red"}
fill=black!5, very thick, minimum size=0.5, minimum width=20mm},
text_node/.style={}"""
class Emulator: class Emulator:
@ -13,7 +11,7 @@ class Emulator:
self.registers = {} self.registers = {}
for reg_name in REGISTERS: for reg_name in REGISTERS:
self.registers[reg_name] = 0 self.registers[reg_name] = 0
self.stack = [0,1,2,3,4,5,6,7] self.stack = {}
self.code = [] self.code = []
self.codep = [] self.codep = []
self.linesExec = [] self.linesExec = []
@ -28,18 +26,21 @@ class Emulator:
for line in iter(source_text.splitlines()): for line in iter(source_text.splitlines()):
index = self.processSourceLine(line, index) index = self.processSourceLine(line, index)
def getReg (self, reg_name): def setStack (self, *stack_list):
return self.registers[reg_name] i = 0
for element in stack_list:
self.stack[i] = element
i += 1
def setRegs (self, reg_dict): def setRegs (self, **reg_dict):
for reg_name, reg_val in reg_dict.iteritems(): for reg_name, reg_val in reg_dict.iteritems():
self.registers[reg_name] = reg_val self.registers["%"+reg_name] = reg_val
def getVal (self, valText): def getVal (self, val_text):
if valText[0] == "$": if val_text[0] == "$":
return int(valText[1:]) return int(val_text[1:])
elif valText[0] == "%": elif val_text[0] == "%":
return self.getReg(valText) return self.registers[val_text]
else: else:
raise "NOT A VALUE! UGHGUIHGUE!" raise "NOT A VALUE! UGHGUIHGUE!"
@ -48,14 +49,91 @@ class Emulator:
val2 = self.getVal(valT1) val2 = self.getVal(valT1)
self.status = {"g":val1>val2} self.status = {"g":val1>val2}
def writeRegs (self, line_nr=None): def changedRegisters (self, *args):
for reg, val in args:
self.changes[reg] = val
def processSourceLine(self, line_text, index):
tokens = re.findall(r'[\w%:$#]+', line_text)
if len(tokens) <= 0:
return index
if tokens[0][-1] == ':':
self.labels[tokens[0][:-1]] = index
tokens = tokens[1:]
for i in range(len(tokens)):
if tokens[i][0] == "#":
tokens = tokens[:i]
break
self.code.append(tokens)
self.codep.append(line_text)
return index + 1
def getRegColor (self, reg_name):
if reg_name in self.changes:
return REG_STATUS_TO_COLOR[self.changes[reg_name]]
else:
return "black"
def getLineSignature (self, line_nr):
return [ "\\"+token if token[0]=="$" or token[0]=="%" else token for token in self.code[line_nr] ]
def draw (self, x, line_nr=None):
x = str(x)
text = ""
pos = 0
for reg_name in REGISTERS:
reg_dect = "register_node, fill=" + self.getRegColor(reg_name)+"!10"
if reg_name != "":
text += "\t\\node["+reg_dect+"]("+reg_name[1:]+") at ("+x+", "+str(pos)+"){"+str(self.registers[reg_name])+"};\n"
pos -= 0.5
pos -= 1
base_sp = max(self.last_sp,self.registers['%rsp'])
if self.last_sp - 1 == self.registers['%rsp']:
pos += 0.5
for index in range(base_sp, -1,-1):
mem_name = "m"+str(index)
reg_dect = "register_node, fill=" + self.getRegColor(mem_name)+"!10"
text += "\t\\node["+reg_dect+"]("+mem_name+") at ("+x+", "+str(pos)+"){"+str(self.stack[index])+"};\n"
pos -= 0.5
if line_nr != None:
pos = 2
signature = self.getLineSignature(line_nr)
try:
for i in range(3):
text += "\t\\node[text_node]() at ("+str(float(x)-1.25)+", "+str(pos)+"){"+signature[i]+"};\n"
pos -= 0.5
except:
pass
#for line_nr in self.linesExec:
# text += "\t\\node[text_node]() at ("+x+", "+str(pos)+"){"+self.codep[line_nr]+"};\n"
self.linesExec = []
return text
def drawRegs (self, line_nr=None):
self.text += self.draw((self.x_coord+1)*2.5-0.5, line_nr) self.text += self.draw((self.x_coord+1)*2.5-0.5, line_nr)
self.text += "\n" self.text += "\n"
self.x_coord += 1 self.x_coord += 1
def changedRegisters (self, *args): def drawNames (self, x, stack_size=8):
for reg, val in args: x = str(x)
self.changes[reg] = val text = ""
pos = 0
for reg_name in REGISTERS:
text += "\t\\node[text_node]("+reg_name[1:]+") at ("+x+", "+str(pos)+"){"+reg_name[1:]+"};\n"
pos -= 0.5
text += "\t\\node[text_node]() at ("+x+", "+str(pos-0.5)+"){TOS+1};\n"
text += "\t\\node[text_node]() at ("+x+", "+str(pos-1)+"){TOS};\n"
pos -= 1.5
for index in range(1,stack_size+1):
mem_name = "m"+str(index)
text += "\t\\node[text_node]() at ("+x+", "+str(pos)+"){TOS-"+str(index)+"};\n"
pos -= 0.5
return text
def iterate (self): def iterate (self):
old_rip = self.registers['%rip'] old_rip = self.registers['%rip']
@ -66,10 +144,9 @@ class Emulator:
instruct = self.code[self.registers['%rip']] instruct = self.code[self.registers['%rip']]
opcode = instruct[0] opcode = instruct[0]
self.changes = {} self.changes = {}
#print(opcode, instruct)
if opcode == "push" or opcode == "pushq": if opcode == "push" or opcode == "pushq":
self.registers['%rsp'] = self.registers['%rsp'] + 1 self.registers['%rsp'] = self.registers['%rsp'] + 1
self.stack[self.registers['%rsp']] = self.getReg(instruct[1]) self.stack[self.registers['%rsp']] = self.registers[instruct[1]]
self.changedRegisters(('%rsp',"change"),("m"+str(self.registers['%rsp']),"insert")) self.changedRegisters(('%rsp',"change"),("m"+str(self.registers['%rsp']),"insert"))
self.max_stack_size = max(self.registers['%rsp'], self.max_stack_size) self.max_stack_size = max(self.registers['%rsp'], self.max_stack_size)
elif opcode == "popq": elif opcode == "popq":
@ -115,151 +192,13 @@ class Emulator:
else: else:
self.changedRegisters(('%rip',"change")) self.changedRegisters(('%rip',"change"))
self.writeRegs(old_rip) self.drawRegs(old_rip)
def processSourceLine(self, line_text, index): def __iter__(self):
tokens = re.findall(r'[\w%:$#]+', line_text) return self
if len(tokens) <= 0:
return index
if tokens[0][-1] == ':':
self.labels[tokens[0][:-1]] = index
tokens = tokens[1:]
for i in range(len(tokens)):
if tokens[i][0] == "#":
tokens = tokens[:i]
break
self.code.append(tokens)
self.codep.append(line_text)
return index + 1
REG_STATUS_TO_COLOR = {"insert":"green","change":"yellow","remove":"red"} def next(self):
if self.iterate() == False:
def getRegColor (self, reg_name): raise StopIteration()
if reg_name in self.changes:
return self.REG_STATUS_TO_COLOR[self.changes[reg_name]]
else: else:
return "black" return self
def getLineSignature (self, line_nr):
return [ "\\"+token if token[0]=="$" or token[0]=="%" else token for token in self.code[line_nr] ]
def draw (self, x, line_nr=None):
x = str(x)
text = ""
pos = 0
for reg_name in REGISTERS:
reg_dect = "register_node, fill=" + self.getRegColor(reg_name)+"!10"
if reg_name != "":
text += "\t\\node["+reg_dect+"]("+reg_name[1:]+") at ("+x+", "+str(pos)+"){"+str(self.registers[reg_name])+"};\n"
pos -= 0.5
pos -= 1
base_sp = max(self.last_sp,self.registers['%rsp'])
if self.last_sp - 1 == self.registers['%rsp']:
pos += 0.5
for index in range(base_sp, -1,-1):
mem_name = "m"+str(index)
reg_dect = "register_node, fill=" + self.getRegColor(mem_name)+"!10"
text += "\t\\node["+reg_dect+"]("+mem_name+") at ("+x+", "+str(pos)+"){"+str(self.stack[index])+"};\n"
pos -= 0.5
if line_nr != None:
pos = 2
signature = self.getLineSignature(line_nr)
try:
for i in range(3):
text += "\t\\node[text_node]() at ("+str(float(x)-1.25)+", "+str(pos)+"){"+signature[i]+"};\n"
pos -= 0.5
except:
pass
#for line_nr in self.linesExec:
# text += "\t\\node[text_node]() at ("+x+", "+str(pos)+"){"+self.codep[line_nr]+"};\n"
self.linesExec = []
return text
def drawNames (self, x, stack_size=8):
x = str(x)
text = ""
pos = 0
for reg_name in REGISTERS:
text += "\t\\node[text_node]("+reg_name[1:]+") at ("+x+", "+str(pos)+"){"+reg_name[1:]+"};\n"
pos -= 0.5
text += "\t\\node[text_node]() at ("+x+", "+str(pos-0.5)+"){TOS+1};\n"
text += "\t\\node[text_node]() at ("+x+", "+str(pos-1)+"){TOS};\n"
pos -= 1.5
for index in range(1,stack_size+1):
mem_name = "m"+str(index)
text += "\t\\node[text_node]() at ("+x+", "+str(pos)+"){TOS-"+str(index)+"};\n"
pos -= 0.5
return text
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)
"""
if __name__ == "__main__":
try:
a = int(sys.argv[1])
except:
a = 2
new_em = Emulator(fib_iter_prog)
new_em.stack = ["junk...","calling eip"]
for i in range(100):
new_em.stack.append(0)
new_em.setRegs({'%rip':0,'%rbp':'old bp','%rsp':1,'%rdi':a})
print("Running for fib("+str(a)+")")
new_em.writeRegs()
while True:
if new_em.iterate() == False:
break
new_em.text += new_em.drawNames(0, new_em.max_stack_size)
print("fib("+str(a)+") = "+str(new_em.registers["%rax"]))
with open("tikz.tex","w") as f:
f.write("\\documentclass{standalone}\n\n\usepackage{tikz}\\begin{document}\n")
f.write("\\begin{tikzpicture}["+OPTIONS+"]\n")
f.write(new_em.text)
f.write("\\end{tikzpicture}\n\\end{document}")

78
tests/test_fib.py Normal file
View File

@ -0,0 +1,78 @@
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)
"""
OPTIONS = """register_node/.style={rectangle, draw=black!30,
fill=black!5, very thick, minimum size=0.5, minimum width=20mm},
text_node/.style={}"""
def printf (str, *args):
print(str.format(*args))
if __name__ == "__main__":
a = 3
printf("Running for fib({})", a)
# Setup
emu = infernal.Emulator(fib_prog)
emu.setStack("junk...","calling eip")
emu.setRegs( rip = 0, rbp = 'old bp', rsp = 1, rdi = a )
emu.drawRegs()
for emu_ in emu:
pass
emu.text += emu.drawNames(0, emu.max_stack_size)
printf("fib({}) = {}", a, emu.registers["%rax"])
with open("tikz.tex","w") as f:
f.write("\\documentclass{standalone}\n\n\usepackage{tikz}\\begin{document}\n")
f.write("\\begin{tikzpicture}["+OPTIONS+"]\n")
f.write(emu.text)
f.write("\\end{tikzpicture}\n\\end{document}")