【问题标题】:When is a variable placed in `.rdata` section and not in `.text` section?什么时候将变量放在“.rdata”部分而不是“.text”部分?
【发布时间】:2013-04-15 22:50:23
【问题描述】:

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

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

当我通过gcc -o a.out sample.c -Wl,Map,test.map 构建和转储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. 在确定.rdata.text 中的位置时是否考虑const 变量的范围?

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

  3. 如果局部const变量过多,则对应.text部分的大小会显着增加。在这种情况下推荐的编程实践是什么?

非常感谢。

【问题讨论】:

    标签: c gcc linker


    【解决方案1】:

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

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

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

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

    【讨论】:

      【解决方案2】:

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

      在第二个示例中,a 被设为全局变量,并将位于您所观察到的 .data 部分中。

      那么,让我们根据上述情况回答您的问题。

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

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

        1234563如果有一个自然分支可以隐藏常量表,则一次跳转就足够了。因此,常数占用了它们需要的空间,而不是(更多)。如果它们都被实现为立即寻址,那么如果有更有效的方法以这种方式获取常量,编译器可能仍会决定从中制作一个表格。

      【讨论】:

      • 感谢您提供非常丰富的回复。我确实有几点需要澄清。一,在我的问题中,与.data 部分相比,变量位于.rdata 部分。该问题的主要目的是了解只读部分的处理,即.rdata.text。至于第 1 点,我相信对于const,虽然我们可能会通过类型转换来欺骗编译器,但运行时会抛出异常。示例:cfiddle.net/zsPfEg.
      • 对不起另一个问题:您提到了PC相对寻址。在嵌入式系统中,我可能具有将特定代码部分加载到更快内存(TI DSP-BIOS 用于支持这一点)的灵活性。在这种情况下,由于当前PC可能在SDRAM中而实际数据在SRAM中,如何处理偏移量计算?
      • 您关于 SDRAM 与 SRAM 的问题超出了我的理解。其他人应该在那里帮助你。我假设常量表也将移动到 SDRAM。
      猜你喜欢
      • 2018-11-12
      • 1970-01-01
      • 1970-01-01
      • 2011-10-28
      • 2019-10-24
      • 1970-01-01
      • 2016-01-09
      • 2019-08-16
      • 1970-01-01
      相关资源
      最近更新 更多