/// <summary> /// RtlGoto transfers control to either a constant destination or /// a variable destination computed at run-time. /// </summary> /// <param name="g"></param> /// <returns></returns> public bool VisitGoto(RtlGoto g) { var blockFrom = blockCur; if ((g.Class & RtlClass.Delay) != 0) { // Get next instruction cluster. rtlStream.MoveNext(); ProcessRtlCluster(rtlStream.Current); } CallSite site; scanner.TerminateBlock(blockCur, rtlStream.Current.Address + ric.Length); var addrTarget = g.Target as Address; if (addrTarget != null) { var impProc = scanner.GetImportedProcedure(addrTarget, this.ric.Address); if (impProc != null) { site = state.OnBeforeCall(stackReg, arch.PointerType.Size); var sig = impProc.Signature; var chr = impProc.Characteristics; if (chr != null && chr.IsAlloca) { return(ProcessAlloca(site, impProc)); } EmitCall(CreateProcedureConstant(impProc), sig, chr, site); Emit(new ReturnInstruction()); blockCur.Procedure.ControlGraph.AddEdge(blockCur, blockCur.Procedure.ExitBlock); return(false); } if (!program.SegmentMap.IsValidAddress(addrTarget)) { var jmpSite = state.OnBeforeCall(stackReg, arch.PointerType.Size); GenerateCallToOutsideProcedure(jmpSite, addrTarget); Emit(new ReturnInstruction()); blockCur.Procedure.ControlGraph.AddEdge(blockCur, blockCur.Procedure.ExitBlock); return(false); } var trampoline = scanner.GetTrampoline(addrTarget); if (trampoline != null) { var jmpSite = state.OnBeforeCall(stackReg, arch.PointerType.Size); var sig = trampoline.Signature; var chr = trampoline.Characteristics; // Adjust stack to "hide" any pushed return value since // currently Reko treats the return value as an implicit detail // of the calling convention. Had the x86 rewriter explicity // generated code to predecrement the stack pointer // when encountering CALL instructions this would // not be necessary. if (sig.ReturnAddressOnStack != 0) { Emit(new Assignment(stackReg, new BinaryExpression( Operator.IAdd, stackReg.DataType, stackReg, Constant.Word(stackReg.DataType.Size, sig.ReturnAddressOnStack)))); } EmitCall(CreateProcedureConstant(trampoline), sig, chr, jmpSite); if (sig.ReturnAddressOnStack != 0) { //$TODO: make x86 calls' implicit storage explicit // to avoid this hacky dance, Emit(new Assignment(stackReg, new BinaryExpression( Operator.ISub, stackReg.DataType, stackReg, Constant.Word(stackReg.DataType.Size, sig.ReturnAddressOnStack)))); } Emit(new ReturnInstruction()); blockCur.Procedure.ControlGraph.AddEdge(blockCur, blockCur.Procedure.ExitBlock); return(false); } var blockTarget = BlockFromAddress(ric.Address, addrTarget, blockCur.Procedure, state); var blockSource = blockCur.IsSynthesized ? blockCur : scanner.FindContainingBlock(ric.Address); EnsureEdge(blockSource.Procedure, blockSource, blockTarget); if (ric.Address == addrTarget) { var bt = BlockFromAddress(ric.Address, addrTarget, blockCur.Procedure, state); EnsureEdge(blockSource.Procedure, blockFrom, bt); } if (blockCur.Statements.Count == 0) { //$REVIEW: we insert a statement into empty blocks to satisfy the BlockHasBeenScanned // predicate. This should be done in a better way; perhaps by keeping track // of scanned blocks in the Scanner class? // The recursive scanning of basic blocks does need improvement; // consider using a similar technique to Shingle scanner, where reachable // statements are collected first, and basic blocks reconstructed afterwards. Emit(new GotoInstruction(addrTarget)); } return(false); } if (g.Target is MemoryAccess mem) { if (mem.EffectiveAddress is Constant) { // jmp [address] site = state.OnBeforeCall(this.stackReg, 4); //$BUGBUG: hard coded. Emit(new CallInstruction(g.Target, site)); Emit(new ReturnInstruction()); blockCur.Procedure.ControlGraph.AddEdge(blockCur, blockCur.Procedure.ExitBlock); return(false); } } if (ProcessIndirectControlTransfer(ric.Address, g)) { return(false); } site = state.OnBeforeCall(this.stackReg, 4); //$BUGBUG: hard coded Emit(new CallInstruction(g.Target, site)); Emit(new ReturnInstruction()); blockCur.Procedure.ControlGraph.AddEdge(blockCur, blockCur.Procedure.ExitBlock); return(false); }