Which GCC 10 default change causes link failures for tentative globals?
0 reputation · 10 Nov 2023, 07:26 UTC
0 reputation · 10 Nov 2023, 07:26 UTC
Local builds compiled with GCC 9 link successfully even when the same uninitialized global variable appears in multiple translation units. In production, after upgrading to GCC 10+, the same source fails with “multiple definition” errors.
GCC 10 changed the default handling of tentative definitions from -fcommon to -fno-common. The linker now rejects multiple tentative definitions that previously merged into a single common block.
It is unclear whether the codebase intentionally relies on the old common‑block behavior or whether the failures expose hidden initialization bugs that the new default would otherwise reveal.
Which GCC flag should be added to restore the legacy behavior? Does using -fcommon mask subtle bugs that the new default would expose? What build‑time checks can detect tentative global conflicts before a compiler upgrade?
31775 reputation · 10 Nov 2023, 14:32 UTC
The link failures are caused by GCC 10 changing the default compiler flag from -fcommon to -fno-common. This change alters how the compiler handles "tentative definitions"—global variables that are declared without an explicit initializer.
In versions prior to GCC 10, the -fcommon default allowed the compiler to place uninitialized global variables into a "common" block. The linker would then merge all common symbols with the same name across different translation units into a single memory location. This effectively allowed the same global variable to be defined in multiple .c files without causing a conflict.
With -fno-common, these variables are now treated as strong symbols in the .bss section. When the linker encounters the same symbol in multiple object files, it no longer merges them; instead, it triggers a "multiple definition" error to enforce stricter C standards.
To restore the legacy behavior and resolve the immediate link failure, add the following flag to your build configuration:
-fcommon
You can verify the symbol type in your object files using the nm utility. Compare the output of a build using both flags:
-fcommon: Symbols appear with a C (Common) tag.-fno-common: Symbols appear with a B (BSS) or D (Data) tag.nm your_object_file.o | grep your_variable_name
While -fcommon fixes the build, it may mask architectural bugs. The new default exposes cases where developers accidentally defined the same variable name in multiple files, intending them to be distinct local-to-module globals. Relying on common blocks can lead to subtle state bugs if different modules unintentionally share and modify the same memory location.
Recommendation: Use -fcommon as a short-term fix. For a long-term solution, refactor the code by declaring the variable once in a .c file and using extern declarations in a shared header file.
To determine if -fcommon is the only necessary fix, please provide the specific linker error message. If the error mentions strong vs weak symbols specifically, it may indicate an initialization mismatch that -fcommon cannot resolve.
Use comments to ask for clarification. Post a solution as an answer.
4,180 reputation · 10 Nov 2023, 18:10 UTC
The -fcommon flag only affects uninitialized globals with external linkage (tentative definitions). Initialized globals or those declared static are never placed in a common block, so they are not influenced by this switch.
-fno-common (GCC 10+ default) each tentative definition becomes a strong .bss symbol; the linker treats duplicate definitions as multiple definitions.-fcommon restores the legacy behavior by emitting a SHN_COMMON symbol, which the linker merges.To verify the symbol type after compilation, you can inspect the object file:
nm -s file.o # shows 'C' for common when -fcommon is used
readelf -s file.o | grep
If you see SHN_COMMON the symbol is in a common block; with -fno-common it will appear as SHN_BSS (or SHN_DATA if initialized).
Detecting problematic tentative globals before upgrading can be done by compiling with -Werror=common (or simply -fno-common) and treating any link error as a signal to fix the source—either by adding an initializer, making the variable static, or giving it a single extern definition.