text, data and bss: Code and Data Size Explained

本文深入解析了ARM/Kinetis项目中使用GCC编译器输出的代码占用空间信息,详细介绍了text、data、bss等代码段的含义及其在FLASH和RAM中的分配情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文原文链接

In “Code Size Information with gcc for ARM/Kinetis” I use an option in the ARM gcc tool chain for Eclipse to show me the code size:

在“在ARM/Kinetis项目中用GCC编译器输出代码占用空间信息”一文中我用了一个用于Eclipse的ARM gcc工具链选项来输出显示代码大小:

  text       data        bss        dec        hex    filename
0x1408       0x18      0x81c       7228       1c3c    size.elf

I have been asked by a reader of this blog what these item numbers really mean. Especially: what the heck is ‘bss’???

一个读者在这篇博客上问我这些字段的数值的真正含义, 特别是’bss’字段。

Note: I’m using the ARM GNU ‘printsize’ utility for gcc, with an example for Kinetis-L (KL25Z).
注:我用来输出这些代码空间信息工具是ARM GNU ‘printsize’,并且以Kinetis-L (KL25Z)作为示例。

text

‘text’ is what ends up in FLASH memory. I can show this with adding

text段最终是存放在FLASH存储器中的。通过增加如下代码到程序中:

void foo(void) {
  /* dummy function to show how this adds to 'text' */
}

to my program, the ‘text’ part increases so:

接着text段的大小增长如下:

   text       data        bss
 0x1414       0x18      0x81c

Likewise, my new function ‘foo’ gets added to the .text segment, as I can see in the map file generated by the linker:

同样的,在链接器产生的map文件里也能看到我新增加的函数foo添加至text段。

*(.text*)
 .text.foo      0x000008c8        0x8 ./Sources/main_c.o
                0x000008c8                foo

But it does not only contain functions, it has constant data as too. If I have a constant table like

但text段不仅包含函数,还有常量。例如我有如下的一个常量表:

const int table[] = {5,0,1,5,6,7,9,10};

then this adds to ‘text’ too. That variable ‘table’ will be in FLASH, initialized with the values specified in the source.

这将会被添加到‘text’段,这个变量‘table’将会在FLASH中,被代码中指定的值所初始化。

Another thing which is included in ‘text’ is the interrupt vector table (more on this later).

还有一样包含在text段里的东西是中断向量表(后续详细说明),因此这也被计算到text段。变量table也会放在FLASH中,并以源码中的数据初始化。

In summary: ‘text’ is what ends up typically in FLASH and has code and constant data.

小结:text段最终存放在FLASH里而,所包含的内容是代码和常量。

data

‘data’ is used for initialized data. This is best explained with the following (global/extern) variable:

data段是用于初始化数据。用如下的变量(全局/外部)可以解释得很清楚:

int32_t myVar = 0x12345678;

Adding above variable to my application will increase the ‘data’ portion by 4 bytes:

加入上述变量会导致我的应用的data部分增长四个字节:

   text       data        bss
 0x1414       0x1c      0x81c

This variable ‘myVar’ is not constant, so it will end up in RAM. But the initialization (0x12345678) is constant, and can live in FLASH memory. The initialization of the variable is done during the normal ANSI startup code. The code will assign/copy the initialization value. This is sometimes named ‘copy-down’. For the startup code used by CodeWarrior for MCU10.3 for Kinetis-L (ARM Cortex-M0+), this is performed in __copy_rom_sections_to_ram():

变量myVar不是常量,所以最终会存放于RAM内。但是初始值(0x12345678)是一个常量,因此可以放在FLASH里。这个变量的初始化在常规的ANSI启动代码中完成。有时这叫做“原样复制”。对用于Kinetics-L (ARM Cortex-M0+)的CodeWarrior的MCU10.3版本所用的启动代码而言,这种操作在__copy_rom_sections_to_ram()中进行。

ARM Startup Code Initializing Variables

Just one thing to consider: my variable ‘myVar’ will use space in RAM (4 bytes in my case), plus space in FLASH/ROM for the initialization value (0x12345678). So I need to count the ‘data’ size twice: that size will end up in RAM, plus will occupy FLASH/ROM. That amount of data in FLASH is not counted in the text portion.

还有一件事情需要考虑:变量myVar将占用RAM的空间(本例中占4个字节),还需累加在FLASH/ROM中初始值(0x12345678)所占用的空间。所以我需要计算data的段的大小两次:即RAM中占的加上FLASH/ROM中占的。而且FLASH中所占的部分并不会计入text部分。

In summary : The ‘data’ only has the initialization data (in my example 0x12345678. And not the variable (myVar).
小结:data段仅包含初始化所用的数据(本例中的0x12345678),并且不含变量(myVar)。

bss

The ‘bss’ contains all the uninitalized data.

bss段包含着所有未初始化的数据。

bss (or .bss, or BSS) is the abbreviation for ‘Block Started by Symbol’ by an old assembler (see this link).
bss(.bss, BSS ) 是旧式汇编器中‘Block Started by Symbol’的简称(详情参看 link)。

This is best explained with following (global/extern) variable:

用如下的变量(全局/外部)可以解释得很清楚:

int32_t myGlobal;

Adding this variable will increase the ‘bss’ portion by 4:

加入上述变量会导致bss部分增长4个字节:

   text       data        bss
 0x1414       0x18      0x820

I like to remember ‘bss’ as ‘Better Save Space’ ?. As bss ends up in RAM, and RAM is very valuable for a microcontroller, I want to keep the amount of variables which end up in the .bss at the absolute minimum.
我喜欢把bss当作‘Better Save Space’(最好节省空间)的简称。因为bss最终存放在RAM内,而且RAM对于单片机来讲是一种宝贵的资源,所以我会令存放在bss中的变量数量尽可能的少。

The bss segment is initialized in the startup code by the zero_fill_bss() function:

启动代码中调用zero_fill_bss()函数初始化bss段:

static void zero_fill_bss(void)
{
    extern char __START_BSS[];
    extern char __END_BSS[];
 
    memset(__START_BSS, 0, (__END_BSS - __START_BSS));
}

dec

The ‘dec’ (as a decimal number) is the sum of text, data and bss:

dec(decimal的缩写,即十进制数)是text,data和bss的算术和。

dec = text + data + bss

Size – GNU Utility

The size (or printsize) GNU utility has more options:
GNU工具 size ( printsize)有许多选项:

size [-A|-B|--format=compatibility]
          [--help]
          [-d|-o|-x|--radix=number]
          [--common]
          [-t|--totals]
          [--target=bfdname] [-V|--version]
          [objfile...]

The ‘System V’ option can be set directly in the Eclipse panel:

‘System V’选项能直接在Eclipse中设置:
GNU Print Size Option in CodeWarrior for MCU10.3
It produces similar information as shown above, but with greater detail.

这将会输出和上面差不多的代码尺寸信息,但是会更详细。

To illustrate this, I use

为了解释这点,我用如下数组变量做示例:

int table[] = {1,2,3,4,5};

While in ‘Berkeley’ mode I get:

当选择‘Berkeley’模式时输出如下:

   text       data        bss        dec        hex    filename
 0x140c       0x2c      0x81c       7252       1c54    size.elf

I get this in ‘System V’ mode:

当选择’System V’模式时输出如下:

section                size         addr
.interrupts            0xc0          0x0
.text                0x134c        0x800
.data                  0x14   0x1ffff000
.bss                   0x1c   0x1ffff014
.romp                  0x18   0x1ffff030
._user_heap_stack     0x800   0x1ffff048
.ARM.attributes        0x31          0x0
.debug_info          0x2293          0x0
.debug_abbrev         0xe66          0x0
.debug_loc           0x27df          0x0
.debug_aranges        0x318          0x0
.debug_macinfo      0x53bf3          0x0
.debug_line          0x1866          0x0
.debug_str            0xc23          0x0
.comment               0x79          0x0
.debug_frame          0x594          0x0
Total               0x5defe

I’m using an ARM Cortex-M0+ in my example, so addresses greater 0x1ffff000 are in RAM.

例子中我用的是ARM Cortex-M0+内核,故在RAM中的地址从0x1ffff000开始。

The lines from .ARM.attributes up to .debug_frame are not ending up in the target, they are debug and other information.

其中.ARM.attributes up 到 .debug_frame所列内容最终不会放在目标硬件中,这些事调试或者其他信息。

.interrupts is my interrupt vector table, and .text is my code plus constants, and is in FLASH memory. That makes the 0xc0+0x134c=0x140c for text in ‘Berkeley’.

.interrupts其是本例的中断向量表,.text是存放在FLASH里的代码和常量。故‘Berkeley’下text段的大小即为:0xc0+0x134c=0x140c。

.bss is my uninitialized (zero-outed) variable area. Additionally there is .user_heap_stack: this is the heap defined in the ANSI library for malloc() calls. That makes the total of 0x1c+0x800=0x81c shown in ‘Berkeley’ format.

.bss是本例中未初始化(为0)变量区域。此外还有一个.user_heap_stack:段用于预留ANSI库中malloc()调用分配的内存。故‘Berkeley’下bss段的大小即为:0x1c+0x800=0x81c。

.data is for my initialized ‘table[]’ variable in RAM (5*4 bytes=0x14)

.data存放了本例中存放在RAM内的初始化了的table[]变量。

The .romp is used by the linker for the ‘copy-down’ and initialization of .data. But it looks confusing: it is shown with addresses in RAM? Checking the linker map file shows:

romp是链接器用于需要‘copy-down’的data中的初始化数据。但是这看起来有点迷:这显示的地址是RAM的地址?链接器生成的map文件说如是:

.romp           0x1ffff030       0x18 load address 0x00001b60
                0x00001b60                __S_romp = _romp_at
                0x1ffff030        0x4 LONG 0x1b4c ___ROM_AT
                0x1ffff034        0x4 LONG 0x1ffff000 _sdata
                0x1ffff038        0x4 LONG 0x14 ___data_size
                0x1ffff03c        0x4 LONG 0x0
                0x1ffff040        0x4 LONG 0x0
                0x1ffff044        0x4 LONG 0x0

Ah! That actually is not in RAM, but in FLASH: the linker maps this to the FLASH address 0x1b60! So this size 0x18 really needs to be added to the FLASH size too!

啊!实际上这并不是在RAM里,而是在FLASH里:链接器映射这段到FLASH地址0x1b60中!故这个0x18大小的尺寸实际上也需要加到FLASH所占空间里去!

Summary

I hope I have sorted out things in a correct way. The way how the initialized data is reported might be confusing. But with the right knowledge (and .map file in mind), things get much clearer:

我希望我把事情表达正确了。虽然初始化数据的报告信息可能挺迷的,但是通过正确的分析(脑中的 .mao文件),事情反而更清楚了。

text’ is my code, vector table plus constants.

text放的是是代码,向量表及常量。

data’ is for initialized variables, and it counts for RAM and FLASH. The linker allocates the data in FLASH which then is copied from ROM to RAM in the startup code.

data放的是初始化的变量,且同时计入RAM和FLASH。链接器把数据分配在FLASH中然后在启动代码中从ROM拷贝到RAM。

bss’ is for the uninitialized data in RAM which is initialized with zero in the startup code.

bss放的是RAM中未初始化的变量,这些变量将在启动代码中填充0。

Happy Sizing

<think>好的,用户现在问的是关于C语言中.data、.bss、.text和.rodata这几个段的问题。我需要先回忆一下这些段各自的作用和区别。 首先,用户之前问了如何指定头文件路径编译,现在转向目标文件的结构,可能是在学习编译链接过程或者底层的内存管理。用户可能是刚开始学习C语言,或者是想更深入了解程序的内存布局。需要以简明的方式解释每个段的作用,同时指出它们的区别和实际应用。 接下来,我要确保每个段的解释准确无误。.text段是存放可执行代码的,通常是只读的。.data段存放已初始化的全局和静态变量,而.bss段则是未初始化的,或者初始化为0的全局和静态变量,这里要注意区分初始化和未初始化的区别。.rodata是只读数据,比如字符串常量和const变量。此外,还要提到这些段在内存中的位置,比如.text和.rodata通常是只读的,防止程序意外修改。 用户可能需要例子来理解,比如用代码示例说明不同变量存放在哪个段。比如全局变量初始化和未初始化的例子,静态变量的例子,以及const修饰的变量是否进入.rodata。同时,要提到.bss段在目标文件中不占空间,但在运行时分配内存,这样可以节省磁盘空间。 还要考虑用户可能的误区,比如未初始化的静态变量是否属于.bss,或者const变量的存储位置是否因编译器而异。需要指出这些细节,帮助用户正确理解。 最后,总结各个段的区别,用表格或对比的方式,让用户一目了然。同时,可以提到实际应用中的注意事项,比如优化代码体积或内存使用时的考虑因素,比如将大型未初始化数组放在.bss段可以减小可执行文件的大小。 需要确保语言通俗易懂,避免过于技术化的术语,但也要准确。可能用户在学习过程中遇到链接错误或者内存问题,理解这些段有助于调试。因此,强调这些段在程序运行中的作用和影响,帮助用户在实际编程中应用这些知识。</think>在C程序的内存布局中,`.data`、`.bss`、`.text`和`.rodata`是目标文件中的关键段(section),其作用如下: --- ### 一、核心作用对照表 | 段名称 | 存储内容 | 内存特性 | 初始状态 | |-----------|-----------------------------------|----------------|----------------| | `.text` | **可执行代码** | 只读 | 编译时确定 | | `.rodata` | **只读数据**(字符串常量、const变量) | 只读 | 编译时写入 | | `.data` | **已初始化的全局/静态变量** | 可读写 | 运行时加载初始值 | | `.bss` | **未初始化或零初始化的全局/静态变量** | 可读写 | 运行时清零 | --- ### 二、详细说明 #### 1. `.text` 代码段 - **存储内容**:编译后的机器指令 - **特性**: - 多个进程可共享同一份代码 - 具有**执行权限**但**不可写** - **示例**: ```c void func() { // 函数体代码存储在此段 int x = 10; } ``` #### 2. `.rodata` 只读数据段 - **存储内容**: - 字符串字面量 `"Hello World"` - `const` 修饰的全局变量 - `switch-case` 跳转表 - **特殊限制**: ```c const int MAX = 100; // 进入.rodata char* ptr = "ReadOnly"; // 字符串本身在.rodata ``` #### 3. `.data` 数据段 - **存储内容**: - 显式初始化的全局变量 - 显式初始化的静态变量 - **特点**: - 占用磁盘空间(存储初始值) - 程序加载时复制到内存 - **示例**: ```c int initialized = 42; // 全局变量 static float pi = 3.14f; // 静态变量 ``` #### 4. `.bss` 未初始化段 - **存储内容**: - 未初始化的全局/静态变量 - 零初始化的全局/静态变量 - **优势**: - **不占用磁盘空间**(仅记录内存大小) - 程序加载时自动填充零 - **示例**: ```c int uninitialized; // 进入.bss static char buffer[1024]; // 进入.bss int zero = 0; // 零初始化也进.bss ``` --- ### 三、内存布局示例 ``` 高地址 +------------------+ | 栈(stack) | | ↓ 生长方向 | +------------------+ | 堆(heap) | | ↑ 生长方向 | +------------------+ | .bss | ← 未初始化数据 +------------------+ | .data | ← 已初始化数据 +------------------+ | .rodata | ← 只读数据 +------------------+ | .text | ← 程序代码 低地址 ``` --- ### 四、验证实验 通过 `size` 命令查看各段大小: ```bash # 编译测试程序 gcc -o test test.c # 查看段信息 size test ``` 输出示例: ``` text data bss dec hex filename 1024 256 128 1408 580 test ``` --- ### 五、编程注意事项 1. **`.bss` 段优化**:大型零初始化数组应声明为全局/静态变量 ```c static char big_buffer[1MB]; // 优于栈上声明(避免栈溢出) ``` 2. **`.rodata` 保护**:尝试修改只读数据段会导致段错误 3. **`.data` 初始化**:显式初始化非零值变量会增大可执行文件体积 4. **多线程安全**:全局变量存储在`.data`/`.bss`中,需注意线程同步问题 通过 `objdump -h` 可查看更详细的段信息,理解这些段有助于进行内存优化和调试内存相关错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值