谈谈程序中BSS段大小问题


section

前几天,有一位网友编译eCos时,出现了BSS段错误,提示的错误信息大概是:ld: address 0x2000f028 of stress_threads section .bss is not within region sram。

为什么会出现这个编译链接错误呢?首先,我们要搞清楚BSS段是干什么用的,然后才能针对问题进行具体分析。所以,本文主要谈谈BSS段问题,以及关于它的大小问题。让我们对BSS段有一个全面深刻的认识。

问题现象

网友的问题大概如下:

arm-eabi-gcc -L/home/dell/stm32_mini/stm32_mini_install/lib -Ttarget.ld -o /home/dell/stm32_mini/stm32_mini_install/tests/kernel/current/tests/thread_gdb tests/thread_gdb.o -mcpu=cortex-m3 -mthumb -Wl,–gc-sections -Wl,-static -Wl,-n -g -nostdlib
/opt/ecos/gnutools/arm-eabi/bin/../lib/gcc/arm-eabi/4.3.2/../../../../arm-eabi/bin/ld: address 0x2000f028 of /home/dell/stm32_mini/stm32_mini_install/tests/kernel/current/tests/stress_threads section .bss is not within region sram
/home/dell/openetech-ecos/packages/pkgconf/rules.mak:173: recipe for target ‘/home/dell/stm32_mini/stm32_mini_install/tests/kernel/current/tests/stress_threads’ failed
/opt/ecos/gnutools/arm-eabi/bin/../lib/gcc/arm-eabi/4.3.2/../../../../arm-eabi/bin/ld: address 0x2000f028 of /home/dell/stm32_mini/stm32_mini_install/tests/kernel/current/tests/stress_threads section .bss is not within region sram
collect2: ld returned 1 exit status
make[1]: Leaving directory ‘/home/dell/stm32_mini/stm32_mini_build/kernel/current’
make[1]: *** [/home/dell/stm32_mini/stm32_mini_install/tests/kernel/current/tests/stress_threads] Error 1
makefile:102: recipe for target ‘tests’ failed
make[1]: *** Waiting for unfinished jobs….
make: Leaving directory ‘/home/dell/stm32_mini/stm32_mini_build’
make: *** [tests] Error 2

详见http://52ecos.net/thread-487-7-1.html,第68楼的帖子描述。

关于BSS段

从问题描述看,是程序BSS段的大小超过了sram的大小。因此,在分析问题之前,先来了解下BSS段。

我们知道,编译出来的映像文件,一个多个section(段)组成,如text段、data段、bss段,还有heap(堆)和stack(栈)等。它们的描述如下:

BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

问题分析

知道了BSS段,就可分析上面这个问题了。打开eCos中的stress_threads.c文件,可看到,这个文件中定义了很多未初始化的全局变量。其占用的大小很可能超过了程序中定义的bss段大小。

这就带出了两个疑问:

  1. 怎么知道程序中为BSS段规划了多少字节空间?
  2. 怎么知道编译出来的映像占用了多少字节空间?

先来看第一个问题。实际上,程序(准确地说是链接脚本)并未对每个section规划多少字节空间,实际是多少则占用多少,每个section依照设定的顺序依次排列。以eCos给出的section为例:

SECTIONS
{
SECTIONS_BEGIN
SECTION_rom_vectors (flash, 0x08000000, LMA_EQ_VMA)
SECTION_RELOCS (flash, ALIGN (0x8), LMA_EQ_VMA)
    SECTION_text (flash, ALIGN (0x8), LMA_EQ_VMA)
    SECTION_fini (flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_rodata (flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_rodata1 (flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_fixup (flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_gcc_except_table (flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_eh_frame (flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_got (flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_sram (sram, 0x20000400, FOLLOWING (.got))
    SECTION_data (ram, 0x68000000, FOLLOWING (.sram))
    SECTION_bss (ram, ALIGN (0x8), LMA_EQ_VMA)
    CYG_LABEL_DEFN(__heap1) = ALIGN (0x8);
SECTIONS_END
}

上面是eCos中*.ldi文件给出的section定义。从中可看出,BSS段位于DATA段之后,BSS段后面紧接着heap段。

第二个问题。可以借助编译器带的工具查看BSS段及其它各段的实际大小。

使用objdump命令查看BSS段大小

映像文件中,BSS段实际占用大小:

image

使用size命令查看BSS段大小

image

对于上图,有一点需要注意:整个elf文件的大小 = text段大小 + data段大小,并不包含bss段大小,这是为什么呢?

关于BSS段大小的终极解释

不管是用objdump或者是size命令查看到的bss段大小,它只是个“大小”而已,它在映像文件中不会有它的空间,只有当映像文件装载运行时,才会被分配内存(并且位于data段内存块之后),并且初始化为0。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值