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:
#ifndef mylib_h__
#define mylib_h__
extern void read_sensor();
#endif
#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:
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)
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.Cdecl)]
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With