LOC_REG=0LOC_ESP_PLUS=1LOC_EBP_PLUS=2LOC_EBP_MINUS=3LOC_MASK=0x03LOC_NOWHERE=LOC_REG|0# x86-32 registers sometimes used to pass arguments when gcc optimizes# a function's calling conventionARGUMENT_REGISTERS_32=('%eax','%edx','%ecx')# x86-64 registers used to pass argumentsARGUMENT_REGISTERS_64=('%rdi','%rsi','%rdx','%rcx','%r8','%r9')defframeloc_esp(offset):assertoffset>=0assertoffset%4==0returnLOC_ESP_PLUS|offsetdefframeloc_ebp(offset):assertoffset%4==0ifoffset>=0:returnLOC_EBP_PLUS|offsetelse:returnLOC_EBP_MINUS|(-offset)classSomeNewValue(object):def__repr__(self):return'somenewvalue'somenewvalue=SomeNewValue()classLocalVar(object):# A local variable location at position 'ofs_from_frame_end',# which is counted from the end of the stack frame (so it is always# negative, unless it refers to arguments of the current function).def__init__(self,ofs_from_frame_end,hint=None):self.ofs_from_frame_end=ofs_from_frame_endself.hint=hintdef__repr__(self):return'<%+d;%s>'%(self.ofs_from_frame_end,self.hintor'e*p')def__hash__(self):returnhash(self.ofs_from_frame_end)def__cmp__(self,other):ifisinstance(other,LocalVar):returncmp(self.ofs_from_frame_end,other.ofs_from_frame_end)else:return1defgetlocation(self,framesize,uses_frame_pointer,wordsize):if(self.hint=='esp'ornotuses_frame_pointerorself.ofs_from_frame_end%2!=0):# try to use esp-relative addressingofs_from_esp=framesize+self.ofs_from_frame_endifofs_from_esp%2==0:returnframeloc_esp(ofs_from_esp)# we can get an odd value if the framesize is marked as bogus# by visit_andl()assertuses_frame_pointerofs_from_ebp=self.ofs_from_frame_end+wordsizereturnframeloc_ebp(ofs_from_ebp)classInsn(object):_args_=[]_locals_=[]def__repr__(self):return'%s(%s)'%(self.__class__.__name__,', '.join([str(getattr(self,name))fornameinself._args_]))defrequestgcroots(self,tracker):return{}defsource_of(self,localvar,tag):returnlocalvardefall_sources_of(self,localvar):return[localvar]classInsnCondJump(Insn):# only for debugging; not used internally_args_=['label']def__init__(self,label):self.label=labelclassLabel(Insn):_args_=['label','lineno']def__init__(self,label,lineno):self.label=labelself.lineno=linenoself.previous_insns=[]# all insns that jump (or fallthrough) hereclassInsnFunctionStart(Insn):_args_=['arguments']framesize=0previous_insns=()def__init__(self,registers,wordsize):self.arguments={}forreginregisters:self.arguments[reg]=somenewvalueself.wordsize=wordsizedefsource_of(self,localvar,tag):iflocalvarnotinself.arguments:ifself.wordsize==4andlocalvarinARGUMENT_REGISTERS_32:# xxx this might show a bug in trackgcroot.py failing to# figure out which instruction stored a value in these# registers. However, this case also occurs when the# the function's calling convention was optimized by gcc:# the 3 registers above are then used to pass argumentspasselifself.wordsize==8andlocalvarinARGUMENT_REGISTERS_64:# this is normal: these registers are always used to# pass argumentspasselse:assert(isinstance(localvar,LocalVar)andlocalvar.ofs_from_frame_end>0),("must come from an argument to the function, got %r"%(localvar,))self.arguments[localvar]=somenewvaluereturnself.arguments[localvar]defall_sources_of(self,localvar):return[]classInsnSetLocal(Insn):_args_=['target','sources']_locals_=['target','sources']def__init__(self,target,sources=()):self.target=targetself.sources=sourcesdefsource_of(self,localvar,tag):iflocalvar==self.target:returnsomenewvaluereturnlocalvardefall_sources_of(self,localvar):iflocalvar==self.target:returnself.sourcesreturn[localvar]classInsnCopyLocal(Insn):_args_=['source','target']_locals_=['source','target']def__init__(self,source,target):self.source=sourceself.target=targetdefsource_of(self,localvar,tag):iflocalvar==self.target:returnself.sourcereturnlocalvardefall_sources_of(self,localvar):iflocalvar==self.target:return[self.source]return[localvar]classInsnStackAdjust(Insn):_args_=['delta']def__init__(self,delta):assertdelta%2==0# should be "% 4", but there is the specialself.delta=delta# case of 'pushw' to handleclassInsnCannotFollowEsp(InsnStackAdjust):def__init__(self):self.delta=-7# use an odd value as markerclassInsnStop(Insn):_args_=['reason']def__init__(self,reason='?'):self.reason=reasonclassInsnRet(InsnStop):_args_=[]framesize=0def__init__(self,registers):self.registers=registersdefrequestgcroots(self,tracker):# no need to track the value of these registers in the caller# function if we are the main(), or if we are flagged as a# "bottom" function (a callback from C code)iftracker.is_stack_bottom:return{}else:returndict(zip(self.registers,self.registers))classInsnCall(Insn):_args_=['lineno','name','gcroots']def__init__(self,name,lineno):# 'gcroots' is a dict built by side-effect during the call to# FunctionGcRootTracker.trackgcroots(). Its meaning is as# follows: the keys are the locations that contain gc roots# (register names or LocalVar instances). The value# corresponding to a key is the "tag", which is None for a# normal gc root, or else the name of a callee-saved register.# In the latter case it means that this is only a gc root if the# corresponding register in the caller was really containing a# gc pointer. A typical example:## InsnCall({LocalVar(-8)': None,# '%esi': '%esi',# LocalVar(-12)': '%ebx'})## means that the value at -8 from the frame end is a gc root# across this call; that %esi is a gc root if it was in the# caller (typically because %esi is not modified at all in the# current function); and that the value at -12 from the frame# end is a gc root if %ebx was a gc root in the caller# (typically because the current function saves and restores# %ebx from there in the prologue and epilogue).self.gcroots={}self.lineno=linenoself.name=namedefsource_of(self,localvar,tag):tag1=self.gcroots.setdefault(localvar,tag)asserttag1==tag,("conflicting entries for\n%s.gcroots[%s]:\n%r and %r"%(self,localvar,tag1,tag))returnlocalvardefall_sources_of(self,localvar):return[localvar]classInsnGCROOT(Insn):_args_=['loc']_locals_=['loc']def__init__(self,loc):self.loc=locdefrequestgcroots(self,tracker):return{self.loc:None}classInsnPrologue(Insn):def__init__(self,wordsize):self.wordsize=wordsizedef__setattr__(self,attr,value):ifattr=='framesize':assertvalue==self.wordsize,("unrecognized function prologue - ""only supports push %ebp; movl %esp, %ebp")Insn.__setattr__(self,attr,value)classInsnEpilogue(Insn):def__init__(self,framesize=None):ifframesizeisnotNone:self.framesize=framesize