ESP8266:我能做些什么来克服"section `.text' will not fit in region `iram1_0_seg'"?
ESP8266: What can I do to overcome "section `.text' will not fit in region `iram1_0_seg'"?
在使用基于 xtensa GCC 的工具链链接 ESP8266 时,针对 .text 区域不适合 "iram1_0_seg" 的一般措施是什么?
估计是ESP8266s RAM不够大,无法容纳某些功能。但是,如何将尽可能多的功能移动到闪存中?
这里是链接器returns:
的例子
/home/user/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-gcc -I/home/user/git/esp-open-sdk/sdk/include -I/home/user/git/esp-open-sdk/sdk/include/json -I/home/user/git/mart3/src/RTMain/ESP8266TargetGroup -Os -D__ESP8266__ -std=c99 -pedantic -Wall -Wpointer-arith -pipe -Wno-unused-parameter -Wno-unused-variable -Os -g -O2 -Wpointer-arith -Wundef -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH -ffunction-sections -fdata-sections -L/home/user/.arduino15/packages/esp8266/hardware/esp8266/2.0.0/tools/sdk/lib -L/home/user/.arduino15/packages/esp8266/hardware/esp8266/2.0.0/tools/sdk/ld -Teagle.flash.512k0.ld -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static -Wl,--gc-sections src/code/CMakeFiles/FX6CodeObj.dir/FX6Generated/src-gen/fxfu___program1.c.obj src/code/CMakeFiles/FX6CodeObj.dir/FX6Generated/src/emptyHello/fxfu___helloart.c.obj src/code/CMakeFiles/FX6CodeObj.dir/FXStd/FXRTMain.c.obj src/code/CMakeFiles/FX6CodeObj.dir/FXStd/NamedList.c.obj -o src/ARTApp/ARTApp.out -Wl,--start-group src/ART/libART.a -lm -lgcc -lhal -lphy -lnet80211 -llwip -lwpa -lmain -lpp -lsmartconfig -lwps -lcrypto -laxtls -Wl,--end-group
/home/user/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/../lib/gcc/xtensa-lx106-elf/4.8.2/../../../../xtensa-lx106-elf/bin/ld: src/ARTApp/ARTApp.out section `.text' will not fit in region `iram1_0_seg'
collect2: error: ld returned 1 exit status
我不了解 Arduino,但如果您要使用此处找到的 Espressif 库进行编程 https://github.com/esp8266/esp8266-wiki/raw/master/sdk/,那么他们有很多用于此类操作的宏。
例如,使用以下行将 ESP 的 "main" 放入闪存中。
void ICACHE_FLASH_ATTR user_init(){
如果您跟踪 ICACHE... 命令,您会发现这个定义
#define ICACHE_FLASH_ATTR __attribute__((section(".irom0.text")))
如果您随后查看 espressif 如何设置内存部分 https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map。irom0.text 被标记为闪存。基本上任何带有 ICACHE... 命令的东西都会被加载到闪存中,任何没有的东西都不会。
再次不确定如何将其转换为 Arduino 代码,但如果您 运行 没有闪存 space,可能是时候离开 Arduino 库了。你没有指定你使用的是哪个 ESP 突破,我的想法可能在捉弄我,但我相信 ESP12-e 使用更新的芯片,它有更多的闪存然后说 ESP01,只是另一种选择。
现在回答这个问题有点晚了,但我认为其他人可能对可能的解决方案感兴趣。这就是我为解决基于 ESP8266 nonos sdk 的固件上的 iram1_0_seg 溢出错误所做的工作。
为了找出在 iram1_0_seg 部分 运行 中实际分配了哪些函数,请使用以下命令:
$ xtensa-lx106-elf-nm -av yourprogram.elf | uniq -u | grep "^4010*"
当然需要把'yourprogram.elf'换成你的固件elf文件名。所有 iram1_0_seg 函数都在 4010xxxx 地址范围内,因此 grep 4010。显然,只有在生成 elf 时才能执行此命令。如果由于iram1_0_seg溢出错误而无法生成elf文件,则需要删除一些代码。或者回滚到仍然适用且没有 iram1_0_seg 溢出错误的代码版本。
上述 nm 命令的输出将以一行结尾,例如:
$ 4010680c A _text_end
iram1_0_seg 在 ESP8266 上限制为 0x8000 字节。在上面的示例中,我分配了“0x680c”字节,因此有足够的空间。 nm 命令将列出分配在 iram1_0_seg 段中的所有函数。请查看哪些功能可能不需要分配在 RAM 中。大多数函数可以 运行 离开 FLASH,但是如果你不使用 ICACHE_FLASH_ATTR 标记它们,那么函数最终会进入 RAM(并用完 0x8000 字节)。
如果您看到应该在 FLASH 中(但不是)的函数实际上来自标准库 libc.a 或 libgcc.a,那么您就有机会腾出空间在 iram1_0_seg 部分。让我们以 memcpy 为例,它已经在 rom 中可用,并且在任何库中根本不需要。无论如何从 libc.a 中提取它的原因是库优先于 linker 脚本中的 PROVIDE 语句。查看您的 eagle.rom.addr.v6.ld 文件(在您的 /esp-open-sdk/sdk/ld 文件夹中)。在那里你可以看到 PROVIDE(memcpy=..) 语句。
这告诉您 memcpy 在 ROM 中可用。那么,如何使用ROM功能代替libc.a版本呢?最简单的方法是简单地从 libc.a 中删除 memcpy,这样 linker 就可以使用 rom 函数。为了安全起见,我建议先复制一份 libc.a ,以防出现问题。需要在库文件夹(libc.a所在的位置)执行以下命令才能删除memcpy:
$ cp libc.a libc2.a
$ ar d libc2.a lib_a-memcpy.o
将 Makefile 更改为 link 并使用 -lc2 而不是 -lc 后,'memcpy' 函数将从 ROM 获取。使用上面的 nm 命令检查是否成功。 memcpy 不应再列在 401 列表中。并可能重复其他 libc.a 函数(例如,'memcmp'、'strlen'、'strcpy'、'strcmp'、...)。
这就是我如何将 iram1_0_seg 的使用量减少到 0x680c 字节。
可以使用 libgcc.a 函数完成类似的过程:__muldf3、__mulsf3、__umulsidi3、...
您可以尝试通过在“工具”>“MMU”部分中选择其他选项来更改内存分配方案。例如,选择“16KB 缓存 + 48KB IRAM (IRAM)”而不是“32KB 缓存 + 32KB IRAM(平衡)”。
在使用基于 xtensa GCC 的工具链链接 ESP8266 时,针对 .text 区域不适合 "iram1_0_seg" 的一般措施是什么?
估计是ESP8266s RAM不够大,无法容纳某些功能。但是,如何将尽可能多的功能移动到闪存中?
这里是链接器returns:
的例子/home/user/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-gcc -I/home/user/git/esp-open-sdk/sdk/include -I/home/user/git/esp-open-sdk/sdk/include/json -I/home/user/git/mart3/src/RTMain/ESP8266TargetGroup -Os -D__ESP8266__ -std=c99 -pedantic -Wall -Wpointer-arith -pipe -Wno-unused-parameter -Wno-unused-variable -Os -g -O2 -Wpointer-arith -Wundef -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH -ffunction-sections -fdata-sections -L/home/user/.arduino15/packages/esp8266/hardware/esp8266/2.0.0/tools/sdk/lib -L/home/user/.arduino15/packages/esp8266/hardware/esp8266/2.0.0/tools/sdk/ld -Teagle.flash.512k0.ld -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static -Wl,--gc-sections src/code/CMakeFiles/FX6CodeObj.dir/FX6Generated/src-gen/fxfu___program1.c.obj src/code/CMakeFiles/FX6CodeObj.dir/FX6Generated/src/emptyHello/fxfu___helloart.c.obj src/code/CMakeFiles/FX6CodeObj.dir/FXStd/FXRTMain.c.obj src/code/CMakeFiles/FX6CodeObj.dir/FXStd/NamedList.c.obj -o src/ARTApp/ARTApp.out -Wl,--start-group src/ART/libART.a -lm -lgcc -lhal -lphy -lnet80211 -llwip -lwpa -lmain -lpp -lsmartconfig -lwps -lcrypto -laxtls -Wl,--end-group
/home/user/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/../lib/gcc/xtensa-lx106-elf/4.8.2/../../../../xtensa-lx106-elf/bin/ld: src/ARTApp/ARTApp.out section `.text' will not fit in region `iram1_0_seg'
collect2: error: ld returned 1 exit status
我不了解 Arduino,但如果您要使用此处找到的 Espressif 库进行编程 https://github.com/esp8266/esp8266-wiki/raw/master/sdk/,那么他们有很多用于此类操作的宏。
例如,使用以下行将 ESP 的 "main" 放入闪存中。
void ICACHE_FLASH_ATTR user_init(){
如果您跟踪 ICACHE... 命令,您会发现这个定义
#define ICACHE_FLASH_ATTR __attribute__((section(".irom0.text")))
如果您随后查看 espressif 如何设置内存部分 https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map。irom0.text 被标记为闪存。基本上任何带有 ICACHE... 命令的东西都会被加载到闪存中,任何没有的东西都不会。
再次不确定如何将其转换为 Arduino 代码,但如果您 运行 没有闪存 space,可能是时候离开 Arduino 库了。你没有指定你使用的是哪个 ESP 突破,我的想法可能在捉弄我,但我相信 ESP12-e 使用更新的芯片,它有更多的闪存然后说 ESP01,只是另一种选择。
现在回答这个问题有点晚了,但我认为其他人可能对可能的解决方案感兴趣。这就是我为解决基于 ESP8266 nonos sdk 的固件上的 iram1_0_seg 溢出错误所做的工作。 为了找出在 iram1_0_seg 部分 运行 中实际分配了哪些函数,请使用以下命令:
$ xtensa-lx106-elf-nm -av yourprogram.elf | uniq -u | grep "^4010*"
当然需要把'yourprogram.elf'换成你的固件elf文件名。所有 iram1_0_seg 函数都在 4010xxxx 地址范围内,因此 grep 4010。显然,只有在生成 elf 时才能执行此命令。如果由于iram1_0_seg溢出错误而无法生成elf文件,则需要删除一些代码。或者回滚到仍然适用且没有 iram1_0_seg 溢出错误的代码版本。
上述 nm 命令的输出将以一行结尾,例如:
$ 4010680c A _text_end
iram1_0_seg 在 ESP8266 上限制为 0x8000 字节。在上面的示例中,我分配了“0x680c”字节,因此有足够的空间。 nm 命令将列出分配在 iram1_0_seg 段中的所有函数。请查看哪些功能可能不需要分配在 RAM 中。大多数函数可以 运行 离开 FLASH,但是如果你不使用 ICACHE_FLASH_ATTR 标记它们,那么函数最终会进入 RAM(并用完 0x8000 字节)。
如果您看到应该在 FLASH 中(但不是)的函数实际上来自标准库 libc.a 或 libgcc.a,那么您就有机会腾出空间在 iram1_0_seg 部分。让我们以 memcpy 为例,它已经在 rom 中可用,并且在任何库中根本不需要。无论如何从 libc.a 中提取它的原因是库优先于 linker 脚本中的 PROVIDE 语句。查看您的 eagle.rom.addr.v6.ld 文件(在您的 /esp-open-sdk/sdk/ld 文件夹中)。在那里你可以看到 PROVIDE(memcpy=..) 语句。
这告诉您 memcpy 在 ROM 中可用。那么,如何使用ROM功能代替libc.a版本呢?最简单的方法是简单地从 libc.a 中删除 memcpy,这样 linker 就可以使用 rom 函数。为了安全起见,我建议先复制一份 libc.a ,以防出现问题。需要在库文件夹(libc.a所在的位置)执行以下命令才能删除memcpy:
$ cp libc.a libc2.a
$ ar d libc2.a lib_a-memcpy.o
将 Makefile 更改为 link 并使用 -lc2 而不是 -lc 后,'memcpy' 函数将从 ROM 获取。使用上面的 nm 命令检查是否成功。 memcpy 不应再列在 401 列表中。并可能重复其他 libc.a 函数(例如,'memcmp'、'strlen'、'strcpy'、'strcmp'、...)。 这就是我如何将 iram1_0_seg 的使用量减少到 0x680c 字节。 可以使用 libgcc.a 函数完成类似的过程:__muldf3、__mulsf3、__umulsidi3、...
您可以尝试通过在“工具”>“MMU”部分中选择其他选项来更改内存分配方案。例如,选择“16KB 缓存 + 48KB IRAM (IRAM)”而不是“32KB 缓存 + 32KB IRAM(平衡)”。