什么时候变量放在`.rdata`部分而不是`.text`部分?

时间:2013-04-15 22:50:24

标签: c gcc linker

我想了解.rdata部分与.text部分的含义。我正在尝试一个简单的程序,如下所示

int main()
{
    const int a = 10;
    printf("%d\n", a);
    return 0;
}

当我通过map构建并转储gcc -o a.out sample.c -Wl,Map,test.map文件并搜索sample.o时,我发现以下分配

.text          0x0040138c       0x34 sample.o
.data          0x00402000        0x0 sample.o
.rdata         0x00403064        0x8 sample.o
.eh_frame      0x00404060       0x38 sample.o
.bss           0x00405020        0x0 sample.o

现在,如果我稍微修改我的程序,使a全局变量为

const int a = 10;
int main()
{
     printf("%d\n", a);
     return 0;
}

重复上述步骤,我发现分配如下

.text          0x0040138c       0x2c sample.o
.data          0x00402000        0x0 sample.o
.rdata         0x00403064        0xc sample.o
.eh_frame      0x00404060       0x38 sample.o
.bss           0x00405020        0x0 sample.o

其中明确显示a.rdata部分分配为

.rdata         0x00403064        0xc sample.o
               0x00403064            a

通过这些实验,我了解global const已分配到.rdata部分,而.text部分尺寸已降低。因此,我假设在第一个示例中a被分配到.text部分。

我的问题是:

  1. const.rdata确定.text变量时,是否考虑了.text变量的范围?

  2. 从我的实验中,我发现变量在分配到.rdata部分时需要8个字节,而在const部分则需要4个字节。造成这种差异的原因是什么?

  3. 如果本地.text变量太多,则相应{{1}}部分的大小将显着增加。在这种情况下,推荐的编程实践是什么?

  4. 非常感谢提前。

2 个答案:

答案 0 :(得分:6)

在第一种情况下,变量被声明为局部变量。它具有“自动”存储持续时间,这意味着它在封闭范围的末尾消失。由于存储持续时间,它不能永久占用任何内存(无论const如何都是如此)。因此,它通常存储在堆栈中或寄存器中。

在第二种情况下,变量被声明为全局变量。它具有静态存储持续时间,因此它在程序的生命周期内持续存在。这可以存储在很多地方,例如.data.bss.text.rdata(或.rodata)。

.data通常用于具有某些预定义(非零)内容的可写静态数据,例如全球int foo = 42;.bss用于初始化为零(或未初始化)的可写静态数据。 rdata用于常量静态数据,如字符串和const变量。当然,这些用途都是“一般”的,并且可能因编译器而异。

那么为什么.text在第一种情况下会变得更大?这是因为编译器必须生成一些额外的指令来在堆栈或寄存器中加载10

答案 1 :(得分:2)

此行为因目标而异。在您认为a最终位于.text部分的第一个示例中,10(可能)最终会出现在文本部分中并被加载到a中在堆栈上。一些具有例如pc相对寻址的架构会将这些常量放在代码之间的某处。其他架构会将10编码为立即寻址模式,这也会导致稍大的代码大小。

在第二个示例中,a成为一个全局变量,并且位于.data部分,这也是您观察到的部分。

让我们回答你的问题,并考虑到上述问题。

    C中的
  1. const并不意味着它是一个常量(uuh ??),这意味着程序员承诺不写它。编译器会警告如果你这样做,或者你有可能会这样做,但仍会编译。因此,您的案例之间的差异实际上是a是第一个示例中的局部变量,而第二个示例中是全局变量。嗯,我只是试着公然写一个const int,它确实给出了一个错误,而不仅仅是一个警告。但是,可以通过引用将这样的const传递给将改变它的函数。交叉单元的东西甚至可能被编译器检测不到。

  2. 如果它位于.data部分,则它是对象的大小,在您的情况下为4个字节。如果10位于代码中,无论是在常量表中还是作为立即寻址模式,那么它实际上取决于体系结构的大小以及对周围代码的影响。

  3. 如果您的代码中有许多常量,并且该体系结构支持pc相对寻址(如ARM),那么它可能会将所有这些常量定位在靠近使用它们的代码的位置,但所有这些常量都在一起一个跳跃就足以跳过它,如果有一个自然分支,常量表可以隐藏,则不会跳转。因此,常数占用了他们需要的空间,而不是(更多)。如果它们都被实现为立即寻址,那么如果有更有效的方法来获取常量,编译器仍然可以决定从中创建一个表。