【问题标题】:.net core - PInvoke C shared library function which depends on another shared library.net core - PInvoke C 共享库函数,它依赖于另一个共享库
【发布时间】:2018-02-12 08:24:50
【问题描述】:

我为现有库 (wiringPi) 编写了一些包装代码来读取温度传感器,但在使用该库时出现错误。

我的包装库看起来像:

mylib.h

#ifndef mylib_h__
#define mylib_h__
extern void read_sensor();
#endif

mylib.c

#include "mylib.h"
#include <wiringPi.h> 

void read_sensor() {
    //here is the first call on the wiringPi lib
    if (wiringPiSetup() == -1)
        exit(1);

    ...
}

然后我使用 gcc 编译我的库:

gcc -Wall -Werror -fPIC -c mylib.c
gcc -shared -o libmylib.so mylib.o -lwiringPi
cp libmylib.so /usr/lib/

提示:如果正常 C 程序使用此库,一切正常。

现在我的 C# 程序使用 PInvoke 从这个库中调用 read_sensor()

程序.cs

class Program 
{
    [DllImport("wiringPi")]
    static extern int wiringPiSetup();

    [DllImport("mylib")]
    static extern void read_sensor();

    static void Main(string[] args)
    {
        wiringPiSetup();
        read_sensor();
    }
}

该程序使用以下参数编译:

dontet publish -r linux-arm

并复制到我的 Raspberry-Pi。

现在我执行这个 C# 程序并抛出以下错误:

./my-program-name:符号查找错误:/usr/lib/libmylib.so:未定义符号:wiringPiSetup

这里出了什么问题?

我的第一个想法是,我的程序不知道 WiringPi 库。所以我为这个 dll 添加了一个导出并调用wiringPiSetup() 进行测试。不管有没有这个语句,结果都是一样的。

我还在我的自定义库中添加了一个没有wiringPi 依赖项的测试函数。这在 C# 中调用得很好。

我是不是在链接时搞砸了?

编辑:

命令ldd /usr/lib/libmylib.so 给出以下输出:

linux-vdso.so.1 (0x7efad000)
/usr/lib/arm-linux-gnueabihf/libarmmem.so (0x76f73000)
libwiringPi.so => /usr/local/lib/libwiringPi.so (0x76f40000)
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0x76dff000)
libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0x76d84000)
libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0x76d5c000)
librt.so.1 => /lib/arm-linux-gnueabihf/librt.so.1 (0x76d45000)
libcrypt.so.1 => /lib/arm-linux-gnueabihf/libcrypt.so.1 (0x76d05000)
/lib/ld-linux-armhf.so.3 (0x54abc000)

【问题讨论】:

  • 你不是已经问过了吗?
  • 请注意this question 中LD_DEBUG 的用法,以获取更多故障排除信息。
  • ldd /usr/lib/libmylib.so 说什么?是否包含libwiringPi.so
  • 我将输出添加到我的问题中。
  • 名字装饰? codeguru.com/csharp/csharp/cs_data/article.php/c4217/… 会不会有这样的工作:[DllImport("TestDll.dll", EntryPoint="myproc", ExactSpelling=false,CallingConvention=CallingConvention.Cdecl)]

标签: c# raspberry-pi shared-libraries .net-core pinvoke


【解决方案1】:

归结为名称装饰。 C++ 编译器不只是将函数的名称放在目标文件中 - 它会根据函数的定义(最显着的是它的参数)在名称中添加信息。

来自https://msdn.microsoft.com/en-us/library/56h2zst2.aspx

表示 C 和 C++ 程序中的函数、数据和对象 在内部通过他们的装饰名称。装饰名称是编码的 编译器在编译对象、数据时创建的字符串, 或函数定义。它记录调用约定、类型、 函数参数和其他信息连同名称。这 名称修饰,也称为名称修饰,帮助链接器查找 链接可执行文件时正确的函数和对象。

但是编译后的 C 代码不这样做 - 名称修饰(或名称修饰)是由 C++ 提供的。

所以,你必须告诉 C#“这个函数的名字没有被修饰。”

为此,请使用如下属性:

[DllImport("TestDll.dll", EntryPoint="myproc", ExactSpelling=false,CallingConvention=CallingConvention.Cdec‌​l)]

是“CallingConvention”位表示“该函数是 C 函数”。

“Cdecl”的意思是“C 声明”,如果我没记错的话。

更多信息请访问:http://www.codeguru.com/csharp/csharp/cs_data/article.php/c4217/Calling-Unmanaged-Code-Part-1--simple-DLLImport.htm

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-06
    • 2011-10-01
    • 2012-06-19
    • 2012-02-24
    • 2016-06-06
    • 1970-01-01
    • 1970-01-01
    • 2018-04-07
    相关资源
    最近更新 更多