Ejemplo n.º 1
0
        /// <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);
        }