我想了解.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
部分。
我的问题是:
在const
或.rdata
确定.text
变量时,是否考虑了.text
变量的范围?
从我的实验中,我发现变量在分配到.rdata
部分时需要8个字节,而在const
部分则需要4个字节。造成这种差异的原因是什么?
如果本地.text
变量太多,则相应{{1}}部分的大小将显着增加。在这种情况下,推荐的编程实践是什么?
非常感谢提前。
答案 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
部分,这也是您观察到的部分。
让我们回答你的问题,并考虑到上述问题。
const
并不意味着它是一个常量(uuh ??),这意味着程序员承诺不写它。编译器会警告如果你这样做,或者你有可能会这样做,但仍会编译。因此,您的案例之间的差异实际上是a
是第一个示例中的局部变量,而第二个示例中是全局变量。嗯,我只是试着公然写一个const int
,它确实给出了一个错误,而不仅仅是一个警告。但是,可以通过引用将这样的const
传递给将改变它的函数。交叉单元的东西甚至可能被编译器检测不到。
如果它位于.data
部分,则它是对象的大小,在您的情况下为4个字节。如果10
位于代码中,无论是在常量表中还是作为立即寻址模式,那么它实际上取决于体系结构的大小以及对周围代码的影响。
如果您的代码中有许多常量,并且该体系结构支持pc相对寻址(如ARM),那么它可能会将所有这些常量定位在靠近使用它们的代码的位置,但所有这些常量都在一起一个跳跃就足以跳过它,如果有一个自然分支,常量表可以隐藏,则不会跳转。因此,常数占用了他们需要的空间,而不是(更多)。如果它们都被实现为立即寻址,那么如果有更有效的方法来获取常量,编译器仍然可以决定从中创建一个表。