Exemple #1
0
bool IA_IAPI::isFrameSetupInsn(Instruction::Ptr i) const
{
    if(i->getOperation().getID() == e_mov)
    {
        if(i->readsMemory() || i->writesMemory())
        {
            parsing_printf("%s[%d]: discarding insn %s as stack frame preamble, not a reg-reg move\n",
                           FILE__, __LINE__, i->format().c_str());
            //return false;
        }
        if(i->isRead(stackPtr[_isrc->getArch()]) &&
           i->isWritten(framePtr[_isrc->getArch()]))
        {
            if((unsigned) i->getOperand(0).getValue()->size() == _isrc->getAddressWidth())
            {
                return true;
            }
            else
            {
                parsing_printf("%s[%d]: discarding insn %s as stack frame preamble, size mismatch for %d-byte addr width\n",
                               FILE__, __LINE__, i->format().c_str(), _isrc->getAddressWidth());
            }
        }
    }
    return false;
}
Exemple #2
0
void SpringboardBuilder::generateBranch(Address from, Address to, codeGen &gen) {
  gen.invalidate();
  gen.allocate(16);

  gen.setAddrSpace(addrSpace_);
  gen.setAddr(from);

  insnCodeGen::generateBranch(gen, from, to);

  springboard_cerr << "Generated springboard branch " << hex << from << "->" << to << dec << endl;

#if 0
#include "InstructionDecoder.h"
    using namespace Dyninst::InstructionAPI;
    Address base = 0;
    InstructionDecoder deco(gen.start_ptr(),gen.size(),Arch_aarch64);
    Instruction::Ptr insn = deco.decode();
    while(base<gen.used()+5) {
        std::stringstream rawInsn;
        unsigned idx = insn->size();
        while(idx--) rawInsn << hex << setfill('0') << setw(2) << (unsigned int) insn->rawByte(idx);

        cerr << "\t" << hex << base << ":   " << rawInsn.str() << "   "
            << insn->format(base) << dec << endl;
        base += insn->size();
        insn = deco.decode();
    }
#endif
}
Exemple #3
0
void LivenessAnalyzer::summarizeBlockLivenessInfo(Function* func, Block *block, bitArray &allRegsDefined) 
{
   if (blockLiveInfo.find(block) != blockLiveInfo.end()){
   	return;
   }
 
   livenessData &data = blockLiveInfo[block];
   data.use = data.def = data.in = abi->getBitArray();

   using namespace Dyninst::InstructionAPI;
   Address current = block->start();
   InstructionDecoder decoder(
                       reinterpret_cast<const unsigned char*>(getPtrToInstruction(block, block->start())),		     
                       block->size(),
                       block->obj()->cs()->getArch());
   Instruction::Ptr curInsn = decoder.decode();
   while(curInsn) {
     ReadWriteInfo curInsnRW;
     liveness_printf("%s[%d] After instruction %s at address 0x%lx:\n",
                     FILE__, __LINE__, curInsn->format().c_str(), current);
     if(!cachedLivenessInfo.getLivenessInfo(current, func, curInsnRW))
     {
       curInsnRW = calcRWSets(curInsn, block, current);
       cachedLivenessInfo.insertInstructionInfo(current, curInsnRW, func);
     }

     data.use |= (curInsnRW.read & ~data.def);
     // And if written, then was defined
     data.def |= curInsnRW.written;
      
     liveness_printf("%s[%d] After instruction at address 0x%lx:\n",
                     FILE__, __LINE__, current);
     liveness_cerr << "        " << regs1 << endl;
     liveness_cerr << "        " << regs2 << endl;
     liveness_cerr << "        " << regs3 << endl;
     liveness_cerr << "Read    " << curInsnRW.read << endl;
     liveness_cerr << "Written " << curInsnRW.written << endl;
     liveness_cerr << "Used    " << data.use << endl;
     liveness_cerr << "Defined " << data.def << endl;

      current += curInsn->size();
      curInsn = decoder.decode();
   }

   liveness_printf("%s[%d] Liveness summary for block:\n", FILE__, __LINE__);
   liveness_cerr << "     " << regs1 << endl;
   liveness_cerr << "     " << regs2 << endl;
   liveness_cerr << "     " << regs3 << endl;
   liveness_cerr << "Used " << data.in << endl;
   liveness_cerr << "Def  " << data.def << endl;
   liveness_cerr << "Use  " << data.use << endl;
   liveness_printf("%s[%d] --------------------\n---------------------\n", FILE__, __LINE__);

   allRegsDefined |= data.def;

   return;
}
Exemple #4
0
bool Injector::inject(std::string libname) {
   int_process *proc = proc_->llproc();
   pthrd_printf("Injecting %s into process %d\n", libname.c_str(), proc->getPid());
   if (!checkIfExists(libname)) {
      perr_printf("Library %s doesn't exist\n", libname.c_str());
      proc->setLastError(err_nofile, "File doesn't exist\n");
      return false;
   }

   Codegen codegen(proc_, libname);
   if (!codegen.generate()) {
      perr_printf("Could not generate code\n");
      proc->setLastError(err_internal, "Error in code generation");
      return false;
   }

   int_iRPC::ptr irpc = int_iRPC::ptr(new int_iRPC(codegen.buffer().start_ptr(),
                                                   codegen.buffer().size(),
                                                   false,
                                                   true,
                                                   codegen.buffer().startAddr()));
   // Don't try to execute a library name...
   irpc->setStartOffset(codegen.startOffset());

#if defined(DEBUG_DISASSEMBLE)
   cerr << "Setting starting offset to " << hex << codegen.startOffset() << endl;
   cerr << "And starting address is " << codegen.buffer().startAddr() << dec << endl;

   unsigned char *ptr = codegen.buffer().start_ptr();
   ptr += codegen.startOffset();
   Offset size = codegen.buffer().size() - codegen.startOffset();

   InstructionDecoder d(ptr, size, proc_->getArchitecture());

   Offset off = 0;
   while (off < size) {
     Instruction::Ptr insn = d.decode();
     cerr << hex << off + codegen.startOffset() + codegen.buffer().startAddr() << " : " << insn->format() << endl;
     off += insn->size();
   }

   off = 0;
   while (off < size) {
     cerr << hex <<  off + codegen.startOffset() + codegen.buffer().startAddr() << ": " << (int) ptr[off] << dec << endl;
     off++;
   }

#endif

   //Post, but doesn't start running yet.
   bool result = rpcMgr()->postRPCToProc(proc, irpc);
   if (!result) {
      pthrd_printf("Error posting RPC to process %d\n", proc->getPid());
      return false;
   }

   //Set the internal state so that this iRPC runs.
   int_thread *thr = irpc->thread();
   thr->getInternalState().desyncState(int_thread::running);
   irpc->setRestoreInternal(true);
   
   //Run the IRPC and wait for completion.
   proc->throwNopEvent();
   result = int_process::waitAndHandleEvents(false);
   if (!result) {
      perr_printf("Error waiting for and handling events\n");
      return false;
   }

   //TODO: Any mechanism for error checks?
   
   return true;
}                                                   
Exemple #5
0
boost::tuple<Instruction::Ptr,
 Instruction::Ptr,
 bool> IA_x86Details::findMaxSwitchInsn(Block *start) 
{
    std::set<Block *> visited;
    std::vector<Block *> WL;
    Block *curBlk;
    int depth = 0;

    bool foundMaxSwitch = false;
    bool foundCondBranch = false;

    WL.push_back(start);
    Instruction::Ptr compareInsn, condBranchInsn;
    bool compareOnTakenBranch = false;
    for(unsigned j=0;j < WL.size(); j++)
    {
        curBlk = WL[j];
        visited.insert(curBlk);

        foundMaxSwitch = false;
        foundCondBranch = false;
        const unsigned char* buf =
                (const unsigned char*)(currentBlock->_isrc->getPtrToInstruction(curBlk->start()));
        if( buf == NULL ) {
            parsing_printf("%s[%d]: failed to get pointer to instruction by offset\n",
                           FILE__, __LINE__);
            return boost::make_tuple(Instruction::Ptr(), Instruction::Ptr(), false);
        }
        InstructionDecoder dec(buf, curBlk->size(), currentBlock->_isrc->getArch());
        Instruction::Ptr i;
        Address curAdr = curBlk->start();
        while((i = dec.decode()))
        {
            if(i->getCategory() == c_CompareInsn)
            // check for cmp
            {
                parsing_printf("\tFound jmp table cmp instruction %s at 0x%lx\n",
                               i->format().c_str(), curAdr);
                compareInsn = i;
                foundMaxSwitch = true;
            }
            if(i->getCategory() == c_BranchInsn &&
               i->allowsFallThrough())
            {
                parsing_printf("\tFound jmp table cond br instruction %s at 0x%lx\n",
                               i->format().c_str(), curAdr);
                condBranchInsn = i;
                foundCondBranch = true;

                Block::edgelist::const_iterator tit = curBlk->targets().begin();
                bool taken_hit = false;
                bool fallthrough_hit = false;
                for ( ; tit != curBlk->targets().end(); ++tit) {
                    ParseAPI::Edge *t = *tit;
                    if (t->type() == COND_TAKEN &&
                        (visited.find(t->trg()) != visited.end()))
                    {
                        taken_hit = true;
                    }
                    if ((t->type() == COND_NOT_TAKEN ||
                         t->type() == FALLTHROUGH) &&
                         (visited.find(t->trg()) != visited.end()))
                    {
                        fallthrough_hit = true;
                    }
                }
                parsing_printf("\tfindMaxSwitchInsn: taken_hit: %d, fallthrough_hit: %d\n", taken_hit, fallthrough_hit);
                compareOnTakenBranch = taken_hit && !fallthrough_hit;
                break;
            }
            curAdr += i->size();
        }

        if(foundMaxSwitch && foundCondBranch)
            break; // done

            // look further back
        Block::edgelist::const_iterator sit = curBlk->sources().begin();
        depth++;
            // We've seen depth 2 in libc et al
        if(depth > 2) return boost::make_tuple(Instruction::Ptr(), Instruction::Ptr(), false);
           
        for( ; sit != curBlk->sources().end(); ++sit)
        {
            ParseAPI::Edge * s = *sit;

            // ignore return edges
            if(s->type() == RET)
                continue;

            if(s->type() == CALL)
                return boost::make_tuple(Instruction::Ptr(), Instruction::Ptr(), false);

            Block * src = s->src();
            if( (visited.find( src ) == visited.end())) {
                WL.push_back(src);
            }
        }
    }
    WL.clear();
    parsing_printf("\tfindMaxSwitchInsn: table on taken branch: %d, returning: %d\n", compareOnTakenBranch, foundMaxSwitch &&
            foundCondBranch);
    
    return boost::make_tuple(compareInsn, condBranchInsn, compareOnTakenBranch);
}
Exemple #6
0
bool IA_x86Details::handleCall(IA_IAPI& block)
{
  parsing_printf("\tchecking call at 0x%lx for thunk\n", block.getAddr());
  if(!block.isRealCall())
  {
    parsing_printf("\tthunk found at 0x%lx, checking for add\n", block.getAddr());
    block.advance();
    thunkInsn.addrFromInsn = block.getAddr();
    Instruction::Ptr addInsn = block.getInstruction();
    if(addInsn)
      parsing_printf("\tinsn after thunk: %s\n", addInsn->format().c_str());
    else
      parsing_printf("\tNO INSN after thunk at 0x%lx\n", thunkInsn.addrFromInsn);
    if(addInsn)
    {
      std::set<RegisterAST::Ptr> boundRegs;
      
      if(addInsn->getOperation().getID() == e_pop)
      {
	addInsn->getWriteSet(boundRegs);
	block.advance();
	addInsn = block.getInstruction();
      }
      if(addInsn && ((addInsn->getOperation().getID() == e_add) ||
		     (addInsn->getOperation().getID() == e_lea)))
      {
	Expression::Ptr op0 = addInsn->getOperand(0).getValue();
	Expression::Ptr op1 = addInsn->getOperand(1).getValue();
	for(std::set<RegisterAST::Ptr>::const_iterator curReg = boundRegs.begin();
	    curReg != boundRegs.end();
	    ++curReg)
	{
	  op0->bind(curReg->get(), Result(u64, 0));
	  op1->bind(curReg->get(), Result(u64, 0));
	  
	}
	
	
	Result imm = addInsn->getOperand(1).getValue()->eval();
	Result imm2 = addInsn->getOperand(0).getValue()->eval();
	if(imm.defined)
	{
	  Address thunkDiff = imm.convert<Address>();
	  parsing_printf("\tsetting thunkInsn.addrFromInsn to 0x%lx (0x%lx + 0x%lx)\n",
			 thunkInsn.addrFromInsn+thunkDiff, thunkInsn.addrFromInsn, thunkDiff);
	  thunkInsn.addrOfInsn = block.getPrevAddr();
	  thunkInsn.addrFromInsn = thunkInsn.addrFromInsn + thunkDiff;
	  return true;
	  
	}
	else if(imm2.defined)
	{
	  Address thunkDiff = imm2.convert<Address>();
	  parsing_printf("\tsetting thunkInsn.addrFromInsn to 0x%lx (0x%lx + 0x%lx)\n",
			 thunkInsn.addrFromInsn+thunkDiff, thunkInsn.addrFromInsn, thunkDiff);
	  thunkInsn.addrOfInsn = block.getPrevAddr();
	  thunkInsn.addrFromInsn = thunkInsn.addrFromInsn + thunkDiff;
	  return true;
	}
	else
	{
	  parsing_printf("\tadd insn %s found following thunk at 0x%lx, couldn't bind operands!\n",
			 addInsn->format().c_str(), thunkInsn.addrFromInsn);
	}
      }
    }
    thunkInsn.addrFromInsn = 0;
  }
  thunkInsn.addrFromInsn = 0;
  thunkInsn.addrOfInsn = 0;
  thunkInsn.insn.reset();
  
  return false;
}
Exemple #7
0
ReadWriteInfo LivenessAnalyzer::calcRWSets(Instruction::Ptr curInsn, Block* blk, Address a)
{

  liveness_cerr << "calcRWSets for " << curInsn->format() << " @ " << hex << a << dec << endl;
  ReadWriteInfo ret;
  ret.read = abi->getBitArray();
  ret.written = abi->getBitArray();
  ret.insnSize = curInsn->size();
  std::set<RegisterAST::Ptr> cur_read, cur_written;
  curInsn->getReadSet(cur_read);
  curInsn->getWriteSet(cur_written);
    liveness_printf("Read registers: \n");
  
  for (std::set<RegisterAST::Ptr>::const_iterator i = cur_read.begin(); 
       i != cur_read.end(); i++) 
  {
    MachRegister cur = (*i)->getID();
    if (cur.getArchitecture() == Arch_ppc64)
	cur = MachRegister((cur.val() & ~Arch_ppc64) | Arch_ppc32);
    liveness_printf("\t%s \n", cur.name().c_str());
    MachRegister base = cur.getBaseRegister();
    if (cur == x86::flags || cur == x86_64::flags){
      if (width == 4){
        ret.read[getIndex(x86::of)] = true;
        ret.read[getIndex(x86::cf)] = true;
        ret.read[getIndex(x86::pf)] = true;
        ret.read[getIndex(x86::af)] = true;
        ret.read[getIndex(x86::zf)] = true;
        ret.read[getIndex(x86::sf)] = true;
        ret.read[getIndex(x86::df)] = true;
        ret.read[getIndex(x86::tf)] = true;
        ret.read[getIndex(x86::nt_)] = true;
      }
      else {
        ret.read[getIndex(x86_64::of)] = true;
        ret.read[getIndex(x86_64::cf)] = true;
        ret.read[getIndex(x86_64::pf)] = true;
        ret.read[getIndex(x86_64::af)] = true;
        ret.read[getIndex(x86_64::zf)] = true;
        ret.read[getIndex(x86_64::sf)] = true;
        ret.read[getIndex(x86_64::df)] = true;
        ret.read[getIndex(x86_64::tf)] = true;
        ret.read[getIndex(x86_64::nt_)] = true;
      }
    }
    else{
      base = changeIfMMX(base);
      ret.read[getIndex(base)] = true;
    }
  }
  liveness_printf("Write Registers: \n"); 
  for (std::set<RegisterAST::Ptr>::const_iterator i = cur_written.begin(); 
       i != cur_written.end(); i++) {  
    MachRegister cur = (*i)->getID();
    if (cur.getArchitecture() == Arch_ppc64)
	cur = MachRegister((cur.val() & ~Arch_ppc64) | Arch_ppc32);
    liveness_printf("\t%s \n", cur.name().c_str());
    MachRegister base = cur.getBaseRegister();
    if (cur == x86::flags || cur == x86_64::flags){
      if (width == 4){
        ret.written[getIndex(x86::of)] = true;
        ret.written[getIndex(x86::cf)] = true;
        ret.written[getIndex(x86::pf)] = true;
        ret.written[getIndex(x86::af)] = true;
        ret.written[getIndex(x86::zf)] = true;
        ret.written[getIndex(x86::sf)] = true;
        ret.written[getIndex(x86::df)] = true;
        ret.written[getIndex(x86::tf)] = true;
        ret.written[getIndex(x86::nt_)] = true;
      }
      else {
        ret.written[getIndex(x86_64::of)] = true;
        ret.written[getIndex(x86_64::cf)] = true;
        ret.written[getIndex(x86_64::pf)] = true;
        ret.written[getIndex(x86_64::af)] = true;
        ret.written[getIndex(x86_64::zf)] = true;
        ret.written[getIndex(x86_64::sf)] = true;
        ret.written[getIndex(x86_64::df)] = true;
        ret.written[getIndex(x86_64::tf)] = true;
        ret.written[getIndex(x86_64::nt_)] = true;
      }
    }
    else{
      base = changeIfMMX(base);
      ret.written[getIndex(base)] = true;
      if ((cur != base && cur.size() < 4) || isMMX(base)) ret.read[getIndex(base)] = true;
    }
  }
  InsnCategory category = curInsn->getCategory();
  switch(category)
  {
  case c_CallInsn:
      // Call instructions not at the end of a block are thunks, which are not ABI-compliant.
      // So make conservative assumptions about what they may read (ABI) but don't assume they write anything.
      ret.read |= (abi->getCallReadRegisters());
      if(blk->lastInsnAddr() == a)
      {
          ret.written |= (abi->getCallWrittenRegisters());
      }
    break;
  case c_ReturnInsn:
    ret.read |= (abi->getReturnReadRegisters());
    // Nothing written implicitly by a return
    break;
  case c_BranchInsn:
    if(!curInsn->allowsFallThrough() && isExitBlock(blk))
    {
      //Tail call, union of call and return
      ret.read |= ((abi->getCallReadRegisters()) |
		   (abi->getReturnReadRegisters()));
      ret.written |= (abi->getCallWrittenRegisters());
    }
    break;
  default:
    {
      bool isInterrupt = false;
      bool isSyscall = false;


      if ((curInsn->getOperation().getID() == e_int) ||
	  (curInsn->getOperation().getID() == e_int3)) {
	isInterrupt = true;
      }
      static RegisterAST::Ptr gs(new RegisterAST(x86::gs));
      if (((curInsn->getOperation().getID() == e_call) &&
	   /*(curInsn()->getOperation().isRead(gs))) ||*/
	   (curInsn->getOperand(0).format(curInsn->getArch()) == "16")) ||
	  (curInsn->getOperation().getID() == e_syscall) || 
	  (curInsn->getOperation().getID() == e_int) || 
	  (curInsn->getOperation().getID() == power_op_sc)) {
	isSyscall = true;
      }

      if (curInsn->getOperation().getID() == power_op_svcs) {
	isSyscall = true;
      }
      if (isInterrupt || isSyscall) {
	ret.read |= (abi->getSyscallReadRegisters());
	ret.written |= (abi->getSyscallWrittenRegisters());
      }
    }
    break;
  }	  
  return ret;
}
Exemple #8
0
bool IA_IAPI::isTailCall(Function * context, EdgeTypeEnum type, unsigned int, const set<Address>& knownTargets) const
{
   // Collapse down to "branch" or "fallthrough"
    switch(type) {
       case COND_TAKEN:
       case DIRECT:
       case INDIRECT:
          type = DIRECT;
          break;
       case CALL:
       case RET:
       case COND_NOT_TAKEN:
       case FALLTHROUGH:
       case CALL_FT:
       default:
          return false;
    }

    parsing_printf("Checking for Tail Call \n");
    context->obj()->cs()->incrementCounter(PARSE_TAILCALL_COUNT); 
    if (tailCalls.find(type) != tailCalls.end()) {
        parsing_printf("\tReturning cached tail call check result: %d\n", tailCalls[type]);
        if (tailCalls[type]) {
            context->obj()->cs()->incrementCounter(PARSE_TAILCALL_FAIL);
            return true;
        }
        return false;
    }
    
    bool valid; Address addr;
    boost::tie(valid, addr) = getCFT();

    Function* callee = _obj->findFuncByEntry(_cr, addr);
    Block* target = _obj->findBlockByEntry(_cr, addr);

    // check if addr is in a block if it is not entry.
    if (target == NULL) {
        std::set<Block*> blocks;
        _obj->findCurrentBlocks(_cr, addr, blocks);
        if (blocks.size() == 1) {
            target = *blocks.begin();
        } else if (blocks.size() == 0) {
	    // This case can happen when the jump target is a function entry,
	    // but we have not parsed the function yet,
	    // or when this is an indirect jump 
	    target = NULL;
	} else {
	    // If this case happens, it means the jump goes into overlapping instruction streams,
	    // it is not likely to be a tail call.
	    parsing_printf("\tjumps into overlapping instruction streams\n");
	    for (auto bit = blocks.begin(); bit != blocks.end(); ++bit) {
	        parsing_printf("\t block [%lx,%lx)\n", (*bit)->start(), (*bit)->end());
	    }
	    parsing_printf("\tjump to 0x%lx, NOT TAIL CALL\n", addr);
	    tailCalls[type] = false;
	    return false;
	}
    }

    if(curInsn()->getCategory() == c_BranchInsn &&
       valid &&
       callee && 
       callee != context &&
       target &&
       !context->contains(target)
       )
    {
      parsing_printf("\tjump to 0x%lx, TAIL CALL\n", addr);
      tailCalls[type] = true;
      return true;
    }

    if (curInsn()->getCategory() == c_BranchInsn &&
            valid &&
            !callee) {
	if (target) {
	    parsing_printf("\tjump to 0x%lx is known block, but not func entry, NOT TAIL CALL\n", addr);
	    tailCalls[type] = false;
	    return false;
	} else if (knownTargets.find(addr) != knownTargets.end()) {
	    parsing_printf("\tjump to 0x%lx is known target in this function, NOT TAIL CALL\n", addr);
	    tailCalls[type] = false;
	    return false;
	}
    }

    if(allInsns.size() < 2) {
      if(context->addr() == _curBlk->start() && curInsn()->getCategory() == c_BranchInsn)
      {
	parsing_printf("\tjump as only insn in entry block, TAIL CALL\n");
	tailCalls[type] = true;
	return true;
      }
      else
      {
        parsing_printf("\ttoo few insns to detect tail call\n");
        context->obj()->cs()->incrementCounter(PARSE_TAILCALL_FAIL);
        tailCalls[type] = false;
        return false;
      }
    }

    if ((curInsn()->getCategory() == c_BranchInsn))
    {
        //std::map<Address, Instruction::Ptr>::const_iterator prevIter =
                //allInsns.find(current);
        
        // Updated: there may be zero or more nops between leave->jmp
       
        allInsns_t::const_iterator prevIter = curInsnIter;
        --prevIter;
        Instruction::Ptr prevInsn = prevIter->second;
    
        while ( isNopInsn(prevInsn) && (prevIter != allInsns.begin()) ) {
           --prevIter;
           prevInsn = prevIter->second;
        }
	prevInsn = prevIter->second;
        if(prevInsn->getOperation().getID() == e_leave)
        {
           parsing_printf("\tprev insn was leave, TAIL CALL\n");
           tailCalls[type] = true;
           return true;
        }
        else if(prevInsn->getOperation().getID() == e_pop)
        {
            if(prevInsn->isWritten(framePtr[_isrc->getArch()]))
            {
                parsing_printf("\tprev insn was %s, TAIL CALL\n", prevInsn->format().c_str());
                tailCalls[type] = true;
                return true;
            }
        }
        else if(prevInsn->getOperation().getID() == e_add)
        {			
            if(prevInsn->isWritten(stackPtr[_isrc->getArch()]))
            {
				bool call_fallthrough = false;
				if (_curBlk->start() == prevIter->first) {				
					for (auto eit = _curBlk->sources().begin(); eit != _curBlk->sources().end(); ++eit) {						
						if ((*eit)->type() == CALL_FT) {
							call_fallthrough = true;
							break;
						}
					}
				}
				if (call_fallthrough) {
					parsing_printf("\tprev insn was %s, but it is the next instruction of a function call, not a tail call %x %x\n", prevInsn->format().c_str()); 
				}	else {
					parsing_printf("\tprev insn was %s, TAIL CALL\n", prevInsn->format().c_str());
					tailCalls[type] = true;
					return true;
				}
			} else
				parsing_printf("\tprev insn was %s, not tail call\n", prevInsn->format().c_str());
        }
    }

    tailCalls[type] = false;
    context->obj()->cs()->incrementCounter(PARSE_TAILCALL_FAIL);
    return false;
}