【问题标题】:DLL using __stdcall without name decoration: why does it even work?使用没有名称修饰的 __stdcall 的 DLL:为什么它甚至可以工作?
【发布时间】:2016-12-07 04:57:27
【问题描述】:

如果我这样声明一个函数:

#ifdef TEST_EXPORTS
#define TEST_API __declspec(dllexport)
#else
#define TEST_API __declspec(dllimport)
#endif

TESTAPI int __stdcall myadd(int a, int b);

DLL 中的符号是_myadd@8,这对我来说非常有意义(也就是说,在阅读了这里的其他问题几个小时之后)。

但是 windows 库似乎做了一些不同的事情。他们也使用__stdcall(伪装成WINAPI),但是DLL中的符号没有名字修饰。如果上面的方法在 windows 库中,符号将是myadd

我的猜测是他们使用 def 文件来给符号起别名。但是,当我链接到这些 DLL 之一时,为什么我的链接器会知道这一点?

windows 头文件用WINAPI 声明这些函数,所以如果我调用它们,链接器应该查找修饰名称,因为它是一个__stdcall 函数。然而不知何故,链接器知道删除名称装饰。

我试图通过编写一个小 DLL 并使用 def 文件删除名称装饰来复制这一点。正如预期的那样,我得到链接器错误,因为链接器仍在寻找修饰名称。我已经在纯 C 中完成了此操作,以确保 c++ 名称修改不会影响它。

编辑:澄清一下,MSVC 14.0 / VS2015,32 位

【问题讨论】:

  • 请注意,x64 不使用名称修饰。
  • @JonathanPotter: “请注意,x64 不使用名称修饰。” - 仅适用于 extern "C"。 C++ 符号必须具有修饰的导出名称以进行重载解析。

标签: c winapi dll linker


【解决方案1】:

这里有一些鲜为人知的魔法在起作用。让我们看一些WIN32 API 函数,例如RegQueryValueExW。它在winreg.h 文件中定义如下:

WINADVAPI LSTATUS APIENTRY RegQueryValueExW(...);

其中WIADVAPI__declspec(dllimport)APIENTRY__stdcall 命名约定的绰号。另请注意,标头中的所有函数都声明为extern "C"。所以无论如何,这个函数应该使用名字修饰,它的DLL导出应该是_RegQueryValueExW@24。然而,当我们使用dumpbin /exports 命令查看advapi32.dll 导出时,我们会看到一个未修饰的名称:

现在让我们使用dumpbin /headers advapi32.lib 命令仔细检查advapi32.lib 文件:

注意undecorate 说明符,它允许将修饰名称链接到未修饰的导出。您可以使用包含未修饰名称的EXPORTS 部分的def 文件为您的dll 获得相同的结果。请参阅this 文章和this answer 了解更多信息。

此外,上面写的所有内容仅对 x86 应用程序有效。 x64位环境下的C函数链接without name decoration

C 函数的修饰形式取决于调用 其声明中使用的约定,如下表所示。 这也是 C++ 代码在 声明具有外部“C”链接。默认调用约定是 __cdecl。请注意,在 64 位环境中,函数不会被修饰。

【讨论】:

  • 就是这样。我试图变得聪明并将装饰名称放入 def 文件中。 lib 确实是缺失的环节。它定义了修饰符号并将调用转发到未修饰的 dll 符号。
猜你喜欢
  • 1970-01-01
  • 2010-11-21
  • 1970-01-01
  • 1970-01-01
  • 2020-07-15
  • 1970-01-01
  • 2014-01-17
  • 2016-02-26
  • 1970-01-01
相关资源
最近更新 更多