public IEnumerable <StatementInfo> GetStatementLines(DisasmInfo method, int ilOffset) { var instrs = GetMetadataMethod(method)?.Body?.Instructions; if (instrs is null) { yield break; } var instr = GetInstruction(instrs, (uint)ilOffset); var seqPoint = instr?.SequencePoint; if (seqPoint is null) { yield break; } const int HIDDEN = 0xFEEFEE; if (seqPoint.StartLine == HIDDEN || seqPoint.EndLine == HIDDEN) { yield break; } foreach (var info in sourceDocumentProvider.GetLines(seqPoint.Document.Url, seqPoint.StartLine, seqPoint.StartColumn, seqPoint.EndLine, seqPoint.EndColumn)) { yield return(new StatementInfo(info.line, info.span, info.partial)); } }
MethodDef?GetMetadataMethod(DisasmInfo method) { if (!StringComparer.OrdinalIgnoreCase.Equals(lastModule?.Location, method.ModuleFilename)) { lastModule = metadataProvider.GetModule(method.ModuleFilename); lastMethod = null; } if (lastModule?.PdbState is null) { return(null); } if (lastMethod?.MDToken.Raw != method.MethodToken) { lastMethod = lastModule?.ResolveToken(method.MethodToken) as MethodDef; } return(lastMethod); }
public void Disassemble(Formatter formatter, TextWriter output, DisasmInfo method) { formatterOutput.writer = output; targets.Clear(); sortedTargets.Clear(); bool upperCaseHex = formatter.Options.UpperCaseHex; output.Write(commentPrefix); output.WriteLine("================================================================================"); output.Write(commentPrefix); output.WriteLine(method.MethodFullName); uint codeSize = 0; foreach (var info in method.Code) { codeSize += (uint)info.Code.Length; } var codeSizeHexText = codeSize.ToString(upperCaseHex ? "X" : "x"); output.WriteLine($"{commentPrefix}{codeSize} (0x{codeSizeHexText}) bytes"); void Add(ulong address, TargetKind kind) { if (!targets.TryGetValue(address, out var addrInfo)) { targets[address] = new AddressInfo(kind); } else if (addrInfo.Kind < kind) { addrInfo.Kind = kind; } } if (method.Instructions.Count > 0) { Add(method.Instructions[0].IP, TargetKind.Unknown); } foreach (ref var instr in method.Instructions) { switch (instr.FlowControl) { case FlowControl.Next: case FlowControl.Interrupt: break; case FlowControl.UnconditionalBranch: Add(instr.NextIP, TargetKind.Unknown); if (instr.Op0Kind == OpKind.NearBranch16 || instr.Op0Kind == OpKind.NearBranch32 || instr.Op0Kind == OpKind.NearBranch64) { Add(instr.NearBranchTarget, TargetKind.Branch); } break; case FlowControl.ConditionalBranch: case FlowControl.XbeginXabortXend: if (instr.Op0Kind == OpKind.NearBranch16 || instr.Op0Kind == OpKind.NearBranch32 || instr.Op0Kind == OpKind.NearBranch64) { Add(instr.NearBranchTarget, TargetKind.Branch); } break; case FlowControl.Call: if (instr.Op0Kind == OpKind.NearBranch16 || instr.Op0Kind == OpKind.NearBranch32 || instr.Op0Kind == OpKind.NearBranch64) { Add(instr.NearBranchTarget, TargetKind.Call); } break; case FlowControl.IndirectBranch: Add(instr.NextIP, TargetKind.Unknown); // Unknown target break; case FlowControl.IndirectCall: // Unknown target break; case FlowControl.Return: case FlowControl.Exception: Add(instr.NextIP, TargetKind.Unknown); break; default: Debug.Fail($"Unknown flow control: {instr.FlowControl}"); break; } var baseReg = instr.MemoryBase; if (baseReg == Register.RIP || baseReg == Register.EIP) { int opCount = instr.OpCount; for (int i = 0; i < opCount; i++) { if (instr.GetOpKind(i) == OpKind.Memory) { if (method.Contains(instr.IPRelativeMemoryAddress)) { Add(instr.IPRelativeMemoryAddress, TargetKind.Branch); } break; } } } else if (instr.MemoryDisplSize >= 2) { ulong displ; switch (instr.MemoryDisplSize) { case 2: case 4: displ = instr.MemoryDisplacement; break; case 8: displ = (ulong)(int)instr.MemoryDisplacement; break; default: Debug.Fail($"Unknown mem displ size: {instr.MemoryDisplSize}"); goto case 8; } if (method.Contains(displ)) { Add(displ, TargetKind.Branch); } } } foreach (var map in method.ILMap) { if (targets.TryGetValue(map.nativeStartAddress, out var info)) { if (info.Kind < TargetKind.BlockStart && info.Kind != TargetKind.Unknown) { info.Kind = TargetKind.BlockStart; } } else { targets.Add(map.nativeStartAddress, info = new AddressInfo(TargetKind.Unknown)); } if (info.ILOffset < 0) { info.ILOffset = map.ilOffset; } } int labelIndex = 0, methodIndex = 0; string GetLabel(int index) => LABEL_PREFIX + index.ToString(); string GetFunc(int index) => FUNC_PREFIX + index.ToString(); foreach (var kv in targets) { if (method.Contains(kv.Key)) { sortedTargets.Add(kv); } } sortedTargets.Sort((a, b) => a.Key.CompareTo(b.Key)); foreach (var kv in sortedTargets) { var address = kv.Key; var info = kv.Value; switch (info.Kind) { case TargetKind.Unknown: info.Name = null; break; case TargetKind.Data: info.Name = GetLabel(labelIndex++); break; case TargetKind.BlockStart: case TargetKind.Branch: info.Name = GetLabel(labelIndex++); break; case TargetKind.Call: info.Name = GetFunc(methodIndex++); break; default: throw new InvalidOperationException(); } } foreach (ref var instr in method.Instructions) { ulong ip = instr.IP; if (targets.TryGetValue(ip, out var lblInfo)) { output.WriteLine(); if (!(lblInfo.Name is null)) { output.Write(lblInfo.Name); output.Write(':'); output.WriteLine(); } if (lblInfo.ILOffset >= 0) { if (ShowSourceCode) { foreach (var info in sourceCodeProvider.GetStatementLines(method, lblInfo.ILOffset)) { output.Write(commentPrefix); var line = info.Line; int column = commentPrefix.Length; WriteWithTabs(output, line, 0, line.Length, '\0', ref column); output.WriteLine(); if (info.Partial) { output.Write(commentPrefix); column = commentPrefix.Length; WriteWithTabs(output, line, 0, info.Span.Start, ' ', ref column); output.WriteLine(new string('^', info.Span.Length)); } } } } } if (ShowAddresses) { var address = FormatAddress(bitness, ip, upperCaseHex); output.Write(address); output.Write(" "); } else { output.Write(formatter.Options.TabSize > 0 ? "\t\t" : " "); } if (ShowHexBytes) { if (!method.TryGetCode(ip, out var nativeCode)) { throw new InvalidOperationException(); } var codeBytes = nativeCode.Code; int index = (int)(ip - nativeCode.IP); int instrLen = instr.ByteLength; for (int i = 0; i < instrLen; i++) { byte b = codeBytes[index + i]; output.Write(b.ToString(upperCaseHex ? "X2" : "x2")); } int missingBytes = HEXBYTES_COLUMN_BYTE_LENGTH - instrLen; for (int i = 0; i < missingBytes; i++) { output.Write(" "); } output.Write(" "); } formatter.Format(instr, formatterOutput); output.WriteLine(); } }