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

GCC版本升级引发多定义链接错误:为何GCC7.5.0可正常编译?

问题描述

我正在将交叉编译工具链从gcc-7.5.0升级至gcc 11.4.0。使用gcc 7.5.0时,下述源码文件可编译成功,但切换至gcc 11.4.0后出现链接错误:

PATH-TO-CROSSTOOLS/ld:
test_beta/libtest_beta.a(test_b.o):/common.h:1: multiple
definition of `g_common';
test_beta/libtest_beta.a(test_a.o):/common.h:1: first
defined here

PATH-TO-CROSSTOOLS/ld:
test_beta/libtest_beta.a(test_c.o):/common.h:1: multiple
definition of `g_common';
test_beta/libtest_beta.a(test_a.o):/common.h:1: first
defined here

我理解该错误源于在C头文件中定义变量,但代码来自第三方库,不便修改。我的问题是:为何gcc 7.5.0能成功编译这些代码?我认为无论使用哪个版本都应编译失败。

相关代码

common.h

int g_common;
int comp(int a, int b);
int plus(int a, int b);
int minus(int a, int b);

test_a.c

#include <stdio.h>
#include "common.h"

int comp(int a, int b){
    g_common = 1;
    printf("g_common: %d\n", g_common);
    return (a > b);
}

test_b.c

#include "common.h"

int plus(int a, int b){
    return (a + b);
}

test_c.c

#include "common.h"

int minus(int a, int b){
    return (a - b);
}

编译脚本

gcc 7.5.0编译脚本

#!/bin/sh

echo "Clean build dir"

rm -rf test_alpha/*

for src in test_a test_b test_c;

do

    echo "Build $src.c"

    <PATH-TO-CROSSTOOLS>/aarch64-openwrt-linux-gcc -fno-builtin -Wno-ignored-qualifiers -g -O2 -Wall -Wbad-function-cast -Wmissing-declarations -Wmissing-format-attribute -Wmissing-prototypes -Wmultichar -Wstrict-prototypes -Wdiv-by-zero -Wpointer-arith   -Wextra -Wchar-subscripts -Wdeprecated-declarations -Winvalid-pch -Wnested-externs -Wsystem-headers -Wunused -fPIC  -rdynamic -Werror -Wformat-nonliteral -Wmissing-noreturn -Wwrite-strings -Wshadow -Wswitch-enum -Wswitch-default  -c -I. $src.c -o test_alpha/$src.o

done

echo "making libtest_alpha.a"

<PATH-TO-CROSSTOOLS>/aarch64-openwrt-linux-ar rc test_alpha/libtest_alpha.a test_alpha/test_a.o test_alpha/test_b.o test_alpha/test_c.o

echo "making libtest_alpha.so"

<PATH-TO-CROSSTOOLS>/aarch64-openwrt-linux-gcc -fno-builtin -Wno-ignored-qualifiers -g -O2 -Wall -Wbad-function-cast -Wmissing-declarations -Wmissing-format-attribute -Wmissing-prototypes -Wmultichar -Wstrict-prototypes -Wdiv-by-zero -Wpointer-arith   -Wextra -Wchar-subscripts -Wdeprecated-declarations -Winvalid-pch -Wnested-externs -Wsystem-headers -Wunused -fPIC  -rdynamic -Werror -Wformat-nonliteral -Wmissing-noreturn -Wwrite-strings -Wshadow -Wswitch-enum -Wswitch-default  -shared -Wl,-soname,libtest_alpha.so -Wl,--whole-archive test_alpha/libtest_alpha.a -Wl,--no-whole-archive -o test_alpha/libtest_alpha.so

gcc 11.4.0编译脚本

#!/bin/sh

PATH=<PATH-TO-CROSSTOOLS>:$PATH

echo "Clean build dir"

rm -rf test_beta/*

for src in test_a test_b test_c;

do

    echo "Build $src.c";

    aarch64-openwrt-linux-gcc --sysroot=<PATH-TO-SYSROOT>/recipe-sysroot -fno-builtin -Wno-ignored-qualifiers -g -O2 -Wall -Wbad-function-cast -Wmissing-declarations -Wmissing-format-attribute -Wmissing-prototypes -Wmultichar -Wstrict-prototypes -Wdiv-by-zero -Wpointer-arith   -Wextra -Wchar-subscripts -Wdeprecated-declarations -Winvalid-pch -Wnested-externs -Wsystem-headers -Wunused  -fPIC  -rdynamic -Werror -Wformat-nonliteral -Wmissing-noreturn -Wwrite-strings -Wshadow -Wswitch-enum -Wswitch-default  -c -I. $src.c -o test_beta/$src.o

done

echo "making libtest_beta.a"

aarch64-openwrt-linux-ar rc test_beta/libtest_beta.a test_beta/test_a.o test_beta/test_b.o test_beta/test_c.o

echo "making libtest_beta.so"

aarch64-openwrt-linux-gcc --sysroot=<PATH-TO-SYSROOT>/recipe-sysroot -fno-builtin -Wno-ignored-qualifiers -g -O2 -Wall -Wbad-function-cast -Wmissing-declarations -Wmissing-format-attribute -Wmissing-prototypes -Wmultichar -Wstrict-prototypes -Wdiv-by-zero -Wpointer-arith   -Wextra -Wchar-subscripts -Wdeprecated-declarations -Winvalid-pch -Wnested-externs -Wsystem-headers -Wunused  -fPIC  -rdynamic -Werror -Wformat-nonliteral -Wmissing-noreturn -Wwrite-strings -Wshadow -Wswitch-enum -Wswitch-default  -shared -Wl,-soname,libtest_beta.so -Wl,--whole-archive test_beta/libtest_beta.a -Wl,--no-whole-archive -o test_beta/libtest_beta.so
问题解答

为何gcc 7.5.0能编译通过?

这是因为gcc的默认行为在不同版本中发生了变化:

  • 在gcc 7及更早版本中,对于头文件中未加static或extern的全局变量定义,编译器默认会将其视为弱符号(weak symbol)。当链接器遇到多个相同的弱符号时,会自动选择其中一个,不会报错。
  • 从gcc 10开始,编译器默认启用了-fno-common选项(之前该选项默认关闭)。这个选项会让编译器将未加修饰的全局变量定义视为强符号(strong symbol),链接器遇到多个强符号定义时就会抛出“multiple definition”错误——这才是符合C标准的严格行为。

简单来说,旧版gcc对这种不符合标准的写法做了妥协,新版gcc则严格遵循C标准,所以升级后会报错。

不修改第三方代码的解决方法

既然无法修改第三方库的代码,可以通过添加编译选项兼容旧行为:
在gcc 11.4.0的编译脚本中,给所有gcc编译命令加上-fcommon选项,让编译器回到旧版处理方式,将全局变量视为弱符号,链接器就不会报错了。

修改后的编译命令示例:

aarch64-openwrt-linux-gcc --sysroot=<PATH-TO-SYSROOT>/recipe-sysroot -fcommon -fno-builtin -Wno-ignored-qualifiers -g -O2 -Wall -Wbad-function-cast -Wmissing-declarations -Wmissing-format-attribute -Wmissing-prototypes -Wmultichar -Wstrict-prototypes -Wdiv-by-zero -Wpointer-arith   -Wextra -Wchar-subscripts -Wdeprecated-declarations -Winvalid-pch -Wnested-externs -Wsystem-headers -Wunused  -fPIC  -rdynamic -Werror -Wformat-nonliteral -Wmissing-noreturn -Wwrite-strings -Wshadow -Wswitch-enum -Wswitch-default  -c -I. $src.c -o test_beta/$src.o

注意要在编译.o文件和生成.so文件的gcc命令中都加上-fcommon选项。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 04:10:54