编译 X86/X64 的程序集以与 C# 一起使用

发布于 2024-08-09 16:04:55 字数 288 浏览 6 评论 0 原文

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

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

因此,我们非常欢迎任何帮助!

I would like to add cpuid functionality to my C# app. I found this interesting blog post online. I will probably need MASM to compile this but:

  1. How should I start?
  2. I suspect that I will have to compile a dll for both X86 and X64 but again I don't have a clue about how to go about such a thing (and I am a bit pressed for time).

So any help would be more than welcome!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

感受沵的脚步 2024-08-16 16:04:55

CPUID 是一个巨大的痛苦,如果可以避免的话,我建议不要走这条路。 Intel 和 AMD 处理器之间的 CPUID 结果不同(至少对于超线程和缓存拓扑等有趣的东西而言),并且在不同处理器版本之间并不是特别稳定。 (较新的 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;
}

CPUID is a giant pain and I would advise against going down that path if you can avoid it. CPUID results are different between Intel and AMD processors (at least for the interesting stuff like hyperthreading and cache topology) and are not particularly stable across different processor versions. (Newer Intel i7 processors introduce a new CPUID value (eax=0xb) which supercedes the information supported by CPUID on earlier processors).

If you can get away with it, your best bet would be to use WMI (see Win32_Processor) or GetLogicalProcessorInformation.

Either of these will be a vastly simpler and more manageable solution if they are supported on your platform (to get logical processor information either way requires WinXP sp3 or newer on the client side or Windows Server 2008 or newer on the server side).

If you really want to try your luck with CPUID, what I would recommend doing would be to create a simple stub which is capable of executing CPUID and returning the results to managed code (you will need different versions for 32-bit and 64-bit) and execute those within the context of your managed application. I do this by compiling a native application and then extracting the raw instruction bytes of my CPUID method into a byte array which can be executed from managed code.

This should get you started for 32-bit support only:

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;
}
锦爱 2024-08-16 16:04:55

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

I think you can build a native CLR assembly employing inline assembler syntax (__asm) in C++ code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文