【问题标题】:How to call native (C or assembly) code from a .NET (C#) program (under Linux)?如何从 .NET (C#) 程序(在 Linux 下)调用本机(C 或汇编)代码?
【发布时间】:2021-01-03 12:16:36
【问题描述】:

dotnet 运行错误定义here。我正在尝试使用 .Net core x64 Linux 在 C# 中通过 C 库 DllImport 运行汇编代码。如何正确地做到这一点?

C#:

    using System.Runtime.InteropServices;
class Program{
[DllImport("lib.so")] public static extern int foo ();
 static void Main(string[] args)
        {
            int code = foo();
System.Console.WriteLine(code);
}
}

Cmy.c:

    #define EXPORT __attribute__((visibility("default")))
EXPORT int foo(void);
int foo(void)
{
    extern int _start();
    return _start();
}

大会asm.s:

.text
   .globl _start

   _start:               
   mov $1, %rax
   ret

程序目标是通过 C 库从汇编返回零结果代码。打印以下错误消息;不会引发异常:

Hosting components are already initialized.
Re-initialization to execute an app is not allowed.

我已经使用 GCC 编译了 C 和汇编源代码:

gcc -shared -fpic -o lib.so my.c asm.s

【问题讨论】:

  • gcc 命令行错误:-nostartfiles 在编译 (-c) 时没有意义,但只有在链接时才有意义。而lib.so 不包含asm.o

标签: .net linux assembly .net-core x86-64


【解决方案1】:
Re-initialization to execute an app is not allowed.

我不确定这里发生了什么,我只有一个怀疑

gcc -shared -o lib.so my.o

使用此命令行,您将生成一个名为 lib.so 的文件,其中包含文件 my.c。但不是文件asm.s

这意味着该文件不包含符号_start

现在的问题是:以下代码行会发生什么:

return _start();

也许代码会跳转到应用程序的_start符号(它正在运行C#程序)。

这就像在没有将全局变量重新设置为初始值且没有有效堆栈帧的情况下重新启动该应用程序...

编辑

无法在共享库“lib.so”中找到名为“foo”的入口点。在 Program.foo()

问题在于,“static”关键字在用于 C++、C# 或 Java 的“方法”或用于 C 或 C++ 的“函数”时具有完全不同的含义:

在函数或全局变量中,“static”关键字表示该函数或变量只能在同一个文件中使用。

(非常类似于 C# 中的“private”。)

你必须在 C 程序中删除“static”,但你必须在 C# 程序中保留“static”。

编辑 2

我不确定您是否知道从 .NET 调用 C(或从 C 中调用程序集)代码的工作原理:

汇编程序就像 .NET 程序的一部分一样被执行。如果您调用syscall 并将rax 设置为231 (exit_group),则您的汇编函数的行为类似于调用Environment.Exit() 的.NET 函数:

您的 C# 程序立即停止。因此,code=foo() 不会返回任何值,因为foo() 将终止您的 C# 程序。

要从汇编函数返回一个值,您只需将要返回的值写入rax 寄存器。然后你用 ret 指令完成函数:

_start:
    mov $1, %rax
    ret

不幸的是,这似乎不是异常的原因,所以您的程序中还有另一个问题...

【讨论】:

  • 我已将构建命令更改为 as --64 -o asm.o asm.s && gcc -c -fpic my.c -o my.o && gcc -shared -o lib .so my.o asm.o 现在我有一个异常未处理的异常。 System.EntryPointNotFoundException:无法在共享库“lib.so”中找到名为“foo”的入口点。在 Program.foo()
  • @ВладимирСмирнов:您可以将源文件传递给 gcc,以便它在要链接的同一命令中组装或编译。喜欢gcc -shared -fpic -o lib.so my.c asm.s。但无论如何,由于foo 在您的.c 文件中是static,因此它在库的符号表中不可见也就不足为奇了。
  • @MartinRosenau:同意你的猜测,我希望 OP 的 lib.so 将有一个未解析的 _start 符号,链接它的程序需要定义。因此,动态链接将其解析为 CRT 入口点_startcall rel32 将超出可执行文件和共享库之间的范围,但 _start() 函数调用已经编译为 call _start@plt,因为使用了 -fpic 和 C 将其声明为 extern 函数,而不是“隐藏" ELF 能见度。
  • 我已经添加了完整的 C# 代码并且未将 foo 标记为静态,但现在我有 托管组件已经初始化。不允许重新初始化以执行应用程序。 在运行 gcc -shared -fpic -o lib.so my.c asm.s 时。
  • @ВладимирСмирнов 不幸的是,我无法帮助您解决这个例外,但请阅读“EDIT2”部分,了解您程序中的另一个问题。
猜你喜欢
  • 2011-10-08
  • 1970-01-01
  • 2016-06-01
  • 2013-04-16
  • 2020-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多