从so文件中加载symbol并调用方法

Loading symbols from so file and calling method

我在 C++ 中遇到问题。

我创建了一个名为 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;
}

所以,这是一个简单的 BubbleSort 函数。我在文件 BubbleSortAlgorithm.cpp.

中有这个函数

因此,在我程序的主要功能中,我检查 libBubbleSortAlgorithm.so 是否存在。如果没有,那么我必须创建这个库。这个库是通过 popen 创建的。所以我得到了文件 libBubbleSortAlgorithm.so。如果我 运行 命令

nm libBubbleSortAlgorithm.so | c++filt

然后我得到这样的东西。

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

我想这没问题。所以,接下来在主程序中,我用 dlopen 在我的程序中加载这个 .so 文件并像这样调用这个函数

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

但是在main之前我也写过这个

#ifdef __cplusplus
extern "C" {
#endif

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

#ifdef __cplusplus
}
#endif

所以,在 Xcode7 中,我得到了这个错误: /Users/Tadej/Documents/Development/ALGatorC_final/ALGatorC/ALGatorC/main.cpp:96:49:从不兼容的类型 'void *'

分配给 'void (*)(int *, int)'

提前感谢您的帮助。

此致, 戈洛比奇

编辑:我更改了如下代码:

#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] << ", ";
}

所以,现在,我在 运行 时遇到了问题。在 execute(tab, x) 处,Xcode 抱怨并这样说:EXC_BAD_ACCESS(code=1, address=0x0)。所以,问题是 execute 是 NULL。有什么帮助吗? :)

您可以安全地转换结果。

dlsym 只是 returns 一个(函数)指针,但不知道(也不能)知道函数的实际签名。只有客户端代码(您的)才能知道。

转换可以这样完成:

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

并记住@molbdnilo 所说的关于要声明为'extern "C" 的函数。这必须在库代码中完成,而不是在客户端

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

库中的函数未使用 extern "C" 链接编译,因此该名称在库中具有 C++ 名称重整。
因此,dlsym 找不到名称 "execute" 和 returns 一个空指针。

在库中添加 extern "C",或者 dlsym 实际符号,如 nm 没有 通过 [=] 管道输出15=].
或者从 C 代码构建您的库。

我遇到了同样的问题,但我没有使用 extern "C" 关键字,而是执行了以下操作。比方说,我们在 Users/you/Desktop/FooProject 中创建一个目录,然后编写我们的共享库 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

我们从 foo.cpp:

创建共享库
          g++ -dynamiclib foo.cpp -o libfoo.dylib

现在我们想编写一个程序来获取指向我们位于同一目录中的库的函数指针 /Users/You/FooProject/。为此,我们使用 dlopen()dlsym()dlclose() 函数。

由于 C++ 名称修改(用于函数重载),我们的函数名称将与我们在 foo.cpp.[=23 上编写的函数名称不同=]

如果我们运行 objdump -t libfoo.dylib我们得到(在我的 machine 上)这个输出:

        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

这是一个棘手的事情来计算。请注意名称 __Z3sumii__Z4subsii 之前的两条下划线。我们要拉取的函数的实际名称只有一个下划线,而不是两个。函数名是 _Z3sumii_Z4subsii 只有一个下划线。

如果您尝试提取 dlsym() 上的函数,传递带有两个下划线的名称或原始名称,它将 return

错误:

 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

正确:

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

因为 dlsym return 是一个空指针并且预期的签名不同,我们需要适当地转换它。

现在我们知道我们需要的名字了。我们可以编写代码来提取 dylib 的函数指针,称为 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;
}

编译pullout.cpp:

   g++ -c pullout.cpp

要Link生成pullout.o文件,可以采用以下几种方法:

  1. 我们可以使用-L来指定查找lib的路径,以及-l

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

    -l选项扩展为-libfoo.dylib。需要注意的是,gcc 的默认搜索路径是:

     /usr/local/lib
     /usr/lib
    

正因如此,我们需要使用-L选项后接路径。在我们的例子中,我们使用当前目录。

  1. 第二个选项是指定完整路径和lib的名称,所以我们不必使用te -L-l 选项.

            g++ pullout.o libfoo.dylib -o pullFoo
    
  2. 第三个选项,将共享库放入公共目录之一,如 /usr/local/lib 。然后我们就这样做:

            g++ pullout.o -lfoo -o pullFoo
    

如果我们运行它:

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

额外:

对于 运行 时间问题,请在此处查看 mac os 的安装名称:dynamic libraries and macOs and here stakoverflow.