为什么从共享库加载对象时会出现段错误?

aut*_*456 1 c error-handling shared-libraries segmentation-fault dlopen

有这个文件:

\n\n

加一c

\n\n
int op(int i){ return i+1; }\n
Run Code Online (Sandbox Code Playgroud)\n\n

主程序

\n\n
#include <stdio.h>\n#include <stdlib.h>\n#include <dlfcn.h>\n\nint main(int argc, char **argv){\n    if (argc<3){\n        printf("usage %s <library> <number>\\n",argv[0]);\n        exit(1);\n    }\n\n    char *lname = argv[1];\n    int num = atoi(argv[2]);\n    void *handle = dlopen(lname, RTLD_LAZY);\n    if(!handle)\n        perror("dlopen");\n\n    int (*opp)(int);\n    opp=dlsym(handle, "op");\n    if(!opp)\n        perror("dlsym");\n\n    printf("number before:%i\\nnumber after:%i\\n",num,opp(num)); \n    dlclose(handle);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

编译为:

\n\n
$cc -fPIC -shared -o plusone.so -ldl plusone.c\n$cc -o main.exe -ldl -Wpedantic main.c\nwarning: ISO C forbids assignment between function pointer and \xe2\x80\x98void *\xe2\x80\x99 [-Wpedantic]\n$ls\nmain.c main.exe plusone.so main.exe\n$main.exe\nusage main.exe <library> <number>\n$main plusone.so 1\ndlopen: Success\ndlsym: Success\nSegmentation fault\n
Run Code Online (Sandbox Code Playgroud)\n\n

为什么会出现段错误?

\n\n

从 bash 输出中可以看出, 和 都dlopen成功dlsym(但它们甚至不应该输出,否则这意味着条件为真,并且这些函数的返回值是 NULL? - 正如条件一样)。但即使返回的“成功” perror,我也无法重现段错误,因为不知道错误在哪里。

\n

alk*_*alk 5

为什么会出现段错误?

很可能是因为opp等于尝试被调用的NULL时刻。opp(num)

您没有正确处理对dlopen()和 的调用的错误dlysym(),尽管代码测试了结果,但它没有对这两个函数的失败采取正确的操作。

这段代码

  void *handle = dlopen(lname, RTLD_LAZY);
  if(!handle)
    perror("dlopen");
Run Code Online (Sandbox Code Playgroud)

dlopen()返回时正确分支NULL指示错误,但随后代码执行错误的操作。

dlopen()没有设置errno,因此使用perror()记录错误是没有意义的,因为perror()依赖于指示errno错误,而事实并非如此。所以在失败时dlopen()你会看到perror()打印

dlopen: Success
Run Code Online (Sandbox Code Playgroud)

这对被调用的事实具有误导性和收缩性perror(),实际上只有在dlopen()returned时才会发生NULL,表明失败。如果dlopen()成功,perror()则根本不会被调用,也不会打印任何内容。

调用 时也会出现同样的错误dlsym()

dl*()要检索有关函数系列成员失败的错误信息,请使用dlerror().

有关如何正确且完整地实施错误处理的示例,请参见下文:

  void *handle = dlopen(...);
  if (!handle)
  {
    fprintf(stderr, "dlopen: %s\n", dlerror());
    exit(EXIT_FAILURE); /* Or do what ever to avoid using the value of handle. */
  }
#ifdef DEBUG
  else
  {
    fputs("dlopen: Success\n", stderr);
  }
#endif
Run Code Online (Sandbox Code Playgroud)

应采用相同的方法来处理 的结果dlsym()


除此之外,与观察到的行为无关,代码dlclose()在使用有效的handle.