internal static bool AreInstructionAdjacent(DalvikInstruction x, DalvikInstruction y) { if (null == x) { throw new ArgumentNullException(); } if (null == y) { throw new ArgumentNullException(); } if (object.ReferenceEquals(x, y)) { throw new InvalidOperationException(); } DalvikInstruction first; DalvikInstruction second; if (x.MethodRelativeOffset < y.MethodRelativeOffset) { first = x; second = y; } else { first = y; second = x; } return (first.MethodRelativeOffset + first.InstructionSize) == second.MethodRelativeOffset; }
/// <summary></summary> /// <param name="method"></param> /// <param name="rootAstNode"></param> /// <returns></returns> internal static CfgNode BuildBasicTree(IMethod method, DalvikInstruction[] instructions, #if DBGCFG bool debugMethodCfg = false #endif ) { CfgNode result = #if DBGCFG new CfgNode(debugMethodCfg); #else new CfgNode(); #endif CreateBasicBlocks(result, method, instructions); EnsureExitNodeUniqueness(result); #if DBGCFG if (debugMethodCfg) { result.DumpGraph(); } #endif return result; }
/// <summary>Bind the given instruction to this block. The instruction must either /// be the first one to be bound to the block or be at offset immediately after the /// last already bound instruction from the block.</summary> /// <param name="instruction">The instruction to be bound.</param> internal void Bind(DalvikInstruction instruction) { if ((null != Successors) && (0 < Successors.Length)) { // We want successorc always to be from the last instruction of the // block so once a successor is set on a block binding additional // instructions is not possible anymore. throw new InvalidOperationException(); } #if DBGCFG if (DebugEnabled && (null != instruction)) { Console.WriteLine("[{0}] += <{1:X4}>", NodeId, instruction.MethodRelativeOffset); } #endif if (null == instruction) { throw new ArgumentNullException(); } if (null == _instructions) { // First instruction to be bound to this block. Easy to handle. _instructions = new List<DalvikInstruction>(); _instructions.Add(instruction); _size += instruction.InstructionSize; return; } // Prevent double insertion. if (_instructions.Contains(instruction)) { throw new InvalidOperationException(); } // The new instruction must be adjacent to the last already bound AND be // at a greater offset. DalvikInstruction lastInstruction = _instructions[_instructions.Count - 1]; uint expectedOffset = lastInstruction.MethodRelativeOffset + lastInstruction.InstructionSize; if (expectedOffset != instruction.MethodRelativeOffset) { throw new InvalidOperationException(); } _instructions.Add(instruction); _size += instruction.InstructionSize; return; }
/// <summary>Create basic blocks.</summary> /// <param name="rootNode">The root node of the graph.</param> /// <param name="method">The method which body is involved.</param> /// <param name="sparseInstructions">A sparse array of instructions to be analyzed. /// Some of the entries in this array are expected to be null references /// denoting no instruction starts at that particular offset within the method /// bytecode.</param> private static void CreateBasicBlocks(CfgNode rootNode, IMethod method, DalvikInstruction[] sparseInstructions) { #if DBGCFG List<CfgNode> methodNodes = new List<CfgNode>(); #endif List<BlockNode> unreachableBlocks = new List<BlockNode>(); BlockNode currentBlock = null; bool firstBlock = true; // An array that maps each bytecode byte to it's owning block if any. BlockNode[] blocksPerOffset = new BlockNode[method.ByteCodeSize]; blocksPerOffset[0] = currentBlock; bool cfgDebuggingEnabled = rootNode.DebugEnabled; // Create basic blocks. These are set of consecutive instructions with // each instruction having a single successor that is the next instruction. foreach (DalvikInstruction scannedInstruction in sparseInstructions) { if (null == scannedInstruction) { continue; } #if DBGCFG if (cfgDebuggingEnabled) { Console.WriteLine("@{0:X4} {1}", scannedInstruction.MethodRelativeOffset, scannedInstruction.GetType().Name); } #endif if (null == currentBlock) { // The previous instruction didn't fail in sequence. Either there is already a // block defined for the current offset or we must create a new one. currentBlock = blocksPerOffset[scannedInstruction.MethodRelativeOffset]; if (null == currentBlock) { currentBlock = new BlockNode(cfgDebuggingEnabled); #if DBGCFG methodNodes.Add(currentBlock); #endif blocksPerOffset[scannedInstruction.MethodRelativeOffset] = currentBlock; if (firstBlock) { CfgNode.Link(rootNode, currentBlock); #if DBGCFG Console.WriteLine("Created first block #{0}", currentBlock.NodeId); #endif firstBlock = false; } else { unreachableBlocks.Add(currentBlock); #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); if (cfgDebuggingEnabled) { Console.WriteLine("Created unreachable block #{0}", currentBlock.NodeId); } #endif } } #if DBGCFG else { if (cfgDebuggingEnabled) { Console.WriteLine("Reusing block #{0}", currentBlock.NodeId); } } #endif } else { // Last instruction failed in sequence. However we may have already defined a // block for the current offset. BlockNode alreadyDefined = blocksPerOffset[scannedInstruction.MethodRelativeOffset]; if ( (null != alreadyDefined) && !object.ReferenceEquals(currentBlock, alreadyDefined)) { #if DBGCFG if (cfgDebuggingEnabled) { Console.WriteLine( "Linking block #{0} to block #{1} and switching to the later", currentBlock.NodeId, alreadyDefined.NodeId); } #endif // make the already defined the current block. CfgNode.Link(currentBlock, alreadyDefined); currentBlock = alreadyDefined; } #if DBGCFG else { if (cfgDebuggingEnabled) { Console.WriteLine("Continuing with block #{0}", currentBlock.NodeId); } } #endif } currentBlock.Bind(scannedInstruction); for(uint sizeIndex = 0; sizeIndex < scannedInstruction.InstructionSize; sizeIndex++) { int offsetIndex = (int)(scannedInstruction.MethodRelativeOffset + sizeIndex); if ( (null != blocksPerOffset[offsetIndex]) && !object.ReferenceEquals(blocksPerOffset[offsetIndex], currentBlock)) { throw new ApplicationException(); } blocksPerOffset[offsetIndex] = currentBlock; #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); #endif } // Scan other targets if any. uint[] otherOffsets = scannedInstruction.AdditionalTargetMethodOffsets; if (null == otherOffsets) { if (!scannedInstruction.ContinueInSequence) { // Must switch to another block. currentBlock = null; } continue; } // Must create a block for each possible target and link current // block to each of those blocks. for (int index = 0; index < otherOffsets.Length; index++) { uint targetOffset = otherOffsets[index]; BlockNode targetBlock = blocksPerOffset[targetOffset]; if (null == targetBlock) { // Block doesn't exists yet. Create and register it. targetBlock = new BlockNode(currentBlock.DebugEnabled); #if DBGCFG methodNodes.Add(targetBlock); #endif blocksPerOffset[targetOffset] = targetBlock; #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); if (targetBlock.DebugEnabled) { Console.WriteLine("Pre-registering block #{0} @{1:X4}", targetBlock.NodeId, targetOffset); Console.WriteLine("Linking block #{0} to block #{1}", currentBlock.NodeId, targetBlock.NodeId); } #endif // Link current node and next one. CfgNode.Link(currentBlock, targetBlock); continue; } // The target block already exists albeit it may deserve a split. // if (0 == targetOffset) { continue; } BlockNode splitCandidate = targetBlock; bool splitCandidateIsCurrentBlock = object.ReferenceEquals(splitCandidate, currentBlock); bool splitCandidateAlreadyAligned = !object.ReferenceEquals(blocksPerOffset[targetOffset - 1], splitCandidate); bool linkCurrentToSplitted = true; try { if (splitCandidateAlreadyAligned && !splitCandidateIsCurrentBlock) { // The split candidate actually starts at target address // and is not the current block. No split required. continue; } // Need a split. uint splitAt; bool makeSplitResultCurrent; if (!splitCandidateAlreadyAligned) { splitAt = targetOffset; makeSplitResultCurrent = false; } else { // The target is the first instruction of current block. We // split the last instruction from the current block. if (!splitCandidateIsCurrentBlock) { throw new AssertionException(); } splitAt = scannedInstruction.MethodRelativeOffset; linkCurrentToSplitted = false; // From now on consider the newly created block to be the // current one. makeSplitResultCurrent = true; } #if DBGCFG if (targetBlock.DebugEnabled) { Console.WriteLine("Spliting block #{0} @{1:X4}.", splitCandidate.NodeId, splitAt); } #endif targetBlock = splitCandidate.Split(splitAt); #if DBGCFG methodNodes.Add(targetBlock); if (targetBlock.DebugEnabled) { Console.WriteLine("New block #{0} created while spliting block #{1}.", targetBlock.NodeId, splitCandidate.NodeId); } #endif if (makeSplitResultCurrent) { currentBlock = targetBlock; } // Update offset to block mapping for new splited block. Also // transfer instructions from existing block to split result. for (int scannedOffset = (int)splitAt; scannedOffset < blocksPerOffset.Length; scannedOffset++) { if (!object.ReferenceEquals(blocksPerOffset[scannedOffset], splitCandidate)) { break; } blocksPerOffset[scannedOffset] = targetBlock; } #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); #endif } finally { if (linkCurrentToSplitted) { #if DBGCFG if ((null != currentBlock) && currentBlock.DebugEnabled) { Console.WriteLine("Linking block #{0} to block #{1}", currentBlock.NodeId, targetBlock.NodeId); } #endif // Link current node and next one. CfgNode.Link(currentBlock, targetBlock); } if (unreachableBlocks.Contains(targetBlock)) { unreachableBlocks.Remove(targetBlock); #if DBGCFG if (targetBlock.DebugEnabled) { Console.WriteLine("Previously unreachable block #{0} now reachable.", targetBlock.NodeId); } #endif } } } // Having other targets force a block reset AND the next instruction to be in // a separate block than the current one, provided the current instruction fails // in sequence. if (scannedInstruction.ContinueInSequence) { uint nextInstructionOffset = scannedInstruction.MethodRelativeOffset + scannedInstruction.InstructionSize; BlockNode nextBlock = blocksPerOffset[nextInstructionOffset]; if (null == nextBlock) { nextBlock = new BlockNode(currentBlock.DebugEnabled); #if DBGCFG methodNodes.Add(nextBlock); #endif blocksPerOffset[nextInstructionOffset] = nextBlock; #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); if (nextBlock.DebugEnabled) { Console.WriteLine("Created next block #{0} @{1:X4}", nextBlock.NodeId, nextInstructionOffset); Console.WriteLine("Linking block #{0} to block #{1}", currentBlock.NodeId, nextBlock.NodeId); } #endif } // Link current node and next one. CfgNode.Link(currentBlock, nextBlock); } // Next block will always be different from the current one. currentBlock = null; } #if DBGCFG if (cfgDebuggingEnabled) { Console.WriteLine("Basic blocks creation done."); } #endif // Link allunreachable blocks to the root node. foreach (BlockNode scannedBlock in unreachableBlocks) { CfgNode.Link(rootNode, scannedBlock); } return; }
/// <summary>Decode instructions for the given method.</summary> /// <param name="method">The method to be decoded.</param> /// <param name="objectResolver">An implementation of an object resolver to be /// used for retrieving classes and constant strings referenced from the bytecode. /// </param> /// <returns>An array indexed by the offset within method byte code and storing /// the instruction starting at this offset (if any).</returns> private static DalvikInstruction[] BuildInstructionList(IMethod method, IResolver objectResolver) { DalvikInstruction[] result = new DalvikInstruction[method.ByteCodeSize]; byte[] byteCode = method.GetByteCode(); List<uint> pendingInstructionsOffset = new List<uint>(); // Add entry point. pendingInstructionsOffset.Add(0); // As well as catch blocks from the exception because they aren't // referenced from normal code. foreach (ITryBlock tryBlock in method.EnumerateTryBlocks()) { foreach (IGuardHandler handler in tryBlock.EnumerateHandlers()) { uint addedOffset = handler.HandlerMethodOffset; // For debugging purpose. Should never occur. if (addedOffset >= byteCode.Length) { throw new ApplicationException(); } pendingInstructionsOffset.Add(addedOffset); } } Console.WriteLine( "Decoding '{0}' method bytecode on {1} bytes starting at 0x{2:X8}.", Helpers.BuildMethodDeclarationString(method), byteCode.Length, method.ByteCodeRawAddress); while (0 < pendingInstructionsOffset.Count) { uint opCodeIndex = pendingInstructionsOffset[0]; pendingInstructionsOffset.RemoveAt(0); bool fallInSequence = true; while (fallInSequence) { // Avoid targeting twice a single instruction. if (null != result[opCodeIndex]) { break; } DalvikInstruction newNode = OpCodeDecoder.Decode(byteCode, method.ByteCodeRawAddress, objectResolver, result, ref opCodeIndex); // Analyse possible targets after this instruction and augment // pending instructions list accordingly. fallInSequence = newNode.ContinueInSequence; uint[] otherTargetMethodOffsets = newNode.AdditionalTargetMethodOffsets; if (null != otherTargetMethodOffsets) { for(int index = 0; index < otherTargetMethodOffsets.Length; index++) { uint targetOffset = otherTargetMethodOffsets[index]; if (targetOffset >= byteCode.Length) { throw new ApplicationException(); } if (!pendingInstructionsOffset.Contains(targetOffset) && (null == result[targetOffset])) { pendingInstructionsOffset.Add(targetOffset); } } } } } return result; }
/// <summary>This method decodes additional content associated with instructions /// haing a "31t" format identifier.</summary> /// <param name="opCodes"></param> /// <param name="index"></param> /// <param name="baseOffset">Offset within method code of the opcode that is referencing /// this additional content. This is meaningfull only for switch instructions in order to /// compute targets offset.</param> /// <returns>Depending on the kind of additional content this is either : /// 1°) an array of bytes to be used for array initialization /// 2°) a dictionnary of method related offsets to jump locations keyed by the switch /// matching value for packed switch management.</returns> private static object DecodeAdditionalContent(byte[] opCodes, uint index, DalvikInstruction[] instructions, uint baseOffset) { uint initialIndexValue = index; // Additional content must be aligned on a doubleword boundary. We use a little // trick to attempt to discover a nop opCode that may exist prior to the additional // content. if ((sizeof(ushort) <= index) && (0 == opCodes[index - 1]) && (0 == opCodes[index - 2])) { // Found a nop. At least consider this as a covered word. // TODO : Consider creating a Nop instruction instance. instructions[initialIndexValue] = null; } byte opCode = opCodes[index++]; byte codeArg = opCodes[index++]; // Initial value accounts for opCode and codeArg. Later augmented depending on the // kind of codeArg uint coveredWordsCount = 1; if (0 != opCode) { throw new REException(); } uint dataSize; int[] keys; Dictionary<int, uint> targetMethodOffsets; switch (codeArg) { case 0x01: dataSize = GetNextDecodedUInt16(opCodes, ref index); targetMethodOffsets = new Dictionary<int,uint>(); int firstKey = GetNextDecodedInt32(opCodes, ref index); coveredWordsCount += (sizeof(ushort) + sizeof(int) + (sizeof(uint) * dataSize)) / sizeof(ushort); for (int targetIndex = 0; targetIndex < dataSize; targetIndex++) { // WARNING : The retrieved double word is a word count not a byte count. uint targetMethodOffset = (uint)(baseOffset + (sizeof(ushort) * GetNextDecodedInt32(opCodes, ref index))); targetMethodOffsets[targetIndex + firstKey] = targetMethodOffset; } return targetMethodOffsets; case 0x02: dataSize = GetNextDecodedUInt16(opCodes, ref index); keys = new int[dataSize]; targetMethodOffsets = new Dictionary<int,uint>(); coveredWordsCount += (sizeof(ushort) + ((sizeof(int) + sizeof(uint)) * dataSize)) / sizeof(ushort); for (int targetIndex = 0; targetIndex < dataSize; targetIndex++) { int key = GetNextDecodedInt32(opCodes, ref index); // WARNING : The retrieved double word is a word count not a byte count. uint targetMethodOffset = (uint)(baseOffset + (sizeof(ushort) * (GetNextDecodedUInt32(opCodes, ref index)))); targetMethodOffsets[key] = targetMethodOffset; } return targetMethodOffsets; case 0x03: ushort elementSize = GetNextDecodedUInt16(opCodes, ref index); uint elementsCount = GetNextDecodedUInt32(opCodes, ref index); uint rawDataSize = elementsCount * elementSize; coveredWordsCount += (sizeof(ushort) + sizeof(uint) + (rawDataSize + 1)) / 2; byte[] initializationData = new byte[(int)rawDataSize]; Buffer.BlockCopy(opCodes, (int)index, initializationData, 0, initializationData.Length); return initializationData; default: throw new REException(); } }
/// <summary>Decode a single instruction into an <see cref="DalvikInstruction"/> /// having the given parent.</summary> /// <param name="opCodes">The method body being decoded.</param> /// <param name="baseAddress">The base address of the method body. Actually this /// is the offset within the dex file where the method body starts.</param> /// <param name="objectResolver"></param> /// <param name="coveredWord">An array of boolean that tell which of the opCode /// word are already disassembled.</param> /// <param name="index">The index of the first byte of the instruction being /// decoded.</param> /// <returns></returns> internal static DalvikInstruction Decode(byte[] opCodes, uint baseAddress, IResolver objectResolver, DalvikInstruction[] instructions, ref uint index) { if (null != instructions[index]) { throw new ApplicationException(); } uint initialIndexValue = index; uint thisAddress = (uint)(baseAddress + index); string address = string.Format("// {0:X8} : ", thisAddress); byte opCode = opCodes[index]; byte codeArg = opCodes[index + 1]; index += 2; StringBuilder extraArgBuilder; string assemblyCode; object additionalContent = null; // TODO : With big endian the opcode would be the second byte from the // current word. OpCodeDecoder decoder = _decoderPerOpCode[opCode]; // Filter out unused instructions. if (null == decoder._opCodeName) { throw new REException(); } StringBuilder resultBuilder = new StringBuilder(); List<object> printArgs = new List<object>(); int argsCount; ushort resolverIndex; ushort extraWord; uint exclusion; long literalOrAddress = 0; ushort[] registers = null; DalvikInstruction result = null; try { switch (decoder._formatId) { case "10t": // GOTO : special case we compute the target address. literalOrAddress = (int)((sizeof(ushort) * (sbyte)codeArg)); printArgs.Add(string.Format("{0:X8}", literalOrAddress)); break; case "10x": if (0 != codeArg) { throw new REException(); } break; case "11n": registers = new ushort[] { (ushort)(codeArg & 0x0F) }; printArgs.Add(registers[0]); literalOrAddress = ((codeArg & 0xF0) >> 4); printArgs.Add(literalOrAddress); break; case "11x": registers = new ushort[] { (ushort)codeArg }; printArgs.Add(registers[0]); break; case "12x": registers = new ushort[] { (ushort)(codeArg & 0x0F), (ushort)((codeArg & 0xF0) >> 4) }; printArgs.Add(registers[0]); printArgs.Add(registers[1]); break; case "20bc": // TODO : Definition is unclear. throw new NotSupportedException(); case "20t": if (0 != codeArg) { throw new REException(); } // GOTO/16 : special case we compute the target address. literalOrAddress = (uint)((sizeof(ushort) * (short)GetNextDecodedUInt16(opCodes, ref index))); printArgs.Add(string.Format("{0:X8}", literalOrAddress)); break; case "21c": resolverIndex = GetNextDecodedUInt16(opCodes, ref index); registers = new ushort[] { (ushort)codeArg }; printArgs.Add(registers[0]); // TODO : Must add an additional field in InstructionAstNode for this case. printArgs.Add(decoder._argResolver(objectResolver, resolverIndex)); break; case "21h": registers = new ushort[] { (ushort)codeArg }; printArgs.Add(registers[0]); literalOrAddress = GetNextDecodedInt16(opCodes, ref index) << (decoder._isWide ? 48 : 16); printArgs.Add(literalOrAddress); break; case "21s": registers = new ushort[] { (ushort)codeArg }; printArgs.Add(registers[0]); literalOrAddress = GetNextDecodedInt16(opCodes, ref index); printArgs.Add(literalOrAddress); break; case "21t": registers = new ushort[] { (ushort)codeArg }; printArgs.Add(registers[0]); literalOrAddress = GetNextDecodedInt16(opCodes, ref index); printArgs.Add(literalOrAddress); break; case "22b": goto case "23x"; case "22c": resolverIndex = GetNextDecodedUInt16(opCodes, ref index); registers = new ushort[] { (ushort)(codeArg & 0x0F), (ushort)((codeArg & 0xF0) >> 4) }; printArgs.Add(registers[0]); printArgs.Add(registers[1]); // TODO : Must add an additional field in InstructionAstNode for this case. printArgs.Add(decoder._argResolver(objectResolver, resolverIndex)); break; case "22s": case "22t": registers = new ushort[] { (ushort)(codeArg & 0x0F), (ushort)((codeArg & 0xF0) >> 4) }; printArgs.Add(registers[0]); printArgs.Add(registers[1]); literalOrAddress = GetNextDecodedInt16(opCodes, ref index); printArgs.Add(literalOrAddress); break; case "22x": registers = new ushort[] { (ushort)codeArg, GetNextDecodedUInt16(opCodes, ref index) }; printArgs.Add(registers[0]); printArgs.Add(registers[1]); break; case "23x": extraWord = GetNextDecodedUInt16(opCodes, ref index); registers = new ushort[] { (ushort)codeArg, (ushort)(extraWord & 0x00FF), (ushort)((extraWord & 0xFF00) >> 8) }; printArgs.Add(registers[0]); printArgs.Add(registers[1]); printArgs.Add(registers[2]); break; case "30t": // GOTO/32 : special case we compute the target address. literalOrAddress = ((sizeof(ushort) * (int)GetNextDecodedInt32(opCodes, ref index))); printArgs.Add(string.Format("{0:X8}", literalOrAddress)); break; case "31i": registers = new ushort[] { (ushort)codeArg }; printArgs.Add(registers[0]); literalOrAddress = GetNextDecodedInt32(opCodes, ref index); printArgs.Add(literalOrAddress); break; case "31t": registers = new ushort[] { (ushort)codeArg }; printArgs.Add(registers[0]); // fill-array-data, packed-switch, sparse-switch // Special case we compute the target address. literalOrAddress = exclusion = (uint)(thisAddress + (sizeof(ushort) * (short)GetNextDecodedInt32(opCodes, ref index))); additionalContent = DecodeAdditionalContent(opCodes, (uint)(literalOrAddress - baseAddress), instructions, initialIndexValue); printArgs.Add(string.Format("{0:X8}", literalOrAddress)); break; case "32x": registers = new ushort[] { GetNextDecodedUInt16(opCodes, ref index), GetNextDecodedUInt16(opCodes, ref index) }; printArgs.Add(registers[0]); printArgs.Add(registers[1]); break; case "3rc": literalOrAddress = resolverIndex = GetNextDecodedUInt16(opCodes, ref index); extraWord = GetNextDecodedUInt16(opCodes, ref index); extraArgBuilder = new StringBuilder(); extraArgBuilder.AppendFormat("{{v{0} .. v{1}}}", extraWord, (ushort)(extraWord + codeArg - 1)); printArgs.Add(extraArgBuilder.ToString()); printArgs.Add(decoder._argResolver(objectResolver, resolverIndex)); break; case "35c": literalOrAddress = resolverIndex = GetNextDecodedUInt16(opCodes, ref index); argsCount = (codeArg & 0xF0) >> 4; if (0 < argsCount) { registers = new ushort[argsCount]; } extraWord = (0 == argsCount) ? (ushort)0 : GetNextDecodedUInt16(opCodes, ref index); extraArgBuilder = new StringBuilder(); switch (argsCount) { case 0: break; case 5: registers[4] = (ushort)((codeArg & 0x0F00) >> 8); extraArgBuilder.Insert(0, " ,v" + registers[4].ToString()); goto case 4; case 4: registers[3] = (ushort)((extraWord & 0xF000) >> 12); extraArgBuilder.Insert(0, " ,v" + registers[3].ToString()); goto case 3; case 3: registers[2] = (ushort)((extraWord & 0x0F00) >> 8); extraArgBuilder.Insert(0, " ,v" + registers[2].ToString()); goto case 2; case 2: registers[1] = (ushort)((extraWord & 0x00F0) >> 4); extraArgBuilder.Insert(0, " ,v" + registers[1].ToString()); goto case 1; case 1: registers[0] = (ushort)(extraWord & 0x000F); extraArgBuilder.Insert(0, "v" + registers[0].ToString()); break; default: throw new REException(); } extraArgBuilder.Insert(0, "{"); extraArgBuilder.Append("}"); printArgs.Add(extraArgBuilder.ToString()); printArgs.Add(decoder._argResolver(objectResolver, resolverIndex)); break; case "51l": registers = new ushort[] { (ushort)codeArg }; printArgs.Add(registers[0]); literalOrAddress = GetNextDecodedInt64(opCodes, ref index); printArgs.Add(literalOrAddress); break; default: throw new ApplicationException(); } printArgs.Insert(0, decoder._opCodeName); try { resultBuilder.AppendFormat(decoder._sourceCodeFormatString, printArgs.ToArray()); } catch { throw; } assemblyCode = address + resultBuilder.ToString(); result = DalvikInstruction.Create(decoder._astNodeType, initialIndexValue, (uint)(index - initialIndexValue), assemblyCode); result.LiteralOrAddress = literalOrAddress; result.Registers = registers; if (null != additionalContent) { result.SetAdditionalContent(additionalContent); } return result; } finally { instructions[initialIndexValue] = result; } }
/// <summary>Split the current node in two separate nodes reltively to the given /// method relative offset. Existing block is left with its orginal predecessors, /// newly create block inherit the successors of the original block and both blocks /// (old and new) are linked together.</summary> /// <param name="methodOffset">The offset within the owning method of the instruction /// that must be migrated to the new node.</param> /// <returns>The newly created block. This block is already linked as a successor of /// the block that has been split.</returns> internal BlockNode Split(uint methodOffset) { int splitIndex; #if DBGCFG if (DebugEnabled) { DalvikInstruction lastInstruction = _instructions[_instructions.Count - 1]; Console.WriteLine("[{0}] ({1:X4}|{2:X4})/{3:X4}", NodeId, _instructions[0].MethodRelativeOffset, lastInstruction.MethodRelativeOffset + lastInstruction.InstructionSize - 1, methodOffset); } #endif // Find index of instruction with method relative offset matching the // given method offset. for (splitIndex = 0; splitIndex < _instructions.Count; splitIndex++) { if (_instructions[splitIndex].MethodRelativeOffset == methodOffset) { break; } } if (splitIndex >= _instructions.Count) { throw new ArgumentException(); } int movedRangeSize = _instructions.Count - splitIndex; DalvikInstruction[] movedRange = new DalvikInstruction[movedRangeSize]; this._instructions.CopyTo(splitIndex, movedRange, 0, movedRange.Length); BlockNode result = new BlockNode(this.DebugEnabled); result._instructions = new List<DalvikInstruction>(movedRange); this._instructions.RemoveRange(splitIndex, movedRangeSize); // Also adjust block size for both blocks. uint sizeDelta = 0; foreach (DalvikInstruction movedIndstruction in result._instructions) { sizeDelta += movedIndstruction.InstructionSize; } this._size -= sizeDelta; result._size = sizeDelta; // We must also transfer existing successors from the splited block to the // newly created one. base.TransferSuccessors(result); // This link MUST occur after successors transfer. CfgNode.Link(this, result); return result; }