dumpbin /exports 是windows下最轻量、无需代码即可查看dll导出函数的方法,直接解析导出表,不依赖运行环境或pdb文件;需关注ordinal、name和forwarder三列,避免将转发导出误认为真实函数。

Windows 下用 dumpbin 查看 DLL 导出函数(最轻量、无需代码)
直接拿到一个 .dll 文件,想快速确认它暴露了哪些 C 风格函数(非 C++ mangled 名),dumpbin 是最可靠的选择——它不依赖运行环境,也不需要符号文件(.pdb),只要 DLL 有导出表就能读出来。
常见错误是误用 dumpbin /exports 却没加 /headers 或忽略输出中的序号列,导致把转发导出(forwarder)当成真实函数;也有人用 depends.exe,但它在 Win10/11 上对 ARM64 或高版本 MSVC 编译的 DLL 支持不稳定。
- 确保已安装 Visual Studio 或 Build Tools,命令行中能调用
dumpbin - 执行:
dumpbin /exports your.dll,关注 “ordinal”、“name” 和 “forwarder” 三列 - 若看到某行 “forwarder” 列显示类似
NTDLL.NtCreateFile,说明该条目只是转发,实际函数不在当前 DLL 中 - 若函数名是 C++ 类成员或模板实例(如
??0MyClass@@QEAA@XZ),说明未用extern "C"导出,dumpbin仍会列出,但你无法直接GetProcAddress
Linux 下用 objdump + nm 解析 SO 导出符号(注意默认隐藏与可见性)
.so 文件没有 Windows 那种显式导出表,符号是否“可被外部使用”取决于编译时的 visibility 设置和链接时的 -fvisibility 策略。直接跑 nm -D libxxx.so 只能看到动态符号表(dynamic symbol table)里的条目,但这不等于“可用导出函数”——很多符号可能被 default visibility 隐藏,或被 strip 掉。
- 先检查是否 stripped:
file libxxx.so,若输出含 “not stripped”,可用nm -C -D libxxx.so | grep " T "筛选全局文本符号(即函数) - 若已 stripped,且你有未 strip 的构建产物,优先用那个;否则只能靠
readelf -d libxxx.so | grep NEEDED看依赖,再结合头文件反推 - 注意:
nm -D显示的是运行时动态链接器能查到的符号,但 C++ name mangling 依然存在,需配合c++filt解码,例如:echo "_Z12myExportedFnv" | c++filt - 真正控制导出范围的是编译参数:
-fvisibility=hidden+ 显式__attribute__((visibility("default"))),否则即使函数定义在源码里,也不会进动态符号表
运行时用 GetProcAddress / dlsym 获取函数指针前必须知道什么
获取函数地址不是“有名字就能调”,核心限制在于:符号名必须与导出时完全一致,且调用约定、ABI、参数布局必须匹配。很多崩溃源于把 __cdecl 函数当 __stdcall 调用,或把返回 std::string 的函数强行用 C 函数指针接住。
- Windows:
GetProcAddress(hModule, "MyFunc")中的字符串必须是原始导出名(C 风格)或 mangled 名(C++ 风格),不能是修饰后的人类可读名 - Linux:
dlsym(handle, "MyFunc")同样要求精确匹配,且需确保RTLD_GLOBAL加载方式(尤其涉及跨 SO 共享符号时) - 类型安全几乎为零:C++ 编译器不会校验你传给
typedef void (*fn_t)()的指针是否真接受 0 参数;建议用static_cast强转,并在调用前用assert(func_ptr != nullptr) - 若函数返回或接收 STL 类型(
std::vector、std::shared_ptr),不要尝试跨模块传递——它们的内存布局和分配器策略可能不兼容
为什么不能只靠头文件或文档来“猜”导出函数
头文件声明和实际导出函数之间存在三类典型脱节:宏控制的条件编译(如 #ifdef BUILD_SHARED)、链接脚本过滤(Linux 的 .map 文件)、以及 Windows 的 .def 文件显式控制导出列表。这意味着头文件里声明了 50 个函数,最终 DLL/SO 可能只导出其中 3 个。
- Windows:
.def文件的EXPORTS段具有最高优先级,哪怕函数在源码中加了__declspec(dllexport),若没列在.def里,就不会出现在导出表中 - Linux:链接时加了
--version-script=exports.map,则只有 map 文件里global:块列出的符号才进动态符号表;local:块内的全被隐藏 - 更隐蔽的是编译器优化:LTO(Link-Time Optimization)可能内联掉某些导出函数,或因 ODR(One Definition Rule)违规导致符号被静默丢弃
- 所以,任何“应该有这个函数”的假设,都得回归二进制本身验证——
dumpbin或nm -D是唯一可信依据
真正麻烦的从来不是“怎么列出来”,而是“列出来的名字能不能安全调用”。mangled 名、调用约定、ABI 版本、STL 实现差异……这些不会出现在导出列表里,却决定你第一行调用会不会 crash。动手前,先确认对方提供的是 C 接口还是 C++ ABI 接口,比多敲十行解析代码重要得多。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











