Go 32-bit DLL 导出函数找不到修复

‌# 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)

发表回复

评论问题之前, 点击我,能帮你解决大部分问题

评论列表(0条)

请用支付宝扫一扫完成支付