7

在 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),*

为什么会这样,什么时候一个符号(特别是对于一个函数)实际上是 NULL?我正在查看代码,发现了一段使用 dlerror 先清理,然后使用 dlsym,然后使用 dlerror 检查错误。但它不会在调用它之前检查结果函数是否为空:

  • dlerror();
  • a_func_name = ...dlsym(...);
  • if (dlerror()) 转到结束;
  • a_func_name(...); // 从不检查 a_func_name == NULL;

我只是一个审阅者,所以没有添加检查的选项。也许作者知道 NULL 永远无法返回。我的工作是挑战这一点,但不知道什么可以使它返回有效的 NULL,因此我可以检查在此代码的上下文中是否可以满足这样的条件。没有找到合适的东西来谷歌阅读,一个好的文档的指针就足够了,除非你想明确地解释哪个会很棒。

4

4 回答 4

3

我知道 dlsym() 返回的符号值可以为 NULL 的一种特殊情况,即在使用GNU 间接函数(IFUNC) 时。但是,可能还有其他情况,因为dlsym(3) 手册页中的文本早于 IFUNC 的发明。

这是一个使用 IFUNC 的示例。首先,将用于创建共享库的文件:

$ 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;
}

现在是主程序,它将foo在共享库中查找符号:

$ 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)
于 2018-12-03T08:32:24.303 回答
2

好吧,如果它没有错误地返回,那么指针是有效的,并且与NULL来自共享对象的任何随机指针一样非法。比如错误的函数、数据或其他什么。

于 2012-12-18T21:45:00.320 回答
1

如果库/PIE 是普通 C 编译的产物,则不可能,因为 C 永远不会将全局对象放在NULL地址处,但您可以获得一个符号来解析以NULL使用特殊的链接器技巧:

空.c:

#include <stdio.h>
extern char null_addressed_char;
int main(void) 
{
    printf("&null_addressed_char=%p\n", &null_addressed_char);
}

编译、链接和运行:

$ clang null.c -Xlinker --defsym -Xlinker null_addressed_char=0 && ./a.out
&null_addressed_char=(nil)

如果您不允许任何此类怪异,您可以将NULL返回的结果dlsym视为错误。

于 2018-12-03T09:24:28.100 回答
-2

dlerror()返回最后一个错误,而不是最后一次调用的状态。因此,如果没有其他问题,您显示的代码可能会从中获得有效结果,dlsym()并自欺欺人地认为存在错误(因为队列中还有一个错误)。dlerror 背后的目的是提供人类可读的错误消息。如果您没有打印结果,则说明您使用错误。

于 2012-12-18T22:08:57.877 回答