import re import sys REGISTERS=["%rip","%rbp","%rsp","","%rax","%rdi","%r8","%r9"] OPTIONS = """register_node/.style={rectangle, draw=black!30, fill=black!5, very thick, minimum size=0.5, minimum width=20mm}, text_node/.style={}""" class Emulator: def __init__ (self, source_text): self.registers = {} for reg_name in REGISTERS: self.registers[reg_name] = 0 self.stack = [0,1,2,3,4,5,6,7] self.code = [] self.codep = [] self.linesExec = [] self.labels = {} self.changes = {} self.status = {} self.text = "" self.x_coord = 0 self.last_sp = 0 self.max_stack_size = 0 index = 0 for line in iter(source_text.splitlines()): index = self.processSourceLine(line, index) def getReg (self, reg_name): return self.registers[reg_name] def setRegs (self, reg_dict): for reg_name, reg_val in reg_dict.iteritems(): self.registers[reg_name] = reg_val def getVal (self, valText): if valText[0] == "$": return int(valText[1:]) elif valText[0] == "%": return self.getReg(valText) else: raise "NOT A VALUE! UGHGUIHGUE!" def compareVal (self, valT1, valT2): val1 = self.getVal(valT2) val2 = self.getVal(valT1) self.status = {"g":val1>val2} def writeRegs (self, line_nr=None): self.text += self.draw((self.x_coord+1)*2.5-0.5, line_nr) self.text += "\n" self.x_coord += 1 def changedRegisters (self, *args): for reg, val in args: self.changes[reg] = val def iterate (self): old_rip = self.registers['%rip'] self.last_sp = self.registers['%rsp'] #self.linesExec.append(old_rip) if self.registers['%rip'] >= len(self.code): return False instruct = self.code[self.registers['%rip']] opcode = instruct[0] self.changes = {} #print(opcode, instruct) if opcode == "push" or opcode == "pushq": self.registers['%rsp'] = self.registers['%rsp'] + 1 self.stack[self.registers['%rsp']] = self.getReg(instruct[1]) self.changedRegisters(('%rsp',"change"),("m"+str(self.registers['%rsp']),"insert")) self.max_stack_size = max(self.registers['%rsp'], self.max_stack_size) elif opcode == "popq": self.registers[instruct[1]] = self.stack[self.registers['%rsp']] self.registers['%rsp'] = self.registers['%rsp'] - 1 self.changedRegisters(('%rsp',"change"),("m"+str(self.registers['%rsp']+1),"remove")) elif opcode == "movq" or opcode == "mov": self.changedRegisters((instruct[2],"change")) self.registers[instruct[2]] = self.getVal(instruct[1]) elif opcode == "cmpq": self.compareVal(instruct[1],instruct[2]) elif opcode == "addq": self.changedRegisters((instruct[2],"change")) self.registers[instruct[2]] = self.getVal(instruct[2]) + self.getVal(instruct[1]) elif opcode == "subq": self.changedRegisters((instruct[2],"change")) self.registers[instruct[2]] = self.getVal(instruct[2]) - self.getVal(instruct[1]) elif opcode == "jg": if self.status["g"]: self.registers['%rip'] = self.labels[instruct[1]] elif opcode == "jmp": self.registers['%rip'] = self.labels[instruct[1]] elif opcode == "call": self.registers['%rsp'] = self.registers['%rsp'] + 1 self.stack[self.registers['%rsp']] = self.registers['%rip']+1 self.registers['%rip'] = self.labels[instruct[1]] self.changedRegisters(('%rsp',"change"),("m"+str(self.registers['%rsp']),"insert")) self.max_stack_size = max(self.registers['%rsp'], self.max_stack_size) elif opcode == "leave": self.registers["%rsp"] = self.registers["%rbp"] self.registers["%rbp"] = self.stack[self.registers['%rsp']] self.registers['%rsp'] = self.registers['%rsp'] - 1 self.changedRegisters(('%rbp',"change")) self.changedRegisters(('%rsp',"change"),("m"+str(self.registers['%rsp']+1),"remove")) elif opcode == "ret": self.registers['%rip'] = self.stack[self.registers['%rsp']] self.registers['%rsp'] = self.registers['%rsp'] - 1 self.changedRegisters(('%rsp',"change"),("m"+str(self.registers['%rsp']+1),"remove")) if self.registers['%rip'] == old_rip: self.registers['%rip'] += 1 else: self.changedRegisters(('%rip',"change")) self.writeRegs(old_rip) 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 REG_STATUS_TO_COLOR = {"insert":"green","change":"yellow","remove":"red"} def getRegColor (self, reg_name): if reg_name in self.changes: return self.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 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}")