为什么GCC以不同方式存储全局和静态int?

时间:2018-01-23 05:55:46

标签: c gcc objdump

这是我的C程序,包含一个静态,两个全局,一个本地和一个外部变量。

#include <stdio.h>

int gvar1;
int gvar2 = 12;
extern int evar = 1;
int main(void)
{
    int lvar;
    static int svar = 4;
    lvar = 2;
    gvar1 = 3;
    printf ("global1-%d global2-%d local+1-%d static-%d extern-%d\n", gvar1, gvar2, (lvar+1), svar, evar);
return 0;
}

请注意,gvar1,gvar2,evar,lvar和svar都被定义为整数。

我使用objdump反汇编代码,debug_str显示如下:

Contents of section .debug_str:
 0000 76617269 61626c65 732e6300 6c6f6e67  variables.c.long
 0010 20756e73 69676e65 6420696e 74002f75   unsigned int./u
 0020 73657273 2f686f6d 6534302f 72616f70  sers/home40/raop
 0030 2f626b75 702f6578 616d706c 65730075  /bkup/examples.u
 0040 6e736967 6e656420 63686172 00737661  nsigned char.sva
 0050 72006d61 696e006c 6f6e6720 696e7400  r.main.long int.
 0060 6c766172 0073686f 72742075 6e736967  lvar.short unsig
 0070 6e656420 696e7400 67766172 31006776  ned int.gvar1.gv
 0080 61723200 65766172 00474e55 20432034  ar2.evar.GNU C 4
 0090 2e342e36 20323031 31303733 31202852  .4.6 20110731 (R
 00a0 65642048 61742034 2e342e36 2d332900  ed Hat 4.4.6-3).
 00b0 73686f72 7420696e 7400               short int.

为什么会显示以下内容?

unsigned char.svar
long int.lvar
short unsigned int.gvar1.gvar2.evar

GCC如何确定应将哪种类型存储为?

我正在使用GCC 4.4.6 20110731(Red Hat 4.4.6-3)

2 个答案:

答案 0 :(得分:4)

  

为什么会显示以下内容?

简单回答:它没有显示您的想法,但它显示出来:

1 "variables.c"
2 "long unsigned int"
2a "unsigned int"
2b "int"
3 "/users/home40/raop/bkup/examples"
4 "unsigned char"
4a "char"
5 "svar"
6 "main"
7 "long int"
8 "lvar"
9 "short unsigned int"
10 "gvar1"
11 "gvar2"
12 "evar"
13 "GNU C 4.4.6 20110731 (Red Hat 4.4.6-3)"
14 "short int"

该部分名为.debug_str;它包含一个由NUL字节分隔的字符串列表。这些字符串按任意顺序,它们由.debug_info部分引用。因此svar跟随unsigned char的事实根本没有意义

.debug_info部分包含实际的调试信息。本节不包含字符串。相反,它将包含这样的信息:

    ...
Item 123:
    Type of information: Data type
    Name: 2b /* String #2b in ".debug_str" is "int" */
    Kind of data type: Signed integer
    Number of bits: 32
    ... some more information ...
Item 124:
    Type of information: Global variable
    Name: 8 /* "lvar" */
    Data type defined by: Item 123
    Stored at: Address 0x1234
    ... some more information ...
Begin item 125:
    Type of information: Function
    Name: 6 /* "main" */
    ... some more information ...
Item 126:
    Type of information: Local variable
    Name: 5 /* "svar" */
    Data type defined by: Item 123
    Stored at: Address 0x1238
    ... some more information ...
End item 125 /* Function "main" */
Item 127:
    ...

您可以使用以下命令查看此信息:

readelf --debug-dump filename.o
  

为什么GCC以不同方式存储全局和静态int?

我编写了两次示例:一次是优化,一次是没有优化。

没有优化svargvar1以相同的方式存储完全:数据类型int,存储在固定地址上。 lvar是:数据类型int,存储在堆栈中。

优化lvarsvar以相同的方式存储:数据类型:int,根本不存储,而是将它们视为常量值。

(这是有道理的,因为这些变量的值永远不会改变。)

答案 1 :(得分:3)

C11规范(读n1570) - 或更旧的C标准 - 没有定义存储全局变量或静态变量的地址或偏移量,因此实现(您的gcc编译器你的ld链接器可以随意放在任何地方。

data segments的组织和布局是一个实施细节。

您可能希望阅读有关DWARF的更多信息,以了解调试信息,这对gdb调试器很有用。

如果您想了解它们的工作原理,您可能需要了解有关linkers and loadersELF格式的更多信息。在Linux上,有几个实用程序可以检查elf(5)个文件,包括objdump(1)readelf(1)nm(1)

请注意,您的GCC4.4是GCC的旧版本。当前版本为GCC7,GCC8将在几周内发布(2018年春季)。我强烈建议您升级编译器。

如果您需要了解数据片段的组织方式和原因以及实施选择此类布局的原因,您可以利用gccld(来自{{3 {}}是binutils,并详细研究其来源。您需要多年的工作,因为它们是复杂的软件(超过一千万行源代码)。

如果您正好开始研究free software的内部,请务必研究最新版本。海湾合作委员会社区的大多数人可能已经忘记了GCC4.4(2009年发布)的细节。自那个古老的东西以来,GCC中的很多东西都发生了变化。几年前,我写过很多关于GCC内部的幻灯片,请参阅GCC

BTW,数据段或其中的变量布局可能会随documentation of GCC MELT而变化。可能会发生lvar不在内存中(例如仅停留在寄存器中);可能会发生静态变量被删除(使用类似optimization options之类的东西)等。

对于单个 as-if rule foo.c,您可以使用gcc -fverbose-asm -S -O foo.c将其编译为汇编代码,并查看发出的foo.s汇编代码。

要了解更多ld链接器的工作原理,您可以查看一些相关的translation unit。您可以在编译和链接命令中使用ld(而不是gcc)查找如何从gcc -v调用gcc

在大多数情况下,你不应该关心关于特定抵消(linker scriptobject files)或地址(executablesvirtual address space 3}})全局变量或静态变量。还要注意processASLR文件系统可用于了解您的流程。

(你的问题严重缺乏一些动力和背景)