Linux 为什么dlsym()返回的符号值可以为null?
在Linux中。根据dlsym(3)Linux手册页Linux 为什么dlsym()返回的符号值可以为null?,linux,dlsym,Linux,Dlsym,在Linux中。根据dlsym(3)Linux手册页 *Since the value of the symbol could actually be NULL (so that a NULL return from dlsym() need not indicate an error),* 当一个符号(特别是函数)实际为空时,为什么会出现这种情况?我正在检查代码,发现一段代码首先使用Dleror进行清理,然后使用dlsym进行清理,然后使用Dleror检查错误。但在调用之前,
*Since the value of the symbol could actually be NULL
(so that a NULL return from dlsym() need not indicate an error),*
当一个符号(特别是函数)实际为空时,为什么会出现这种情况?我正在检查代码,发现一段代码首先使用Dleror进行清理,然后使用dlsym进行清理,然后使用Dleror检查错误。但在调用之前,它不会检查结果函数是否为null:
- 德莱罗()李>
- a_func_name=…dlsym(…)李>
- 如果(dlerror())转到结束李>
- 一个函数名(…);//从不检查a_func_name==NULL李>
我只是一个审核人,所以没有选择权只添加检查。也许作者知道NULL永远不会被返回。我的工作是挑战这一点,但不知道是什么使这个返回为有效的NULL,这样我就可以检查在这个代码的上下文中是否可以满足这样的条件。如果您没有找到适合Google阅读的内容,那么一个指向好文档的指针就足够了,除非您想明确解释哪一个更好。好吧,如果它返回时没有错误,那么指针是有效的,
NULL
与来自共享对象的任何随机指针一样非法。类似于错误的函数、数据或任何内容。dlerror()
返回最后一个错误,而不是最后一个调用的状态。因此,如果没有其他内容,您显示的代码可能会从dlsym()
中获得有效的结果,并欺骗自己认为存在错误(因为队列中仍然有一个错误)。dlerror背后的目的是提供人类可读的错误消息。如果没有打印结果,则使用错误。我知道一种特殊情况,即dlsym()返回的符号值可以为NULL,即使用(IFUNCs)时。然而,可能还有其他的情况,因为在IFUNC发明之前的文本
下面是一个使用IFUNCs的示例。首先,将用于创建共享库的文件:
$ cat foo.c
/* foo.c */
#include <stdio.h>
/* This is a 'GNU indirect function' (IFUNC) that will be called by
dlsym() to resolve the symbol "foo" to an address. Typically, such
a function would return the address of an actual function, but it
can also just return NULL. For some background on IFUNCs, see
https://willnewton.name/uncategorized/using-gnu-indirect-functions/ */
asm (".type foo, @gnu_indirect_function");
void *
foo(void)
{
fprintf(stderr, "foo called\n");
return NULL;
}
$ cat main.c
/* main.c */
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
void *handle;
void (*funcp)(void);
handle = dlopen("./foo.so", RTLD_LAZY);
if (handle == NULL) {
fprintf(stderr, "dlopen: %s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror(); /* Clear any outstanding error */
funcp = dlsym(handle, "foo");
printf("Results after dlsym(): funcp = %p; dlerror = %s\n",
(void *) funcp, dlerror());
exit(EXIT_SUCCESS);
}
现在构建并运行一个案例,其中dlsym()
返回NULL
,而dlerror()
也返回NULL
:
$ cc -Wall -fPIC -shared -o libfoo.so foo.c
$ cc -Wall -o main main.c libfoo.so -ldl
$ LD_LIBRARY_PATH=. ./main
foo called
Results after dlsym(): funcp = (nil); dlerror = (null)
如果库/饼图是普通C编译的产物,则不能这样做,因为C永远不会将全局对象放在
NULL
地址,但您可以使用特殊的链接器技巧获得一个符号来解析为NULL
:
null.c:
#include <stdio.h>
extern char null_addressed_char;
int main(void)
{
printf("&null_addressed_char=%p\n", &null_addressed_char);
}
如果您不允许任何这种奇怪的情况,您可以将
dlsym
返回的NULL
视为错误。这就是在dlsym
之前立即调用dlerror()
以清除最近的错误变量的目的。没有队列(如果手册页可信的话)。啊,错过了。是的,这是毫无意义但正确的dlsym
被记录为在出错时返回NULL,但是dlerror
的非NULL结果是等效的(除非出现线程安全性bug之类的问题——显然,如果另一个线程也在做同样的废话,这里就存在竞争)。这仍然是对API的滥用,当然每个线程都有自己的错误变量副本。无论如何,这是调用API的正确方法,而不是滥用。比较:errno=0;INTA=itoa(s);if(errno).
因为if(a)无法区分s=“0”代码>来自s=“垃圾”
。这是正确的方法,但另一方面,在这种特殊情况下,检查NULL
也应该足够了。在glibc中可能是这样的(errno/perror也是如此),但对于无提示的文档来说则不然。我不指望所有的C库都那么健壮。。。事实上,我刚刚检查了仿生系统,它根本就不是线程安全的。“滥用”不是关于bug,而是关于意图——Dleror设计用于格式化错误消息,句号。抱歉,使用它来避免检查您已经拥有的返回值是疯狂的。如果返回值是共享变量(或函数)的值,这是有意义的。但它应该是地址,不是吗(或者这取决于标志)?好吧,假设它实际上是从一个地址表中读取一个值,并且二进制可以被编辑为在该表中有零(或者像你说的任何无效指针)。好吧,我不确定,但是导出的符号不能有一个绝对地址吗?你可以定义,在汇编程序中或者使用特定于GCC的技巧,给定的符号位于地址0,您可以dlsym
该符号。谢谢。但是,在什么情况下会导致dlsym()返回NULL作为符号值?我无法生成一个案例,但我可能缺少一些内容。@mtk如果要使用defsym从null.c文件生成一个共享库来提供null_addressed_char符号,您应该能够dlopen它和dlsym null_addressed_char,并且它应该返回null而不设置错误。理论上。实际上,Linux的动态链接器正在设置一个错误,除非您删除main(因为它引用null_addressed_char),否则它甚至不会打开。根据dlsym手册页上的说法,我认为这是一个错误。@mtk这很有趣,但看起来dlsym手册页希望人们允许动态符号被空寻址的可能性,而Linux动态链接器却不能很好地处理它们。“实际上,Linux的动态链接器设置了一个错误,除非您删除主链接(因为它引用了null_addressed_char),所以它甚至不会dlopen。”。是的。我看到的就是这样。如果删除对null_addressed_char的引用,以便库可以加载,那么使用dlsym()进行查找会得到null+dlerror()=“未定义的符号”。这似乎与值为0的null_addressed_char有关。如果我将-defsym设置为非零值,那么dlsym()将成功。但是,dlsym(3)手册页中的哪些文本会让您得出这是一个bug的结论?@mtkdlsym
手册页说:“因为符号的值实际上可能是null(因此从dlsym()返回null)无需指示错误),测试错误的正确方法是调用dlerro
$ clang null.c -Xlinker --defsym -Xlinker null_addressed_char=0 && ./a.out
&null_addressed_char=(nil)