使用 NULL 句柄调用 dlsym() 不会 return NULL,而是 return 一个随机函数

Calling dlsym() with a NULL handle doesn't return NULL, but rather returns a random function

我的标题可能不清楚,所以请允许我解释一下。我有一段代码是这样的:

void* pluginFile = dlopen(fileName, RTLD_LAZY);
auto function = dlsym(pluginFile, "ExpectedFunction");

如果 dlopen return 是正确的文件,这就可以正常工作。我的问题是 dlopen 找不到文件并且 returns NULL。当前发生的是进行此调用:

dlsym(0x0, "ExpectedFunction");

问题是这个 return 是我名为 ExpectedFunction 的项目中的一个随机函数。我认为会发生的是 dlsym 会 return NULL 因为传递的句柄是 NULL。我无法在线找到此类用例的预期行为。

我的问题是,当您将 NULL 句柄传递给 dlsym 时应该发生什么?它会简单地 return NULL 还是将其解释为位置 0x0 的句柄?如果预期行为是后者,那么我将简单地添加一个检查以确保 dlopen suceeded。如果不是,我想知道如果句柄是 NULL.

,为什么它会随机 returns 一个来自其他库的同名函数

我当前的用例是我正在加载我制作的 10 个共享库,它们都有一个函数 ExpectedFunction()。但是,如果我们使用不存在的共享库的文件名调用 dlopen,它将 return NULL。然后,dlsym 将 return 指向最后加载的库的 ExpectedFunction() 的指针。

My question is, what is supposed to happen when you pass a NULL handle to dlsym?

规范说:

If handle does not refer to a valid object opened by dlopen() ... dlsym() shall return NULL.

但是,有一些保留句柄值具有特殊行为。如果您传递此类保留句柄,则行为会有所不同。 POSIX 未指定确切值,但例如在 glibc 中:

# define RTLD_NEXT        ((void *) -1l)
# define RTLD_DEFAULT        ((void *) 0)

(void *) 0 为空,因此您不小心将 RTLD_DEFAULT 传递给了 dlsym。其中,规范说:

RTLD_DEFAULT

The symbol lookup happens in the normal global scope; that is, a search for a symbol using this handle would find the same definition as a direct use of this symbol in the program code.

所以,总而言之,应该发生什么取决于 NULL 是否是保留值。它恰好在glibc中保留,但在其他实现中不一定如此。

在传递给 dlsym 之前,您应该检查 dlopen 是否 return null(或检查 dlerror 是否 return null)。

来自 dlfcn.h Ubuntu Linux:

/* If the first argument to `dlsym' or `dlvsym' is set to RTLD_DEFAULT
   the run-time address of the symbol called NAME in the global scope
   is returned.  */
# define RTLD_DEFAULT   ((void *) 0)

来自 dlsym man-page:

RTLD_DEFAULT

Find the first occurrence of the desired symbol using the default shared object search order. The search will include global symbols in the executable and its dependencies, as well as symbols in shared objects that were dynamically loaded with the RTLD_GLOBAL flag.