简体   繁体   English

在程序集中创建Hello World库函数并从C#调用它

[英]Creating a Hello World library function in assembly and calling it from C#

Let's say we use NASM as they do in this answer: how to write hellow world in assembly under windows . 假设我们在这个答案中使用NASM: 如何在windows下的汇编中编写hellow world

I got a couple of thoughts and questions regarding assembly combined with c# or any other .net languages for that matter. 关于汇编,我得到了一些关于c#或任何其他.net语言的想法和问题。

First of all I want to be able to create a library that has the following function HelloWorld that takes this parameter: 首先,我希望能够创建一个具有以下函数HelloWorld的库,该函数接受以下参数:

  • Name 名称

In C# the method signature would looke like this: void HelloWorld(string name) and it would print out something like 在C#中,方法签名会像这样解释: void HelloWorld(string name) ,它会打印出类似的东西

Hello World from name Hello World来自名字

I've searched around a bit but can't find that much good and clean material for this to get me started. 我已经搜索了一下但是找不到那么好的和干净的材料让我开始。 I know some basic assembly from before mostly gas though. 我知道一些基本的装配,然后大多是gas

So any pointers in the right direction is very much apprechiated. 因此,正确方向的任何指针都非常适用。

To sum it up 把它们加起来

  • Create a routine in ASM ( NASM ) that takes one or more parameters 在ASM(NASM)中创建一个带有一个或多个参数的例程
  • Compile and create a library of the above functionality 编译并创建上述功能的库
  • Include the library in any .net language 以任何.net语言包含库
  • Call the included library function 调用包含的库函数

Bonus features 奖金功能

  • How does one handle returned values? 如何处理返回值?
  • Is it possible to write the ASM-method inline? 是否可以内联编写ASM方法?

When creating libraries in assembly or c, you do follow a certain "pre defined" way, the c calling convetion, correct? 在程序集或c中创建库时,您确实遵循某种“预定义”方式,即c调用对话,是否正确?

Something like this should get you a working DLL: 像这样的东西应该让你一个工作的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

You then just need to call the 'hello' function using P/Invoke. 然后你只需要使用P / Invoke调用'hello'函数。 It doesn't clean up after itself, so you need to set CallingConvention to Cdecl; 它不会自行清理,所以你需要将CallingConvention设置为Cdecl; you also need to tell it you're using ANSI strings. 你还需要告诉它你正在使用ANSI字符串。 Untested, but it should work fine. 未经测试,但它应该工作正常。

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_);
    }
}

I will first cover basic guidelines for doing what you want.. then I will try to explain how to do the Hello World example. 我将首先介绍做你想做的基本指导。然后我将尝试解释如何做Hello World示例。

The first step would be writing a function, and making dll out of it. 第一步是编写一个函数,并从中创建dll。 Function in assembly should adhere to one of calling standards, stdcall is the most common one (you can read more about stdcall and other calling standards here ) An example of creating an dll with asm can be found here 程序集中的函数应该遵循一个调用标准,stdcall是最常见的一个(你可以在这里阅读更多关于stdcall和其他调用标准的内容)在这里可以找到用asm创建一个dll的例子

The second step would be to import the method in a managed language using P/Invoke. 第二步是使用P / Invoke以托管语言导入方法。 You can read more about it here 你可以在这里阅读更多相关信息

And that's it.. 就是这样......

Now for your mentioned example, you will need to make an asm function that takes all the input parameters and passes it to some other function that knows how to print to stdout (printf from standard c lib as mentioned above, or using winapi calls like here ) 现在,对于您提到的示例,您需要创建一个asm函数,它接受所有输入参数并将其传递给知道如何打印到stdout的其他函数(如上所述,从标准c lib中打印printf,或者使用像这里的 winapi调用)

After that you'll need to import the dll to C#, as described above. 之后,您需要将dll导入C#,如上所述。 Things you should take special care about are, character encoding for strings and clean up of data. 您应该特别注意的事项是字符串的字符编码和数据清理。 They are all mentioned in the 3rd link I provided (marshaling text section). 它们都在我提供的第3个链接中提到(编组文本部分)。

Bonus part: 奖金部分:

  • Handling of returned values depends on the calling conventions used 处理返回值取决于使用的调用约定
  • It is not possible to write inline assembly with C#. 用C#编写内联汇编是不可能的。 However you can write them with Managed C++. 但是,您可以使用托管C ++编写它们。

This is a very good question and deserves a vote from me. 这是一个非常好的问题,值得我投票。 In relation to your question, the only way this can be done as Cody has pointed out his assembler routine, is to build a DLL based on that, and from there to use p/Invoke using interops such as 关于你的问题,Cody指出他的汇编程序例程的唯一方法是基于它构建一个DLL,并从那里使用p / Invoke使用interops如

[DllImport("mylib.dll")]

etc. However, unfortunately, due to the nature of the C# or any other language for that matter, the CLR runtime environment is using the managed code. 但是,遗憾的是,由于C#或任何其他语言的性质,CLR运行时环境正在使用托管代码。 It would be awkward in having the CLR or the assembler to set up the stack frame, registers, then cross jumping from native world to the managed world. 让CLR或汇编程序设置堆栈框架,注册,然后从本地世界交叉跳​​转到托管世界会很尴尬。 That would be a hairy job to do, but if anyone has seen that kind of thing, please leave a comment at the end of my answer and I will amend this answer accordingly. 这将是一件毛茸茸的工作,但如果有人见过这样的事情,请在我的答案结尾留下评论,我会相应地修改这个答案。

And therefore, to my knowledge, there is no way to inline an assembler routine, by assembler, in that context, using system registers such as eax, ebx, stack frames etc, into a CLR code, just like the sample of code that Cody above has supplied in his answer. 因此,据我所知,没有办法通过汇编程序在汇编程序中内联汇编程序,使用系统寄存器(如eax,ebx,堆栈帧等)转换为CLR代码,就像Cody的代码示例一样以上提供了他的答案。 It would have held true with C/C++ in this case: 在这种情况下,它将适用于C / C ++:

void foo(void){
   _asm{
     xor ecx, ecx
     mov eax, 1
     ....
   }
}

It would be nice to do that from the CLR perspective like this in order to optimize code further, in theory at least but that would be an insurmountable job for the CLR to actually host that kind of thing like this, there is one exception... it can be done with Managed C++ compiler which can do this, but NOT from VB.NET/C# anyway: 从CLR的角度来看这样做会很好,以便进一步优化代码,至少在理论上这对于CLR实际托管这类事情是一个不可逾越的工作,有一个例外。它可以使用Managed C ++编译器来完成,但不能从VB.NET / C#中完成:

private void mycsfunction(string s){
    // Managed code ahoy
    StringBuilder sb = new StringBuilder(s);
    ......
    _asm{
        push ebp
        mov ebp, esp
        lea edx, offset sb
        ....
        mov eax, 1
        pop ebp
    }
}

However, while on the subject, it can be done to generate IL code to native assembler, now, as I write this, I am not 100% sure if this can be done in the reverse, See here for a detailed description of how this can happen 然而,在这个主题上,可以完成生成IL代码到本地汇编程序,现在,正如我写的那样,我不是100%确定是否可以反向完成,请参阅此处以获取详细说明可以发生

But however, the only way of working on CLR assembly is handwriting the IL code and compiling it instead, that is, the assembler of the CLR runtime, directly, in very much the same way as native assembler can be called by native (read NON-CLR binaries), see this article here on how to accomplish this also. 但是 ,处理CLR程序集的唯一方法是手写IL代码并改为编译它,即直接编译CLR运行时的汇编程序,与本机汇编程序可以被本机调用的方式非常相似(读取NON)二进制-CLR),看到这个文章在这里就如何也做到这一点。

As I see it, your question is two-fold: 在我看来,你的问题是双重的:

  1. How do I write a function using assembly and put it in a DLL? 如何使用程序集编写函数并将其放入DLL中?
  2. How do I call a function in an unmanaged DLL from managed C#? 如何从托管C#调用非托管DLL中的函数?

The answer to 2 is to use P/Invoke. 2的答案是使用P / Invoke。 Lots of documentation and tutorials are available on that topic, eg http://msdn.microsoft.com/en-us/magazine/cc164123.aspx 有关该主题的大量文档和教程,例如http://msdn.microsoft.com/en-us/magazine/cc164123.aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM