Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different Static Global Variables Share the Same Memory Address

Tags:

c

gcc

arm

gnu-arm

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

  1. Set breakpoints on the myVar = ... line inside each function.
  2. Call someFooFunc, someBarFunc, and someBazFunc in that order from main.
  3. Inside someFooFunc myVar initially is set to VALUE_A, after stepping over the line it is set to VALUE_B.
  4. Inside someBarFunc myVar is for some reason initally set to VALUE_B before stepping over the line, not VALUE_A as I'd expect, indicating the linker may have merged the separate global variables based on them having an identical name.
  5. The same goes for someBazFunc when it is called.
  6. If I use the debugger to evaluate the value of &myVar when 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 ...
like image 931
helsmore Avatar asked Jun 28 '17 12:06

helsmore


People also ask

Is static memory same as global memory?

The memory allocated for static variables is only once i.e all the objects shared by same memory but when it comes to global variables memory is allocated for each and every object. Static and global variable differ in life and scope.

Where static global variables are stored in memory?

Global and static variables are stored in the address space of a virtual processor, in the data segment of a shared-object file.

Are global variables stored in memory?

Global variables are stored in the data section. Unlike the stack, the data region does not grow or shrink — storage space for globals persists for the entire run of the program. Finally, the heap portion of memory is the part of a program's address space associated with dynamic memory allocation.

Is static variable and global variables are same?

The difference between static variables and global variables lies in their scope. A static variable only has a block scope while a global variable can be accessed from anywhere inside the program.


3 Answers

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 int instead of custom enum typedef, since it should not matter at all.

foo.c

static int myVar = 1;  int someFooFunc(void) {         myVar += 2;         return myVar; } 

bar.c

static int myVar = 1;  int someBarFunc(void) {         myVar += 3;         return myVar; } 

main.c

#include <stdio.h>  int someFooFunc(void); int someBarFunc(void);  int main(int argc, char* argv[]) {         printf("%d\n", someFooFunc());         printf("%d\n", someBarFunc());         return 0; } 

I'm compiling it on x86_64 Ubuntu 14.04 with GCC 4.8.4:

$ g++ main.c foo.c bar.c $ ./a.out 3 4 

Obtaining such results effectively means that myVar variables in foo.c and bar.c are different. If you look at the disassembly (by objdump -D ./a.out):

000000000040052d <_Z11someFooFuncv>:   40052d:       55                      push   %rbp   40052e:       48 89 e5                mov    %rsp,%rbp   400531:       8b 05 09 0b 20 00       mov    0x200b09(%rip),%eax        # 601040 <_ZL5myVar>   400537:       83 c0 02                add    $0x2,%eax   40053a:       89 05 00 0b 20 00       mov    %eax,0x200b00(%rip)        # 601040 <_ZL5myVar>   400540:       8b 05 fa 0a 20 00       mov    0x200afa(%rip),%eax        # 601040 <_ZL5myVar>   400546:       5d                      pop    %rbp   400547:       c3                      retq  0000000000400548 <_Z11someBarFuncv>:   400548:       55                      push   %rbp   400549:       48 89 e5                mov    %rsp,%rbp   40054c:       8b 05 f2 0a 20 00       mov    0x200af2(%rip),%eax        # 601044 <_ZL5myVar>   400552:       83 c0 03                add    $0x3,%eax   400555:       89 05 e9 0a 20 00       mov    %eax,0x200ae9(%rip)        # 601044 <_ZL5myVar>   40055b:       8b 05 e3 0a 20 00       mov    0x200ae3(%rip),%eax        # 601044 <_ZL5myVar>   400561:       5d                      pop    %rbp   400562:       c3                      retq    

You can see that the actual addresses of static variables in different modules are indeed different: 0x601040 for foo.c and 0x601044 for bar.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:

0000000000601040 l     O .data  0000000000000004              _ZL5myVar 0000000000601044 l     O .data  0000000000000004              _ZL5myVar 

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.

like image 134
iehrlich Avatar answered Sep 22 '22 22:09

iehrlich


so.s make the linker happy

.globl _start _start: b _start 

one.c

static unsigned int hello = 4; static unsigned int one = 5; void fun1 ( void ) {     hello=5;     one=6; } 

two.c

static unsigned int hello = 4; static unsigned int two = 5; void fun2 ( void ) {     hello=5;     two=6; } 

three.c

static unsigned int hello = 4; static unsigned int three = 5; void fun3 ( void ) {     hello=5;     three=6; } 

first off if you optimize then this is completely dead code and you should not expect to see any of these variables. The functions are not static so they dont disappear:

Disassembly of section .text:  08000000 <_start>:  8000000:   eafffffe    b   8000000 <_start>  08000004 <fun1>:  8000004:   e12fff1e    bx  lr  08000008 <fun2>:  8000008:   e12fff1e    bx  lr  0800000c <fun3>:  800000c:   e12fff1e    bx  lr 

If you dont optimize then

08000000 <_start>:  8000000:   eafffffe    b   8000000 <_start>  08000004 <fun1>:  8000004:   e52db004    push    {r11}       ; (str r11, [sp, #-4]!)  8000008:   e28db000    add r11, sp, #0  800000c:   e59f3020    ldr r3, [pc, #32]   ; 8000034 <fun1+0x30>  8000010:   e3a02005    mov r2, #5  8000014:   e5832000    str r2, [r3]  8000018:   e59f3018    ldr r3, [pc, #24]   ; 8000038 <fun1+0x34>  800001c:   e3a02006    mov r2, #6  8000020:   e5832000    str r2, [r3]  8000024:   e1a00000    nop         ; (mov r0, r0)  8000028:   e28bd000    add sp, r11, #0  800002c:   e49db004    pop {r11}       ; (ldr r11, [sp], #4)  8000030:   e12fff1e    bx  lr  8000034:   20000000    andcs   r0, r0, r0  8000038:   20000004    andcs   r0, r0, r4  0800003c <fun2>:  800003c:   e52db004    push    {r11}       ; (str r11, [sp, #-4]!)  8000040:   e28db000    add r11, sp, #0  8000044:   e59f3020    ldr r3, [pc, #32]   ; 800006c <fun2+0x30>  8000048:   e3a02005    mov r2, #5  800004c:   e5832000    str r2, [r3]  8000050:   e59f3018    ldr r3, [pc, #24]   ; 8000070 <fun2+0x34>  8000054:   e3a02006    mov r2, #6  8000058:   e5832000    str r2, [r3]  800005c:   e1a00000    nop         ; (mov r0, r0)  8000060:   e28bd000    add sp, r11, #0  8000064:   e49db004    pop {r11}       ; (ldr r11, [sp], #4)  8000068:   e12fff1e    bx  lr  800006c:   20000008    andcs   r0, r0, r8  8000070:   2000000c    andcs   r0, r0, r12  08000074 <fun3>:  8000074:   e52db004    push    {r11}       ; (str r11, [sp, #-4]!)  8000078:   e28db000    add r11, sp, #0  800007c:   e59f3020    ldr r3, [pc, #32]   ; 80000a4 <fun3+0x30>  8000080:   e3a02005    mov r2, #5  8000084:   e5832000    str r2, [r3]  8000088:   e59f3018    ldr r3, [pc, #24]   ; 80000a8 <fun3+0x34>  800008c:   e3a02006    mov r2, #6  8000090:   e5832000    str r2, [r3]  8000094:   e1a00000    nop         ; (mov r0, r0)  8000098:   e28bd000    add sp, r11, #0  800009c:   e49db004    pop {r11}       ; (ldr r11, [sp], #4)  80000a0:   e12fff1e    bx  lr  80000a4:   20000010    andcs   r0, r0, r0, lsl r0  80000a8:   20000014    andcs   r0, r0, r4, lsl r0  Disassembly of section .data:  20000000 <hello>: 20000000:   00000004    andeq   r0, r0, r4  20000004 <one>: 20000004:   00000005    andeq   r0, r0, r5  20000008 <hello>: 20000008:   00000004    andeq   r0, r0, r4  2000000c <two>: 2000000c:   00000005    andeq   r0, r0, r5  20000010 <hello>: 20000010:   00000004    andeq   r0, r0, r4 

there are three hello variables created (you should notice by now that there is no reason to start up the debugger this can all be answered by simply examining the compiler and linker output, the debugger just gets in the way)

 800000c:   e59f3020    ldr r3, [pc, #32]   ; 8000034 <fun1+0x30>   8000034:   20000000    andcs   r0, r0, r0   8000044:   e59f3020    ldr r3, [pc, #32]   ; 800006c <fun2+0x30>   800006c:   20000008    andcs   r0, r0, r8   800007c:   e59f3020    ldr r3, [pc, #32]   ; 80000a4 <fun3+0x30>   80000a4:   20000010    andcs   r0, r0, r0, lsl r0  20000000 <hello>: 20000000:   00000004    andeq   r0, r0, r4  20000008 <hello>: 20000008:   00000004    andeq   r0, r0, r4  20000010 <hello>: 20000010:   00000004    andeq   r0, r0, r4 

each function is accessing its own separate version of the static global. They are not combined into one shared global.

like image 41
old_timer Avatar answered Sep 21 '22 22:09

old_timer


The answers thus far have demonstrated that it should work as written, but the actual answer is only in the comments so I will post it as an answer.

What you’re seeing is a debugger artifact, not the real situation. In my experience, this should be your first guess of any truely wierd observation within the debugger. Verify the observation in the actual running program before going on. E.g. an old fashioned debug printf statement.

like image 22
JDługosz Avatar answered Sep 22 '22 22:09

JDługosz