You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MinGW-w64下objcopy -L本地化符号失效,如何正确处理?

问题描述

我有两组代码文件:

foo 模块文件

foo_var.c

int global_var = 1;

foo_print.c

#include <stdio.h>

extern int global_var;

void print_foo(void)
{
    printf("print_foo = %d\n", global_var);
}

我尝试将它们编译链接为foo.o并本地化global_var符号:

gcc -c foo_var.c foo_print.c
ld -r -o foo.o foo_print.o foo_var.o
objcopy -L global_var foo.o

bar 模块文件

bar_var.c

int global_var = 2;

bar_print.c

#include <stdio.h>

extern int global_var;

void print_bar(void)
{
    printf("print_bar = %d\n", global_var);
}

同样编译链接为bar.o并本地化global_var符号:

gcc -c bar_var.c bar_print.c
ld -r -o bar.o bar_print.o bar_var.o
objcopy -L global_var bar.o

主程序文件 main.c

void print_foo(void);
void print_bar(void);

int main(int argc, char const *argv[])
{
    print_foo();
    print_bar();

    return 0;
}

将所有文件编译链接:

gcc -c main.c
gcc main.o foo.o bar.o

运行生成的a.exe时,得到结果:

print_foo = 1
print_bar = 1

但预期结果应为:

print_foo = 1
print_bar = 2

使用的 GCC 版本为 12.2.0(x86_64-win32-seh-rev2, Built by MinGW-W64 project),请问在 MinGW-w64 中如何正确本地化符号?


解决方案

MinGW-w64 采用 PE/COFF 格式,和 Linux 下的 ELF 符号机制不同,objcopy -L(局部化符号)无法按预期工作——PE 会默认导出所有全局符号,链接器遇到同名符号时会直接选用第一个找到的实现,导致两个模块共用同一个global_var。

要实现符号的模块本地化,推荐使用编译时符号可见性控制,具体步骤如下:

1. 修改模块代码,标记需导出的函数

我们只需要对外暴露print_foo和print_bar,global_var仅作为模块内部变量使用。因此在这两个函数前添加__attribute__((visibility("default"))),显式标记它们为可导出符号:

修改后的foo_print.c:

#include <stdio.h>

extern int global_var;

// 标记该函数为可导出
void __attribute__((visibility("default"))) print_foo(void)
{
    printf("print_foo = %d\n", global_var);
}

修改后的bar_print.c:

#include <stdio.h>

extern int global_var;

// 标记该函数为可导出
void __attribute__((visibility("default"))) print_bar(void)
{
    printf("print_bar = %d\n", global_var);
}

2. 编译模块时启用符号隐藏

使用-fvisibility=hidden编译选项,让编译器默认隐藏所有未显式标记为visibility("default")的全局符号,这样global_var就会被限制在当前模块内部可见。

编译 foo 模块:

# 编译单个源文件,启用符号隐藏
gcc -c -fvisibility=hidden -o foo_var.o foo_var.c
gcc -c -fvisibility=hidden -o foo_print.o foo_print.c
# 合并为 foo.o
ld -r -o foo.o foo_print.o foo_var.o

编译 bar 模块:

gcc -c -fvisibility=hidden -o bar_var.o bar_var.c
gcc -c -fvisibility=hidden -o bar_print.o bar_print.c
ld -r -o bar.o bar_print.o bar_var.o

3. 编译并链接主程序

gcc -c main.c
gcc main.o foo.o bar.o -o a.exe

4. 运行验证

执行a.exe会得到预期结果:

print_foo = 1
print_bar = 2

其他可选方案(需修改代码)

如果不想使用编译选项,也可以通过重命名符号避免冲突:

  1. 执行objcopy --rename-symbol global_var=foo_global_var foo.o,将 foo 模块的global_var重命名为foo_global_var;
  2. 修改foo_print.c中的extern int global_var;为extern int foo_global_var;;
  3. 对 bar 模块执行同样操作,将global_var重命名为bar_global_var并修改对应代码;
  4. 重新编译链接即可。不过这种方法需要修改代码,灵活性不如符号可见性控制。

内容的提问来源于stack exchange,提问作者Liarsenic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 07:05:24