File: Compiler\Disassembler.cs
Web Access
Project: src\src\runtime\src\coreclr\tools\aot\ILCompiler.Compiler\ILCompiler.Compiler.csproj (ILCompiler.Compiler)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

using ILCompiler.DependencyAnalysis;
using Internal.TypeSystem;

namespace ILCompiler
{
    /// <summary>
    /// Disassembler based on CoreDisTools. Only available in Debug builds.
    /// </summary>
    public class Disassembler
    {
        public static string Disassemble(TargetArchitecture arch, byte[] bytes, Relocation[] relocs)
        {
            var sb = new StringBuilder();

            // The coredistools library is not available in release builds because
            // we don't want to ship yet another huge LLVM-based DLL.
#if DEBUG
            SortedList<int, Relocation> sortedRelocs = new SortedList<int, Relocation>(relocs.Length);
            foreach (Relocation reloc in relocs)
                sortedRelocs.Add(reloc.Offset, reloc);

            int relocIndex = 0;

            using (var disasm = new CoreDisassembler(arch))
            {
                int offset = 0;
                while (offset < bytes.Length)
                {
                    int size = disasm.Disassemble(bytes, offset, out string dis);
                    if (size == 0)
                        break;
                    offset += size;

                    // Drop the annoying `\n`
                    dis = dis.Substring(0, dis.Length - 1);

                    sb.Append(dis);

                    if (relocIndex < sortedRelocs.Count)
                    {
                        Relocation currentReloc = sortedRelocs.Values[relocIndex];
                        if (currentReloc.Offset < offset)
                        {
                            sb.Append(" // ");
                            sb.Append(currentReloc.Target.ToString());
                            relocIndex++;
                        }
                    }

                    sb.AppendLine();
                }
            }
#else
            sb.AppendLine("// CoreDisTools not available in release builds");
#endif
            return sb.ToString();
        }

        private sealed class CoreDisassembler : IDisposable
        {
            private IntPtr _handle;

            private const string Library = "coredistools";

            private enum TargetArch
            {
                Target_X86 = 1,
                Target_X64,
                Target_Thumb,
                Target_Arm64
            };

            [DllImport(Library)]
            private static extern IntPtr InitBufferedDisasm(TargetArch Target);

            public CoreDisassembler(TargetArchitecture arch)
            {
                _handle = InitBufferedDisasm(arch switch
                {
                    TargetArchitecture.X86 => TargetArch.Target_X86,
                    TargetArchitecture.X64 => TargetArch.Target_X64,
                    TargetArchitecture.ARM => TargetArch.Target_Thumb,
                    TargetArchitecture.ARM64 => TargetArch.Target_Arm64,
                    _ => throw new NotSupportedException()
                });

                if (_handle == IntPtr.Zero)
                    throw new OutOfMemoryException();
            }

            [DllImport(Library)]
            private static extern int DumpInstruction(IntPtr handle, ulong address, IntPtr bytes, int size);

            public unsafe int Disassemble(byte[] bytes, int offset, out string instruction)
            {
                int size;
                fixed (byte* pByte = &bytes[offset])
                {
                    size = DumpInstruction(_handle, (ulong)offset, (IntPtr)pByte, bytes.Length - offset);
                }

                instruction = Marshal.PtrToStringUTF8(GetOutputBuffer());
                ClearOutputBuffer();
                return size;
            }

            [DllImport(Library)]
            private static extern IntPtr GetOutputBuffer();

            [DllImport(Library)]
            private static extern void ClearOutputBuffer();

            [DllImport(Library)]
            private static extern void FinishDisasm(IntPtr handle);

            public void Dispose()
            {
                DisposeThis();
                GC.SuppressFinalize(this);
            }

            private void DisposeThis()
            {
                FinishDisasm(_handle);
                _handle = IntPtr.Zero;
            }

            ~CoreDisassembler()
            {
                DisposeThis();
            }
        }
    }
}