从so文件加载符号并调用方法

时间:2015-07-09 11:16:07

标签: c++ xcode shared-libraries

我在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是否存在。如果没有,那么我必须创建这个lib。这个lib是通过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);
}

但在主要之前我也写过这个

#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:分配给&#39; void(*)(int *,int)&#39;来自不兼容的类型&#39; void *&#39;

提前感谢您的帮助。

此致 golobich

编辑:我改变了这样的代码:

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

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

3 个答案:

答案 0 :(得分:2)

您可以安全地投射结果。

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

Cast可以这样做:

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++)
    {
....

答案 1 :(得分:2)

库中的函数未使用 final Intent intent = new Intent(Intent.ACTION_VIEW); String extension = fileName.substring(fileName.lastIndexOf('.')); intent.setDataAndType(Uri.fromFile(new File("fileName")), "application/pdf"); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_SINGLE_TOP); if (!this.context.getPackageManager() .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) { startActivity(intent); finish(); } 链接进行编译,因此该名称在库中具有C ++名称更改。
因此,extern "C"找不到名称“execute”并返回空指针。

在库中添加dlsym,或extern "C"实际符号,如dlsym 所示,不用通过nm管道输出。<登记/> 或者用C代码构建你的库。

答案 2 :(得分:0)

我遇到了同样的问题,但是我没有使用外部“ 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 上编写的名称。

如果我们运行 objdump -t libfoo.dylib ,我们将在我的机器上得到以下输出:

        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()上的函数,并用两个下划线或原始名称传递名称,则它将返回 NULL

错误:

 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返回一个void指针,并且预期的signatura不同,因此我们需要对其进行适当的强制转换。

现在,我们知道了所需的名称。我们可以编写代码来提取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

要链接生成的 pullout.o 文件,我们可以使用几种方法:

  1. 我们可以使用 -L 指定搜索lib的路径,以及 -l <​​/ strong>:

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

    -l <​​/ strong>选项扩展为 -libfoo.dylib 。请务必注意,gcc的默认搜索路径为:

     /usr/local/lib
     /usr/lib
    

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

  1. 第二个选项是指定lib的完整路径和名称,因此我们不必使用te -L -l <​​/ strong>选项

            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

其他:

有关运行时问题,请参见dynamic libraries和macOs以及此处stakoverflow的mac os安装名称。