Summary
I have several C source files that all declare individual identically named static global variables. My understanding is that the static global variable in each file should be visible only within that file and should not have external linkage applied, but in fact I can see when debugging that the identically named variables share the same memory address.
It is like the static keyword is being ignored and the global variables are being treated as extern instead. Why is this?
Example Code
foo.c:
/* Private variables -----------------------------------*/
static myEnumType myVar = VALUE_A;
/* Exported functions ----------------------------------*/
void someFooFunc(void) {
myVar = VALUE_B;
}
bar.c:
/* Private variables -----------------------------------*/
static myEnumType myVar = VALUE_A;
/* Exported functions ----------------------------------*/
void someBarFunc(void) {
myVar = VALUE_C;
}
baz.c:
/* Private variables -----------------------------------*/
static myEnumType myVar = VALUE_A;
/* Exported functions ----------------------------------*/
void someBazFunc(void) {
myVar = VALUE_D;
}
Debugging Observations
- Set breakpoints on the
myVar = ...line inside each function. - Call
someFooFunc,someBarFunc, andsomeBazFuncin that order from main. - Inside
someFooFuncmyVarinitially is set toVALUE_A, after stepping over the line it is set toVALUE_B. - Inside
someBarFuncmyVaris for some reason initally set toVALUE_Bbefore stepping over the line, notVALUE_Aas I'd expect, indicating the linker may have merged the separate global variables based on them having an identical name. - The same goes for
someBazFuncwhen it is called. - If I use the debugger to evaluate the value of
&myVarwhen at each breakpoint the same address is given.
Tools & Flags
Toolchain: GNU ARM GCC (6.2 2016q4)
Compiler options:
arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -mlong-calls -O1 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -ffreestanding -fno-move-loop-invariants -Wall -Wextra -g3 -DDEBUG -DTRACE -DOS_USE_TRACE_ITM -DSTM32L476xx -I"../include" -I"../system/include" -I"../system/include/cmsis" -I"../system/include/stm32l4xx" -I"../system/include/cmsis/device" -I"../foo/inc" -std=gnu11 -MMD -MP -MF"foo/src/foo.d" -MT"foo/src/foo.o" -c -o "foo/src/foo.o" "../foo/src/foo.c"
Linker options:
arm-none-eabi-g++ -mcpu=cortex-m4 -mthumb -mlong-calls -O1 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -ffreestanding -fno-move-loop-invariants -Wall -Wextra -g3 -T mem.ld -T libs.ld -T sections.ld -nostartfiles -Xlinker --gc-sections -L"../ldscripts" -Wl,-Map,"myProj.map" --specs=nano.specs -o ...
NOTE: I do understand that OP's target platform is ARM, but nevertheless I'm still posting an answer in terms of x86. The reason is, I have no ARM backend in handy, while the question is not limited to a particular architecture.
Here's a simple test stand. Note that I'm using
intinstead of customenumtypedef, since it should not matter at all.foo.c
bar.c
main.c
I'm compiling it on x86_64 Ubuntu 14.04 with GCC 4.8.4:
Obtaining such results effectively means that
myVarvariables infoo.candbar.care different. If you look at the disassembly (byobjdump -D ./a.out):You can see that the actual addresses of static variables in different modules are indeed different:
0x601040forfoo.cand0x601044forbar.c. However, they are associated with a single symbol_ZL5myVar, which really screws up GDB logic.You can double-check that by means of
objdump -t ./a.out:Yet again, different addresses, same symbols. How GDB will resolve this conflict is purely implementation-dependent.
I strongly believe that it's your case as well. However, to be double sure, you might want to try these steps in your environment.