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)
omajid
  • 14,165
  • 4
  • 47
  • 64
senz
  • 879
  • 10
  • 25
  • Didn't you ask this already? – David Heffernan Sep 04 '17 at 07:31
  • 2
    Note the usage of LD_DEBUG in [this question](https://stackoverflow.com/questions/27054737/how-to-fix-symbol-lookup-error-undefined-symbol-errors-in-a-cluster-environment) to get more troubleshooting info. – Hans Passant Sep 07 '17 at 09:48
  • What does `ldd /usr/lib/libmylib.so` say? Does it include a `libwiringPi.so`? – omajid Sep 08 '17 at 15:22
  • I added the output to my question. – senz Sep 08 '17 at 15:49
  • Name decoration? http://www.codeguru.com/csharp/csharp/cs_data/article.php/c4217/Calling-Unmanaged-Code-Part-1--simple-DLLImport.htm Would something like this work: [DllImport("TestDll.dll", EntryPoint="myproc", ExactSpelling=false,CallingConvention=CallingConvention.Cdecl)] – Adam Benson Sep 11 '17 at 15:01
  • @AdamBenson yea, that actually worked. Could you explain this in an answer? I will accept it. – senz Sep 11 '17 at 16:07
  • @senz - Glad it helped :-) I've added the answer - if you think it needs anything more let me know. – Adam Benson Sep 11 '17 at 16:44

1 Answers1

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

Adam Benson
  • 7,480
  • 4
  • 22
  • 45