【问题标题】:Creating a Hello World library function in assembly and calling it from C#在程序集中创建一个 Hello World 库函数并从 C# 调用它
【发布时间】:2010-05-18 19:53:56
【问题描述】:

假设我们像他们在这个答案中那样使用 NASM:how to write hellow world in assembly under windows

我有一些关于汇编与 c# 或任何其他 .net 语言相结合的想法和问题。

首先,我希望能够创建一个库,该库具有以下函数HelloWorld,该函数采用此参数:

  • 姓名

在 C# 中,方法签名看起来像这样:void HelloWorld(string name),它会打印出类似

来自名称的Hello World

我搜索了一下,但找不到那么多好的和干净的材料让我开始。我知道一些以前的基本程序集,主要是gasthough。

因此,任何指向正确方向的指针都非常受欢迎。

总结一下

  • 在 ASM (NASM) 中创建一个采用一个或多个参数的例程
  • 编译并创建上述功能的库
  • 包含任何 .net 语言的库
  • 调用包含的库函数

奖励功能

  • 如何处理返回值?
  • 是否可以内联编写 ASM 方法?

在汇编或 c 中创建库时,您确实遵循某种“预定义”方式,即 c 调用约定,对吗?

【问题讨论】:

    标签: c# .net vb.net assembly inline-assembly


    【解决方案1】:

    这样的东西应该可以让你得到一个工作的 DLL:

    extern _printf
    
    section .text
    global _hello
    _hello:
        push ebp
        mov ebp, esp
    
        mov eax, [ebp+12]
        push eax
        push helloWorld
        call _printf
        add esp, 8
        pop ebp
        ret
    
    export _hello
    
    helloWorld: db 'Hello world from %s', 10, 0
    

    然后您只需要使用 P/Invoke 调用“hello”函数。它不会自行清理,因此您需要将 CallingConvention 设置为 Cdecl;您还需要告诉它您使用的是 ANSI 字符串。未经测试,但应该可以正常工作。

    using System.Runtime.InteropServices;
    
    namespace Test {
        public class Test {
            public static Main() {
                Hello("C#");
            }
    
            [DllImport("test.dll", EntryPoint="hello", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)]
            public static extern Hello(string from_);
        }
    }
    

    【讨论】:

    • Cdecl 将返回值存储在eax 中,对吗?所以调整 ASM 以返回字符串而不是 (作弊 ;) ) 调用 printf 会相当容易?
    • 当然,你只需要为字符串分配内存,将传入的值与静态字符串连接起来,然后在eax中返回那个值。
    【解决方案2】:

    我将首先介绍做你想做的事情的基本准则。然后我将尝试解释如何做 Hello World 示例。

    第一步是编写一个函数,并从中生成 dll。汇编中的函数应遵循其中一种调用标准,stdcall 是最常见的一种(您可以阅读更多关于 stdcall 和其他调用标准的信息here) 可以找到使用 asm 创建 dll 的示例here

    第二步是使用 P/Invoke 以托管语言导入方法。你可以阅读更多关于它here

    就是这样。。

    现在对于您提到的示例,您将需要创建一个 asm 函数,该函数接受所有输入参数并将其传递给其他知道如何打印到标准输出的函数(如上所述来自标准 c lib 的 printf,或使用 winapi像here这样的电话)

    之后,您需要将 dll 导入 C#,如上所述。您应该特别注意的是字符串的字符编码和数据的清理。我提供的第三个链接(编组文本部分)中都提到了它们。

    奖金部分:

    • 返回值的处理取决于 关于使用的调用约定
    • 不能用 C# 编写内联汇编。但是,您可以使用托管 C++ 编写它们。

    【讨论】:

      【解决方案3】:

      这是一个非常好的问题,值得我投票。关于您的问题,正如 Cody 指出他的汇编程序例程那样,这样做的唯一方法是基于此构建一个 DLL,然后从那里使用 p/Invoke 使用诸如

      之类的互操作 [DllImport("mylib.dll")]

      等等。然而,不幸的是,由于 C# 或任何其他语言的性质,CLR 运行时环境正在使用托管代码。让 CLR 或汇编器设置堆栈帧、寄存器,然后从本地世界交叉跳​​转到托管世界会很尴尬。那将是一项艰巨的工作,但是如果有人看到过这种事情,请在我的答案末尾发表评论,我会相应地修改此答案。

      因此,据我所知,在这种情况下,汇编程序无法使用 eax、ebx、堆栈帧等系统寄存器将汇编程序例程内联到 CLR 代码中,就像上面的科迪在他的回答中提供的代码。在这种情况下,对于 C/C++ 来说也是如此:

      无效富(无效){ _asm{ 异或 ecx, ecx 移动 eax, 1 …… } }

      从 CLR 的角度来看,这样做会很好,以便进一步优化代码,至少在理论上是这样,但对于 CLR 来说,实际托管这样的事情是一项不可逾越的工作,有一个例外...可以使用可以执行此操作的托管 C++ 编译器来完成,但是 NOT 无论如何来自 VB.NET/C#:

      私人无效mycsfunction(字符串s){ // 托管代码 StringBuilder sb = new StringBuilder(s); …… _asm{ 推送ebp mov ebp, esp lea edx, offset sb …… 移动 eax, 1 流行音乐 } }

      然而,在这个主题上,可以将 IL 代码生成到本地汇编程序,现在,当我写这篇文章时,我不能 100% 确定这是否可以反过来完成,请参阅 here 以获得详细说明这是如何发生的

      但是然而,处理 CLR 汇编的唯一方法是手写 IL 代码并改为编译它,即 CLR 运行时的汇编器,直接,方式与native assembler 可以被 native 调用(读取 NON-CLR 二进制文件),请参阅这篇文章 here 了解如何完成此操作。

      【讨论】:

      • 有趣.. 我想知道 inline-asm 在托管 c++ 中是否有可能?
      • 你确实可以在 C++/CLI 中使用内联汇编,虽然 /when/ 你可以使用它很复杂。我用它来修补我使用 EasyHook 注入的二进制文件中的几个函数,但是混合内联汇编和托管代码很困难。除非您别无选择,否则我强烈建议您反对。另一方面,有一个库允许您将本机代码嵌入到 MS.NET 的 JITC 代码中,但我记不起名字了。
      【解决方案4】:

      在我看来,您的问题有两个方面:

      1. 如何使用汇编编写函数并将其放入 DLL 中?
      2. 如何从托管 C# 调用非托管 DLL 中的函数?

      2 的答案是使用 P/Invoke。有很多关于该主题的文档和教程,例如http://msdn.microsoft.com/en-us/magazine/cc164123.aspx

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-14
        • 1970-01-01
        • 2022-01-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多