不同的静态全局变量共享相同的内存地址

hel*_*ore 26 c gcc arm gnu-arm

摘要

我有几个C源文件,它们都声明了个别名称相同的静态全局变量.我的理解是每个文件中的静态全局变量应该只在该文件中可见,并且不应该应用外部链接,但实际上我可以在调试时看到同名的变量共享相同的内存地址.

就像static关键字被忽略一样,全局变量被视为extern相反.为什么是这样?

示例代码

foo.c的:

/* Private variables -----------------------------------*/
static myEnumType myVar = VALUE_A;

/* Exported functions ----------------------------------*/
void someFooFunc(void) {
    myVar = VALUE_B;
}
Run Code Online (Sandbox Code Playgroud)

bar.c:

/* Private variables -----------------------------------*/
static myEnumType myVar = VALUE_A;

/* Exported functions ----------------------------------*/
void someBarFunc(void) {
    myVar = VALUE_C;
}
Run Code Online (Sandbox Code Playgroud)

baz.c:

/* Private variables -----------------------------------*/
static myEnumType myVar = VALUE_A;

/* Exported functions ----------------------------------*/
void someBazFunc(void) {
    myVar = VALUE_D;
}
Run Code Online (Sandbox Code Playgroud)

调试观察

  1. myVar = ...在每个函数内的行上设置断点.
  2. 从main 调用someFooFunc,someBarFunc并按someBazFunc顺序调用.
  3. 内部someFooFunc myVar最初设置为VALUE_A,在踩到线路后设置为VALUE_B.
  4. 内部someBarFunc myVar由于某种原因VALUE_B在步进之前初始设置为,而不是VALUE_A我所期望的,表明链接器可能已经基于具有相同名称的单独全局变量合并.
  5. 这同样适用于someBazFunc当它被调用.
  6. 如果我使用调试器来评估&myVar每个断点处的值,则给出相同的地址.

工具和标志

工具链:GNU ARM GCC(6.2 2016q4)

编译器选项:

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"
Run Code Online (Sandbox Code Playgroud)

链接器选项:

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 ...
Run Code Online (Sandbox Code Playgroud)

ieh*_*ich 23

注意:我确实知道OP的目标平台是ARM,但是我仍然在发布x86方面的答案.原因是,我没有派上用场的ARM后端,而问题不仅限于特定的架构.

这是一个简单的试验台.请注意,我使用的int是自定义enumtypedef,因为它根本不重要.

foo.c的

static int myVar = 1;

int someFooFunc(void)
{
        myVar += 2;
        return myVar;
}
Run Code Online (Sandbox Code Playgroud)

bar.c

static int myVar = 1;

int someBarFunc(void)
{
        myVar += 3;
        return myVar;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

我正在使用GCC 4.8.4在x86_64 Ubuntu 14.04上编译它:

$ g++ main.c foo.c bar.c
$ ./a.out
3
4
Run Code Online (Sandbox Code Playgroud)

有效地获得这样的结果意味着myVar变量在foo.cbar.c不同.如果你看一下反汇编(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   
Run Code Online (Sandbox Code Playgroud)

您可以看到不同模块中静态变量的实际地址确实不同:0x601040for foo.c0x601044for bar.c.但是,它们与单个符号相关联_ZL5myVar,这实际上搞砸了GDB逻辑.

您可以通过以下方式仔细检查objdump -t ./a.out:

0000000000601040 l     O .data  0000000000000004              _ZL5myVar
0000000000601044 l     O .data  0000000000000004              _ZL5myVar
Run Code Online (Sandbox Code Playgroud)

又一次,不同的地址,相同的符号.GDB如何解决这一冲突完全取决于实现.

我坚信这也是你的理由.但是,要确保双重,您可能需要在您的环境中尝试这些步骤.