简体   繁体   中英

Loading symbols from so file and calling method

I have a problem in C++.

I've created a function called execute

int* execute(int tab[], int n)
{
    for (int i = 0; i<n; i++)
    {
        for (int j = 0; j < n-1; j++)
        {
            if (tab[j] > tab[j+1])
            {
                int tmp = tab[j];
                tab[j] = tab[j+1];
                tab[j+1] = tmp;
            }
        }
    }
    return tab;
}

So, this is simple BubbleSort function. I have this function in file BubbleSortAlgorithm.cpp.

So, in main function of my program, I check if libBubbleSortAlgorithm.so exist. If not, then I must create this lib. This lib is created via popen. So I've ended up with file libBubbleSortAlgorithm.so. If I run command

nm libBubbleSortAlgorithm.so | c++filt

then I get something like this.

0000000000000ed0 T execute(int*, int)
                 U dyld_stub_binder

I presume this is ok. So, next in main program, I load this .so file in my program with dlopen and call this function like this

void *handle = dlopen(buff, RTLD_LAZY);
if (handle)
{
    execute = dlsym(handle, "execute");
    int tab[5] = { 5, 2, 4, 7, 1 };
    int x = 5;
    execute(tab, x);
}

But before main I've also wrote this

#ifdef __cplusplus
extern "C" {
#endif

    void (*execute)(int*, int);

#ifdef __cplusplus
}
#endif

So, in Xcode7, I get this error: /Users/Tadej/Documents/Development/ALGatorC_final/ALGatorC/ALGatorC/main.cpp:96:49: Assigning to 'void (*)(int *, int)' from incompatible type 'void *'

Thank you in advance for help.

Regards, golobich

Edit: I've changed code like this:

#ifdef __cplusplus
extern "C" {
#endif

    int* (*execute)(int*, int);

#ifdef __cplusplus
}
#endif


execute = (int*(*)(int*, int))dlsym(handle, "execute");
int tab[5] = { 5, 2, 4, 7, 1 };
int x = 5;
int *xs = execute(tab, x);
for (int i = 0; i<5; i++)
{
    std::cout << xs[i] << ", ";
}

So, now, I have problem at runtime. At execute(tab, x), Xcode complain and say this: EXC_BAD_ACCESS(code=1, address=0x0). So, problem is that execute is NULL. Any help? :)

You can safely cast the result.

dlsym just returns a (function) pointer, but does not know (nor it can) know the actual signature of your function. Only client code (your) can know that.

Cast can be done like this:

typedef int *(*execute_t)(int, int) ;
...
execute = (execute_t *)dlsym(handle, "execute");

And keep in mind what @molbdnilo says about function to be declared 'extern "C". This has to be done in libary code, not on client side

extern "C" int* execute(int tab[], int n)
{
    for (int i = 0; i<n; i++)
    {
....

The function in the library wasn't compiled with extern "C" linkage, so the name has C++ name-mangling in the library.
Because of this, dlsym can't find the name "execute" and returns a null pointer.

Either add extern "C" in the library, or dlsym the actual symbol as shown by nm without piping its output through c++filt .
Or build your library from C code.

I had same Problem but I did the following, without using the extern "C" keyword . Let say, we create a directory in Users/you/Desktop/FooProject and we write our shared library foo.cpp :

    #ifndef FOO_H
    #define FOO_H

    int sum(int x, int y)
    {
       return x+y;
    }

    int subs( int x, int y)
    {
        return x-y;
    }

    #endif //FOO_H

We create the share library from foo.cpp :

          g++ -dynamiclib foo.cpp -o libfoo.dylib

Now we would like to write a program that gets the function pointers to our library in the same directory /Users/You/FooProject/ . For that we use dlopen() , dlsym() and dlclose() functions.

Because of the C++ name-mangling (used for function overloading ) our function names will differ from the ones that we have written on foo.cpp .

If we run objdump -t libfoo.dylib we get ( on my machine) this output:

        libfoo.dylib:   file format Mach-O 64-bit x86-64

        SYMBOL TABLE:
        0000000000000f80 g     F __TEXT,__text  __Z3sumii
        0000000000000fa0 g     F __TEXT,__text  __Z4subsii
    0000000000000000         *UND*  dyld_stub_binder

Here is the tricky thing to take in count. Notice the two underscore lines before the names __Z3sumii and __Z4subsii . The actual names of the functions that we want to pull are just with one underscore instead of two. The names of the functions are _Z3sumii and _Z4subsii with only one underscore.

If you try to pull out the functions on dlsym() passing the name with the two underscores or the original names it will return NULL .

Wrong:

 int (*sum)(int, int);
 sum = dlsym(foo_lib_handle,"__Z3sumii");//two underscores and no casting

 or

 int (*sum)(int, int);
 sum = dlsym(foo_lib_handle,"sum"); //the name demangled and no casting

Correct:

  int (*sum)(int, int);
  sum = ( int(*)(int, int) ) dlsym(foo_lib_handle,"_Z3sumii");

Because the dlsym returns a void pointer and the signatura expected is different we need to cast it appropriately.

Now that we know the names we need. We can write the code to pull out the function pointers for the dylib, called pullout.cpp :

#include <dlfcn.h>
#include <iostream>

 int main( )
 {   
   //function pointers for the pulled functions
   int (*sum)(int, int);
   int (*subs)(int, int);

   // open the shared library
   void* foo_lib_handle = dlopen("libfoo.dylib", RTLD_LAZY | RTLD_GLOBAL);

   if(!foo_lib_handle)
   {
       std::cout << "problemo loading dylib" << std::endl;
       return 1;
   }

   //notice the casting and the name "_Z3sumii" instead of "__Z3sumii" and the same for subs
   sum  = ( int(*)(int, int) ) dlsym(foo_lib_handle,"_Z3sumii"); 
   subs = ( int(*)(int,int ) ) dlsym(foo_lib_handle,"_Z4subsii");

   if( sum == NULL || subs == NULL )
   {

      std::cout << "functions pointers are null" << std::endl;
      return 1;
   }

   std::cout << "calling sum(8,8) = " << sum(8,8) << '\n';
   std::cout << "calling subs(18,8) = "<< subs(18,8) <<'\n';

   //close the library
   dlclose(foo_lib_handle);

  return 0;
}

Compile pullout.cpp :

   g++ -c pullout.cpp

To Link the pullout.o file generated, we can use several approaches :

  1. We can use the -L to specify the path where to search the lib, and -l :

      g++ pullout.o -L. -lfoo -o pulloutFoo 

    The -l option is expanded as -libfoo.dylib . Is important to notice that the default search path for gcc are:

      /usr/local/lib /usr/lib 

Because of this, we need to use the -L option followed by the path. In our case we use the current directory.

  1. Second option is to specify the full path and the name of the lib, so we do not have to use te -L , and -l options.

      g++ pullout.o libfoo.dylib -o pullFoo 
  2. Third option, put the shared library into one of the common directories like /usr/local/lib . Then we would just do :

      g++ pullout.o -lfoo -o pullFoo 

If we run it:

  $ ./pullFoo
  calling sum(8,8) 16 
  calling subs(18,8) 10

Extra:

For runtime problems, see install names for mac os here: dynamic libraries and macOs and here stakoverflow .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM