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

1

6

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)
Compagnie answered 4/9, 2017 at 6:18 Comment(7)
Didn't you ask this already?Pisciculture
Note the usage of LD_DEBUG in this question to get more troubleshooting info.Eleemosynary
What does ldd /usr/lib/libmylib.so say? Does it include a libwiringPi.so?Copter
I added the output to my question.Compagnie
Name decoration? codeguru.com/csharp/csharp/cs_data/article.php/c4217/… Would something like this work: [DllImport("TestDll.dll", EntryPoint="myproc", ExactSpelling=false,CallingConvention=CallingConvention.Cdecl)]Snoopy
@AdamBenson yea, that actually worked. Could you explain this in an answer? I will accept it.Compagnie
@Compagnie - Glad it helped :-) I've added the answer - if you think it needs anything more let me know.Snoopy
S
4

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

Snoopy answered 11/9, 2017 at 16:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.