public static Block[] DecodeFunction(MemoryManager memory, ulong address, ExecutionMode mode, bool highCq) { List <Block> blocks = new List <Block>(); Queue <Block> workQueue = new Queue <Block>(); Dictionary <ulong, Block> visited = new Dictionary <ulong, Block>(); int opsCount = 0; int instructionLimit = highCq ? MaxInstsPerFunction : MaxInstsPerFunctionLowCq; Block GetBlock(ulong blkAddress) { if (!visited.TryGetValue(blkAddress, out Block block)) { if (opsCount > instructionLimit || !memory.IsMapped((long)blkAddress)) { return(null); } block = new Block(blkAddress); workQueue.Enqueue(block); visited.Add(blkAddress, block); } return(block); } GetBlock(address); while (workQueue.TryDequeue(out Block currBlock)) { // Check if the current block is inside another block. if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex)) { Block nBlock = blocks[nBlkIndex]; if (nBlock.Address == currBlock.Address) { throw new InvalidOperationException("Found duplicate block address on the list."); } nBlock.Split(currBlock); blocks.Insert(nBlkIndex + 1, currBlock); continue; } // If we have a block after the current one, set the limit address. ulong limitAddress = ulong.MaxValue; if (nBlkIndex != blocks.Count) { Block nBlock = blocks[nBlkIndex]; int nextIndex = nBlkIndex + 1; if (nBlock.Address < currBlock.Address && nextIndex < blocks.Count) { limitAddress = blocks[nextIndex].Address; } else if (nBlock.Address > currBlock.Address) { limitAddress = blocks[nBlkIndex].Address; } } FillBlock(memory, mode, currBlock, limitAddress); opsCount += currBlock.OpCodes.Count; if (currBlock.OpCodes.Count != 0) { // Set child blocks. "Branch" is the block the branch instruction // points to (when taken), "Next" is the block at the next address, // executed when the branch is not taken. For Unconditional Branches // (except BL/BLR that are sub calls) or end of executable, Next is null. OpCode lastOp = currBlock.GetLastOp(); bool isCall = IsCall(lastOp); if (lastOp is IOpCodeBImm op && !isCall) { currBlock.Branch = GetBlock((ulong)op.Immediate); } if (!IsUnconditionalBranch(lastOp) || isCall) { currBlock.Next = GetBlock(currBlock.EndAddress); } } // Insert the new block on the list (sorted by address). if (blocks.Count != 0) { Block nBlock = blocks[nBlkIndex]; blocks.Insert(nBlkIndex + (nBlock.Address < currBlock.Address ? 1 : 0), currBlock); } else { blocks.Add(currBlock); } } TailCallRemover.RunPass(address, blocks); return(blocks.ToArray()); }
// ブロック単位のデコード を繰り返して複数のブロックを返す // // Cq = Code quality public static Block[] Decode(IMemoryManager memory, ulong address, ExecutionMode mode, bool highCq, bool singleBlock) { // デコード処理したブロックが入っている これを返す List <Block> blocks = new List <Block>(); // デコード処理をまっているブロックを入れておくキュー Queue <Block> workQueue = new Queue <Block>(); Dictionary <ulong, Block> visited = new Dictionary <ulong, Block>(); Debug.Assert(MaxInstsPerFunctionLowCq <= MaxInstsPerFunction); int opsCount = 0; int instructionLimit = highCq ? MaxInstsPerFunction : MaxInstsPerFunctionLowCq; // blkAddressを先頭とするBlock構造体を返す // すでにBlock構造体を構築しているならそれを、そうでないなら新しく作ってworkQueueにpushしたあと返す Block GetBlock(ulong blkAddress) { if (!visited.TryGetValue(blkAddress, out Block block)) { block = new Block(blkAddress); if ((singleBlock && visited.Count >= 1) || opsCount > instructionLimit || !memory.IsMapped(blkAddress)) { block.Exit = true; block.EndAddress = blkAddress; } workQueue.Enqueue(block); visited.Add(blkAddress, block); } return(block); } GetBlock(address); // キューに入ったブロックを順に処理していく while (workQueue.TryDequeue(out Block currBlock)) { // 処理対象のブロックがとあるブロックの内側に含まれている // blocks[nBlkIndex].Address <= currBlock.Address < blocks[nBlkIndex].EndAddress // currBlock.EndAddress <= blocks[nBlkIndex].EndAddress は成り立っているのか? if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex)) { Block nBlock = blocks[nBlkIndex]; if (nBlock.Address == currBlock.Address) { throw new InvalidOperationException("Found duplicate block address on the list."); } currBlock.Exit = false; // ブロックの終端ではない // nBlock を分割して間にcurrBlockを差し込む nBlock.Split(currBlock); blocks.Insert(nBlkIndex + 1, currBlock); continue; } // NOTE: nBlkIndex = 新しいブロックが入るべきblocksのインデックス // Exit=false つまり現在のブロックの後にブロックがある場合は、リミットアドレスを設定する if (!currBlock.Exit) { ulong limitAddress = ulong.MaxValue; if (nBlkIndex != blocks.Count) // 念の為処理対象のブロックがblocksの最後のブロックでないか確認 { Block nBlock = blocks[nBlkIndex]; int nextIndex = nBlkIndex + 1; if (nBlock.Address < currBlock.Address && nextIndex < blocks.Count) { // nBlock <- currBlock <- blocks[nextIndex] の場合は blocks[nextIndex] まで limitAddress = blocks[nextIndex].Address; } else if (nBlock.Address > currBlock.Address) { // 新しいブロック -> nBlock の場合は nBlockまで limitAddress = blocks[nBlkIndex].Address; } } FillBlock(memory, mode, currBlock, limitAddress); opsCount += currBlock.OpCodes.Count; if (currBlock.OpCodes.Count != 0) { // 分岐命令だった場合に子ブロックを設定する // "Branch "は、分岐命令が指すブロック、"Next"は、分岐しなかった場合に実行される次のアドレスのブロックです // 無条件分岐(関数コールであるBL/BLRを除く)や実行ファイルの終了の場合、Nextはnullです。 OpCode lastOp = currBlock.GetLastOp(); bool isCall = IsCall(lastOp); if (lastOp is IOpCodeBImm op && !isCall) { currBlock.Branch = GetBlock((ulong)op.Immediate); } if (!IsUnconditionalBranch(lastOp) || isCall) { currBlock.Next = GetBlock(currBlock.EndAddress); } } } // Insert the new block on the list (sorted by address). if (blocks.Count != 0) { Block nBlock = blocks[nBlkIndex]; blocks.Insert(nBlkIndex + (nBlock.Address < currBlock.Address ? 1 : 0), currBlock); } else { blocks.Add(currBlock); } } if (blocks.Count == 1 && blocks[0].OpCodes.Count == 0) { Debug.Assert(blocks[0].Exit); Debug.Assert(blocks[0].Address == blocks[0].EndAddress); throw new InvalidOperationException($"Decoded a single empty exit block. Entry point = 0x{address:X}."); } if (!singleBlock) { return(TailCallRemover.RunPass(address, blocks)); } else { return(blocks.ToArray()); } }