转载至:https://blog.csdn.net/espressif/article/details/78563811
我们在使用ESP8266的过程中,可能会有疑问,如: 函数放在哪了? 变量放在哪了?常量放在哪了?等等
我们以esp8266 rtos sdk 为例,进行分析,esp8266 nonos sdk 有些许差异,希望在阅读完本文之后,可以旁通。阅读前本文前,先阅读 esp8266 内存分配
我们先来看看编译生成的 *.dump 文件吧,比如 bin/upgrade/user1.1024.new.2.dump。
以下以 rtos 的 openssl_demo 为例,打开 dump 文件,我们可以看到如下信息:
- Sections:
- Idx Name Size VMA LMA File off Algn
- 0 .text 000076a1 40100000 40100000 00001ad4 2**2
- CONTENTS, ALLOC, LOAD, READONLY, CODE
- 1 .irom0.text 00053778 40201010 40201010 00009180 2**4
- CONTENTS, ALLOC, LOAD, READONLY, CODE
- 2 .data 000008ac 3ffe8000 3ffe8000 000000e0 2**4
- CONTENTS, ALLOC, LOAD, DATA
- 3 .rodata 00001144 3ffe88b0 3ffe88b0 00000990 2**4
- CONTENTS, ALLOC, LOAD, READONLY, DATA
- 4 .bss 00006fc8 3ffe99f8 3ffe99f8 00001ad8 2**4
- ALLOC
如果之前未接触过 gcc,那可能会有这样的疑问:为什么这些段,比如 .text 会被放在特定的地址?这个其实是链接器干的事情,在链接的环节中,链接器会读取 ld
目录下的特定 *.ld 文件,从而将特定 attribute 属性的函数、变量等放在了特定的位置。
打开 ld/eagle.app.v6.common.ld,可以看到类似如下信息:
- .text : ALIGN(4)
- { ... }
- >iram1_0_seg :iram1_0_phdr
简单说,就是将 .text 段放到 iram1_0_seg 里,结合 ld/eagle.app.v6.ld,可以看到如下信息:
- MEMORY
- {
- ...
- iram1_0_seg : org = 0x40100000, len = 0x8000
- ...
- }
这样我们就该知道为什么 .text 段会位于 0x40100000 开始的位置了。ld 的事说来话长,想详细了解的,可以看看博友们关于 ld 的文章:)
.text & .irom0.text
均是用来存放程序执行代码的区域,.text 段位于 iRAM 空间,.irom0.text 段会被映射到 cache 空间。
在编译选项 -ffunction-sections 打开的情况下,函数的默认属性为 *.text.*,我们可以通过如下方法查看:
- cd user/.output/eagle/debug/lib
- xtensa-lx106-elf-size -A libuser.a
我们会看到如下信息:
.text.user_init 174 0
user_init 函数的属性为 .text.user_init,在 rtos sdk 的 ld/eagle.app.v6.common.ld 中,我们可以看到:
- .irom0.text : ALIGN(4)
- {
- ...
- *(.literal.* .text.*)
- ...
} >irom0_0_seg :irom0_0_phdr
*.text.* 属性的函数,将会被放到 .irom0.text 段中,通过 dump 文件,我们可以看到
- 40232158 g F .irom0.text 00000062 user_init
链接到 .irom0.text 段的函数会位于 spi flash,将通过 cache 预取。
我们也可以通过 IRAM_ATTR 强制将函数的属性改为 .text。IRAM_ATTR 的定义如下:
- #define IRAM_ATTR __attribute__((section(".text")))
在 openssl_demo 中,给 user_init 函数加上 IRAM_ATTR 后,重新编译,通过 xtensa-lx106-elf-size 命令,已经找不到 .text.user_init 了,但是可以发现 .text 变大了:
- .text 166 0
我们再来看看 ld/eagle.app.v6.common.ld 文件,
- .text : ALIGN(4)
- {
- ...
- *(.literal .text .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
- ...
- } >iram1_0_seg :iram1_0_phdr
通过 dump 文件,我们可以看到:
- 40107130 g F .text 0000006e user_init
user_init 函数已经位于 .text 段内,将在系统启动时,被 bootloader 加载到 iRAM 空间。
我们还可以通过修改 ld 文件,强制将某些库中的某些属性的函数放在某个段中,比如:
- .text : ALIGN(4)
- {
- ...
- *libfreertos.a:(.literal .text .literal.* .text.*)
- ...
- } >iram1_0_seg :iram1_0_phdr
FreeRTOS 中的所有函数,将被强制放在 .text 段中,即均在 iRAM 中运行。
.data & .rodata & .bss
.data 段通常是用来存放程序中已初始化的全局变量的一块内存区域,属于静态内存分配。
.bss 段通常是指用来存放程序中未初始化的全局变量的一块内存区域。属于静态内存分配。
.rodata 段通常是指用来存放程序中常量的一块内存区域。属于静态内存分配。
查看 ld/eagle.app.v6.common.ld,可以看到 .data、.bss、.rodata 段均 >dram0_0_seg,因此都在 dRAM 内。
在 .dump 文件内,我们可以看到不是 static 的 .data、.bss、.rodata 的地址,比如:
- 3ffe8010 g O .data 00000018 flashchip
- 3ffee6c8 l O .bss 00000400 recv_buf
- 3ffe8a38 g O .rodata 00000004 ip_addr_any
.rodata 数据在整个程序运行过程中均不会改变,比如 const 的常量、字符串等,因此放在 dRAM 中是很浪费的,所以我们有其他方法来做这部分优化,以释放更多的 dRAM 空间。
heap
用于存放程序运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。为什么说他是大小是动态的,因为这完全取决于 .data、.bss、.rodata 的大小。
在 ld/eagle.app.v6.common.ld 中,决定了 .data、.rodata、.bss 在 dRAM 中的依次分布,可以通过 *.dump 查看。
heap 区的起始地址,在 .bss 段中有定义:
- .bss ALIGN(8) (NOLOAD) : ALIGN(4)
- {
- ... _heap_start = ABSOLUTE(.);
- } >dram0_0_seg :dram0_0_bss_phdr
通过 *.dump 文件,可以看到:
- 3ffefa20 g *ABS* 00000000 _heap_start