# Go 32-bit DLL 导出函数找不到修复
## 症状
程序调用 DLL 时报错:
- "无法找到指定DLL库文件"中的输出命令"XXX"
- EPL 或其他调用方找不到 DLL 导出的函数
即使函数已用 `__declspec(dllexport)` 导出、def 文件也已写入,调用方仍找不到。
## 根因
问题出在 **stdcall 名称修饰** 上。
- `__stdcall` 约定下,编译器自动给导出函数加 `@N` 后缀(N = 参数总字节)
- 实际导出名是 `FunctionName@4`,而调用方在找 `FunctionName`
- MinGW GCC 链接器在 def 文件指定裸名时,默认不自动去 `@N`
### 常见误区(不正确的修复方式)
1. **用 MSVC 的 `#pragma comment(linker, "/export:XXX=_XXX@4")`**
- MinGW GCC **不支持** MSVC 的 pragma comment(linker...),这些行被静默忽略
- 写了跟没写一样
2. **在 def 文件中写 `XXX=_XXX@4`**
- MinGW 的 `--output-def` 生成的 .def 里符号名有下划线前缀
- 但链接器解析时会认为 `_XXX@4` 只是一个导出别名,不解决 stdcall 名称问题
- 可能仍然匹配失败
## 解决方案
在 GCC 链接命令上使用 **`-Wl,--kill-at`** 参数。
### `--kill-at` 的作用
告诉 MinGW 链接器:导出 stdcall 函数时,去掉 `@N` 后缀。最终 DLL 的导出表中,函数名保持干净无后缀。
### 必须的三步配置
#### 1. exports.def — 用裸函数名
```
EXPORTS
FunctionName1
FunctionName2
FunctionName3
```
**不要**写 `FunctionName1=_FunctionName1@4`,保持裸名即可。
#### 2. stdcall_wrapper.c — 用 `__declspec(dllexport)` + `__stdcall`
```c
__declspec(dllexport) void __stdcall FunctionName1(void) {
// ...
}
```
**删除**任何 `#pragma comment(linker, ...)` 指令 — MinGW 不支持,写了也无效。
#### 3. 构建脚本 — 链接时加 `-Wl,--kill-at`
```bash
gcc -shared -o output.dll wrapper.o -L. -lgo -Wl,def_file.def -m32 -Wl,--kill-at
```
关键参数 `-Wl,--kill-at` 要放在链接器参数部分,通常在 def 文件之后。
### 验证是否修复
用 MinGW 自带的 `objdump` 检查 DLL 导出表:
```bash
objdump -p your.dll | grep "Exported symbols" -A 20
```
或直接看带编号的导出行:
```bash
objdump -p your.dll | grep "^\["
```
修复前的输出(错误):
```
[ 3] FunctionName@4
```
修复后的输出(正确):
```
[ 3] FunctionName
```
### 完整构建流程示例
```bash
# 1. 编译 Go 核心 DLL(c-shared)
go build -buildmode=c-shared -o libgo.a .
# 2. 编译 C 包装器
gcc -c wrapper.c -o wrapper.o -m32
# 3. 链接生成最终 DLL(加 --kill-at)
gcc -shared -o final.dll wrapper.o -L. -lgo exports.def -m32 -Wl,--kill-at
# 4. 验证导出名
objdump -p final.dll | grep "^\["
```
## 注意事项
- `--kill-at` 只影响导出表中的名称,不影响函数内部 stdcall 调用约定 — 参数仍在栈上,调用方和函数仍按 stdcall 规则匹配
- 纯 C/C++ 项目中 `.def` 配合 `EXPORTS` 写裸名也能正常工作,不需要在 def 里做名称修饰
- 如果同时有多个 DLL(如 Go 核心 DLL + C 包装 DLL),只需在最终包装 DLL 的链接命令上加 `--kill-at` 即可
- 编译 Go 核心 DLL 时用 `buildmode=c-shared` 会自动导出 Go 函数,但 Go 导出的符号是 cdecl 而非 stdcall,需要用 C 包装器转成 stdcall


赞
打赏
生成海报

发表回复
评论列表(0条)