Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.net core - PInvoke C shared library function which depends on another shared library

I wrote some wrapper code for an existing library (wiringPi) to read a temperature sensor but ended up with an error while consuming this library.

My wrapper lib looks like:

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

    ...
}

then i use gcc to compile my library:

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

Hint: In case of a normal C program consumption of this library everything works fine.

Now there‘s my C# program which use PInvoke to call read_sensor() from this library:

Program.cs

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

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

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

This program is compiled with the following arguments:

dontet publish -r linux-arm

and copied to my Raspberry-Pi.

Now i execute this C# program and the following error is thrown:

./my-program-name: symbol lookup error: /usr/lib/libmylib.so: undefined symbol: wiringPiSetup

What‘s going wrong here?

My first thought was, my program didn‘t know the wiringPi library. So i added an export for this dll and called wiringPiSetup() for testing. Same result with or without this statement.

I added also a test function without the wiringPi dependency into my custom library. This is called fine by C#.

Did i mess something up at linking time?

Edit:

The command ldd /usr/lib/libmylib.so gives this output:

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)
like image 685
senz Avatar asked Sep 04 '17 06:09

senz


1 Answers

It comes down to name decoration. The C++ compiler doesn't just put the name of a function in the object file - it adds information to the name according to the function's definition (most notably its parameters).

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

Functions, data, and objects in C and C++ programs are represented internally by their decorated names. A decorated name is an encoded string created by the compiler during compilation of an object, data, or function definition. It records calling conventions, types, function parameters and other information together with the name. This name decoration, also known as name mangling, helps the linker find the correct functions and objects when linking an executable.

But Compiled C code does not do this - name decorating (or name mangling) came in with C++.

So, you have to tell C# "this function's name is not decorated."

To do that use an attribute like this:

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

It's the "CallingConvention" bit that says "the function is a C function."

"Cdecl" means "C declaration", if I remember right.

More information can be found at: http://www.codeguru.com/csharp/csharp/cs_data/article.php/c4217/Calling-Unmanaged-Code-Part-1--simple-DLLImport.htm

like image 136
Adam Benson Avatar answered Oct 19 '22 21:10

Adam Benson