为什么从共享库加载对象时会出现段错误?
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 输出可以看出,dlopen
和 dlsym
都成功(但它们甚至不应该输出,否则意味着条件为真,并且这些函数的返回值是 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()
有这个文件:
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 输出可以看出,dlopen
和 dlsym
都成功(但它们甚至不应该输出,否则意味着条件为真,并且这些函数的返回值是 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()