【问题标题】:How can dlerror() return NULL if dlopen() fails?如果 dlopen() 失败,dlerror() 如何返回 NULL?
【发布时间】:2011-12-17 19:31:59
【问题描述】:

我正在开发通过调用脚本自动加载所有 *.so 模块库的系统。

我尝试更新其中一个模块以支持 XML-RPC。我在 Ubuntu 10.10 上使用了库 ibxmlrpc-c3-devdlopen() 在我的更改后失败并且dlerror() 返回NULL 的问题。编译不会返回任何错误。

如何调试和解决此问题?下面是代码:

#include "stdlib.h"
#include "stdio.h"
#ifndef WIN32
#include "unistd.h"
#endif

#include "xmlrpc-c/base.h"
#include "xmlrpc-c/server.h"
#include "xmlrpc-c/server_abyss.h"

#include "config.h"  /* information about this build environment */

而且,我添加了这个功能,大部分行都被注释掉了,即使dlopen()失败了:

int RPC_Server(int           const port) {

   // xmlrpc_server_abyss_parms serverparm;
    //xmlrpc_registry * registryP;
    xmlrpc_env env;



    xmlrpc_env_init(&env);

    //registryP = xmlrpc_registry_new(&env);

   // xmlrpc_registry_add_method(
    //    &env, registryP, NULL, "sample.add", &sample_add, NULL);

    /* In the modern form of the Abyss API, we supply parameters in memory
       like a normal API.  We select the modern form by setting
       config_file_name to NULL:
    */
  //  serverparm.config_file_name = NULint
    RPC_Server(int           const port) {

       // xmlrpc_server_abyss_parms serverparm;
        //xmlrpc_registry * registryP;
        xmlrpc_env env;



        xmlrpc_env_init(&env);

        //registryP = xmlrpc_registry_new(&env);

       // xmlrpc_registry_add_method(
        //    &env, registryP, NULL, "sample.add", &sample_add, NULL);

        /* In the modern form of the Abyss API, we supply parameters in memory
           like a normal API.  We select the modern form by setting
           config_file_name to NULL:
        */
      //  serverparm.config_file_name = NULL;
       // serverparm.registryP        = registryP;
       // serverparm.port_number      = port;
       // serverparm.log_file_name    = "/tmp/xmlrpc_log";

       // printf("Running XML-RPC server...\n");

       // xmlrpc_server_abyss(&env, &serverparm, XMLRPC_APSIZE(log_file_name));

        /* xmlrpc_server_abyss() never returns */

        return 0;
    }L;
   // serverparm.registryP        = registryP;
   // serverparm.port_number      = port;
   // serverparm.log_file_name    = "/tmp/xmlrpc_log";

   // printf("Running XML-RPC server...\n");

   // xmlrpc_server_abyss(&env, &serverparm, XMLRPC_APSIZE(log_file_name));

    /* xmlrpc_server_abyss() never returns */

    return 0;
}

这是用于加载模块的代码

#ifndef RTLD_NOW

#define RTLD_NOW DL_LAZY
#endif   
void* handle;
char* error;


handle=dlopen(mod->binary_file, RTLD_NOW); 

if (!handle){
LOG( " could not open file [%s]: %s\n",
    mod_cfg->binary_file, dlerror() );
return 0;
}

【问题讨论】:

  • 使用DLL不会导致编译错误;它们是动态链接库。
  • 确实,更多细节可能会有所帮助。一般来说,我建议使用gdb 进行调试并使用vim 进行修复。
  • 我只添加了inludes语句和一个函数,我注释掉了大部分测试行,但dlopen仍然失败
  • 你能粘贴调用dlopendlerror的代码吗?
  • dlopen 的参数是什么? (路径是绝对路径吗,见realpath函数)?

标签: c linux ubuntu shared-libraries


【解决方案1】:

在这段代码中:

handle=dlopen(mod->binary_file, RTLD_NOW); 

if (!handle) {
    LOG( " could not open file [%s]: %s\n",
         mod_cfg->binary_file, dlerror() );

我能想到的dlerror() 返回NULL 的最可能方式是LOG 本身调用dl* 例程之一(这将清除dlerror 返回的错误状态)。

所以,

  • 向我们展示LOG 宏(如果它确实宏)扩展为什么,并且
  • 在 GDB 下运行程序,在 dlopendlmopendlsymdlvsym 上设置断点,并观察其中一个在您调用 dlopen 之后被调用在您致电 dlerror 之前和之前。

【讨论】:

    【解决方案2】:

    我会使用像gdb 这样的调试器。

    如果不能使用,请尝试在执行dlopen的进程上使用straceltrace

    另外,在调用dlopen 之前清除errno 并在失败的dlopen 之后显示(或在调试器下打印)。

    检查fileobjdumpnm -D,确保您的dlopen-ed *.so 文件具有所有必需的属性(例如符号)。

    也许执行dlopen 的进程的内存地址空间太满(或已达到某些资源限制),以至于libdl.so 内部的某些内部malloc 失败(例如dlerror 使用的那个)。

    【讨论】:

      【解决方案3】:
      handle=dlopen(mod->binary_file, RTLD_NOW); 
      
      if (!handle){
      string errmsg = string(dlerror());
      LOG( " could not open file [%s]: %s\n",
          mod_cfg->binary_file, errmsg.c_str() );
      return 0;
      }
      

      我在使用 BoostLog 时遇到了同样的问题,以上是我的解决方案。 我想 LOG 会影响 dlerror()。

      【讨论】:

      • 这是另一个答案的副本。
      【解决方案4】:

      我在 Ubuntu 系统上使用 VirtualBox 和 Qt5 时遇到了这个问题; Qt5 无法动态加载libqxcb.so,经过一些调试发现dlopen()dlerror() 都返回了NULL

      原因原来是因为VirtualBox 是一个setuid 二进制文件,不能dlopen 库在不属于root 的目录中。就我而言,解决方案就像运行一样简单:

      sudo chown root:root /usr/lib/x86_64-linux-gnu
      

      我的非 root 用户(UID 1000)以某种方式拥有 /usr/lib/x86_64-linux-gnu,这可能是我的一些错误。但是在将所有权归还给 root 之后,VirtualBox——以及 Qt 的扩展——可以很好地加载库。

      我怀疑这可能与 AppArmor 有关,但我没有这方面的经验,所以我不能肯定。

      【讨论】:

        【解决方案5】:

        首先,您绝对应该使用dlerror

        【讨论】:

        • 但原发帖人说它返回 NULL !
        • @jørgensen 只是一个 ping 让您知道问题已被编辑。 OP 确实注意到 dlerror() 返回 NULL,但为了清楚起见已进行了编辑。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-26
        • 1970-01-01
        相关资源
        最近更新 更多