【问题标题】:Compiling assembly for X86/X64 for use with C#为 X86/X64 编译程序集以与 C# 一起使用
【发布时间】:2009-11-03 19:49:49
【问题描述】:

我想将 cpuid 功能添加到我的 C# 应用程序中。我在网上找到了this 有趣的博文。我可能需要 MASM 来编译它,但是:

  1. 我应该如何开始?
  2. 我怀疑我将不得不为 X86 和 X64 编译一个 dll,但我还是不知道如何去做这样的事情(而且我有点时间紧迫)。

所以任何帮助都会受到欢迎!

【问题讨论】:

    标签: c# assembly x86-64 masm


    【解决方案1】:

    CPUID 是一个巨大的痛苦,如果可以避免的话,我建议不要走这条路。 CPUID 结果在 Intel 和 AMD 处理器之间是不同的(至少对于像超线程和缓存拓扑这样的有趣的东西)并且在不同的处理器版本之间不是特别稳定。 (较新的 Intel i7 处理器引入了一个新的 CPUID 值 (eax=0xb),它取代了早期处理器上 CPUID 支持的信息)。

    如果你能侥幸成功,最好的办法是使用 WMI(参见 Win32_Processor)或 GetLogicalProcessorInformation

    如果您的平台支持这两种解决方案,它们都将是一个非常简单且更易于管理的解决方案(要获取逻辑处理器信息,客户端需要 WinXP sp3 或更高版本,或者服务器端需要 Windows Server 2008 或更高版本) .

    如果您真的想用 CPUID 试试运气,我建议您创建一个能够执行 CPUID 并将结果返回到托管代码的简单存根(32 位和64 位)并在托管应用程序的上下文中执行这些操作。为此,我编译了一个原生应用程序,然后将我的 CPUID 方法的原始指令字节提取到一个字节数组中,该数组可以从托管代码中执行。

    这应该让您开始仅支持 32 位:

    using System;
    using System.Runtime.InteropServices;
    
    static class Program {
      static void Main() {
        //Allocate the executable buffer on a distinct page 
        // rather than just pinning it in place because we
        // need to mark the page as executable.
        // Failing to do this would cause NX-enabled machines 
        // to have access violations attempting to execute.
        IntPtr pExecutableBuffer = VirtualAlloc(
          IntPtr.Zero,
          new IntPtr(CPUID_32.Length),
          AllocationType.MEM_COMMIT | AllocationType.MEM_RESERVE,
          MemoryProtection.PAGE_EXECUTE_READWRITE
        );
    
        Marshal.Copy(CPUID_32, 0, pExecutableBuffer, CPUID_32.Length);
        CPUID executeHandler = (CPUID)Marshal.GetDelegateForFunctionPointer(
          pExecutableBuffer, typeof(CPUID));
        CPUID_Args args = new CPUID_Args();
        args.eax = 0;
        executeHandler(ref args);
        Console.WriteLine("eax: {0} ebx: {1} ecx: {2} edx: {3}",
          args.eax,
          args.ebx,
          args.ecx,
          args.edx);
        VirtualFree(
          pExecutableBuffer,
          IntPtr.Zero,
          FreeType.MEM_RELEASE);
      }
    
      [UnmanagedFunctionPointer(CallingConvention.StdCall)]
      delegate void CPUID(ref CPUID_Args args);
    
      private static readonly byte[] CPUID_32 = new byte[] {
        0x53,          // push ebx 
        0x57,          // push edi 
        0x8B, 0x7C, 0x24, 0x0C, // mov edi,dword ptr [esp+0Ch] 
        0x8B, 0x07,       // mov eax,dword ptr [edi] 
        0x8B, 0x4F, 0x08,    // mov ecx,dword ptr [edi+8] 
        0x0F, 0xA2,       // cpuid      
        0x89, 0x07,       // mov dword ptr [edi],eax 
        0x89, 0x5F, 0x04,    // mov dword ptr [edi+4],ebx 
        0x89, 0x4F, 0x08 ,    // movdword ptr [edi+8],ecx 
        0x89, 0x57, 0x0C ,    // mov dword ptr [edi+0Ch],edx 
        0x5F,          // pop     edi 
        0x5B,          // pop     ebx 
        0xC2, 0x04, 0x00     // ret
        };
    
      [Flags]
      enum AllocationType {
        MEM_COMMIT = 0x1000,
        MEM_RESERVE = 0x2000,
      }
    
      [Flags]
      enum MemoryProtection {
        PAGE_EXECUTE_READWRITE = 0x40,
      }
    
      [Flags]
      enum FreeType {
        MEM_RELEASE = 0x8000
      }
    
      [DllImport("kernel32.dll")]
      static extern IntPtr VirtualAlloc(
        IntPtr lpAddress,
        IntPtr dwSize,
        AllocationType flAllocationType,
        MemoryProtection flProtect);
    
      [DllImport("kernel32.dll")]
      [return: MarshalAs(UnmanagedType.Bool)]
      static extern bool VirtualFree(
        IntPtr lpAddress,
        IntPtr dwSize,
        FreeType dwFreeType);
    }
    
    [StructLayout(LayoutKind.Sequential)]
    struct CPUID_Args {
      public uint eax;
      public uint ebx;
      public uint ecx;
      public uint edx;
    }
    

    【讨论】:

    • 现在这是一个有用的答案 :) 您是第一个提到 32 位和 64 位之间可能存在真正区别的人。您能否就这些差异的所在提供一些额外的指导?您的总体策略似乎与此一致:devpinoy.org/blogs/cvega/archive/2006/04/07/2658.aspx您可能知道如何在任一位模式下正确编译它吗?
    • 我刚刚意识到您答案中的代码可能只能用于 32 位,因为 X64 位编译不允许内联 asm?
    • 该代码可以在运行 32 位操作系统的 64 位机器上运行(尽管这可能不是您要问的)。真正支持 64 位更复杂……首先,您必须决定是支持 IA64 还是只支持 x64/AMD64/无论您想怎么称呼它。然后,您将需要不同版本的字节数组(称为 CPUID_64)和代码,以确保在您想要调用 CPUID 时执行正确版本的字节数组。然而,这个例子本身并没有使用“内联汇编”。 x86 是动态加载和执行的,它不是二进制文件的可执行部分。
    • 呃,为什么总是要对这些事情如此复杂。人们会认为始终可以访问这些信息可能很有用......
    • 实际上我链接的代码在我的 Win7 X64 上运行得很好......如果我能弄清楚如何编译这个东西:D
    【解决方案2】:

    我认为您可以在 C++ 代码中使用内联汇编语法 (__asm) 构建本机 CLR 程序集。

    【讨论】:

    • 哦,我已经有一段时间没有在 windows 上使用 C#/C++ 了,那肯定是 win32。
    猜你喜欢
    • 2015-07-24
    • 1970-01-01
    • 2011-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-18
    • 2021-05-21
    • 1970-01-01
    相关资源
    最近更新 更多