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

Why am I getting segfault with loading objects from shared library?

有这个文件:

plusone.c

int op(int i){ return i+1; }

main.c

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main(int argc, char **argv){
    if (argc<3){
        printf("usage %s <library> <number>\n",argv[0]);
        exit(1);
    }

    char *lname = argv[1];
    int num = atoi(argv[2]);
    void *handle = dlopen(lname, RTLD_LAZY);
    if(!handle)
        perror("dlopen");

    int (*opp)(int);
    opp=dlsym(handle, "op");
    if(!opp)
        perror("dlsym");

    printf("number before:%i\nnumber after:%i\n",num,opp(num)); 
    dlclose(handle);
}

编译为:

$cc -fPIC -shared -o plusone.so -ldl plusone.c
$cc -o main.exe -ldl -Wpedantic main.c
warning: ISO C forbids assignment between function pointer and ‘void *’ [-Wpedantic]
$ls
main.c main.exe plusone.so main.exe
$main.exe
usage main.exe <library> <number>
$main plusone.so 1
dlopen: Success
dlsym: Success
Segmentation fault

为什么会出现段错误?

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

Why is segfault?

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

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

这个代码

  void *handle = dlopen(lname, RTLD_LAZY);
  if(!handle)
    perror("dlopen");

dlopen() 上正确分支,返回 NULL,这表示错误,但随后代码采取了错误的操作。

dlopen() does not set errno, so using perror() 记录错误是没有意义的,因为 perror() 依赖于 errno 指示错误,而事实并非如此。因此,如果 dlopen() 失败,您会看到 perror() 正在打印

dlopen: Success

这对 perror() 被调用的事实具有误导性和收缩性,实际上只有 dlopen() 返回 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

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


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

完成后未调用 dlclose()