我正在使用 ARM Cortex-M4 处理器(特别是
STM32F4
微控制器)开发一个嵌入式项目,并且遇到了与链接器脚本中定义的内存部分相关的特殊问题。我正在使用 arm-none-eabi-gcc
工具链进行编译。
我有一个二进制文件,我想将其包含在我的固件中。我在汇编文件中创建了一个自定义部分 (
.rawdata
) 以包含此二进制文件,并且我修改了 linker script
以将此部分放入 Flash
内存中。但是,我在运行时面临 .rawdata 部分中数据完整性的问题。
当我将
.rawdata
部分放置在 .data
部分或我的 .text
中的 linker script
部分时,一切都会按预期工作:数据在内存中正确分配,并且实际数据正确存储。我可以在运行时访问和打印数据,没有任何问题。
但是,当我尝试创建一个独立的
.rawdata
部分(在 .data
之外)或将其放置在 .text
部分内时,数据似乎已分配,但内容不是我所期望的。在运行时,当我打印 .rawdata
部分的内容时,我得到不正确的值(与原始 .bin
内容不匹配)。
有趣的是,当我使用 .elf
检查 arm-none-eabi-objdump
文件时,.rawdata
部分似乎包含正确的数据。这种差异仅在运行时发生。
链接器脚本片段:
ld
.rawdata :
{
. = ALIGN(4);
_mstart = .;
KEEP(*(.rawdata))
. = ALIGN(4);
_mend = .;
} >FLASH
为什么将
.rawdata
部分放置在 .data
部分内可以正常工作,但将其创建为独立部分会导致运行时数据不正确?
对于 ARM Cortex-M4 处理器的链接器脚本中的自定义部分,是否缺少特定的注意事项或配置?
当
.rawdata
部分的数据在 .data 部分之外定义时,如何确保其数据在运行时保持完整?
任何有关如何解决此问题的见解或建议将不胜感激。预先感谢您的帮助!
如果我检查使用
arm-none-eabi-objdump -h -j .rawdata blink.elf
arm-none-eabi-objdump -s -j .rawdata blink.elf
blink.elf: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
1 .rawdata 0000003c 080001c8 080001c8 000071dc 2**0
CONTENTS, READONLY
blink.elf: file format elf32-littlearm
Contents of section .rawdata:
80001c8 cdcccc3d cdcc4c3e 9a99993e cdcccc3e ...=..L>...>...>
80001d8 0000003f 9a99193f 3333333f cdcc4c3f ...?...?333?..L?
80001e8 6666663f 0000803f cdcc8c3f 9a99993f fff?...?...?...?
80001f8 6666a63f 3333b33f 0000c03f ff.?33.?...?
这是应该加载的正确数据。
但是,如果在运行时我检查使用 USART 打印该内存区域内的内容,我会得到:
data size: 60 bytes
Start address: 0x80001c8
End address: 0x8000204
00000000
00000000
3f501eb4
bfe008ee
3d600962
416277e1
c6ffe8d3
3c201ffe
40c1e8d1
41428902
c6004342
4161df76
c2000560
4161a8f3
c2000560
其中地址与上面相同,但内存中的内容不同。
相反, 如果我将
.rawdata
放在 .text
或 .data
部分内,我会得到与应有的相同的内存内容。
这就是你看到的吗?
.globl _start
.thumb
_start:
.word 0x20001000
.word reset
.thumb_func
reset:
b .
.align
.word 0x88888888
.word _mstart
.word _mend
.word 0x88888888
.section .data
.word 0x12341234
.section .rawdata
.word 0x11223344
.section .bss
.word 0
.section .rodata
.word 0x22222222
并用
进行测试arm-none-eabi-as so.s -o so.o
arm-none-eabi-ld -T so.ld so.o -o so.elf
arm-none-eabi-objdump -D so.elf
arm-none-eabi-readelf -a so.elf
arm-none-eabi-objcopy -O binary so.elf so.bin
hexdump -C so.bin
arm-none-eabi-objcopy -O srec --srec-forceS3 so.elf so.srec
cat so.srec
并且可以清楚地看到问题
arm-none-eabi-objdump -D so.elf
so.elf: file format elf32-littlearm
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000009 stmdaeq r0, {r0, r3}
08000008 <reset>:
8000008: e7fe b.n 8000008 <reset>
800000a: 46c0 nop ; (mov r8, r8)
800000c: 88888888 stmhi r8, {r3, r7, fp, pc}
8000010: 08000020 stmdaeq r0, {r5}
8000014: 08000024 stmdaeq r0, {r2, r5}
8000018: 88888888 stmhi r8, {r3, r7, fp, pc}
Disassembly of section .bss:
20000000 <.bss>:
20000000: 00000000 andeq r0, r0, r0
Disassembly of section .data:
20000004 <.data>:
20000004: 12341234 eorsne r1, r4, #52, 4 ; 0x40000003
Disassembly of section .rawdata:
08000020 <_mstart>:
8000020: 11223344 ; <UNDEFINED> instruction: 0x11223344
Disassembly of section .rodata:
08000020 <.rodata>:
8000020: 22222222 eorcs r2, r2, #536870914 ; 0x20000002
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 08000000 010000 00001c 00 AX 0 0 4
[ 2] .bss NOBITS 20000000 030000 000004 00 WA 0 0 1
[ 3] .data PROGBITS 20000004 020004 000004 00 WA 0 0 1
[ 4] .rawdata PROGBITS 08000020 020024 000004 00 0 0 1
[ 5] .rodata PROGBITS 08000020 020020 000004 00 A 0 0 1
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x010000 0x08000000 0x08000000 0x0001c 0x0001c R E 0x10000
LOAD 0x020004 0x20000004 0x0800001c 0x00004 0x00004 RW 0x10000
LOAD 0x020020 0x08000020 0x08000020 0x00004 0x00004 R 0x10000
LOAD 0x000000 0x20000000 0x20000000 0x00000 0x00004 RW 0x10000
Section to Segment mapping:
Segment Sections...
00 .text
01 .data
02 .rodata
03 .bss
00000000 00 10 00 20 09 00 00 08 fe e7 c0 46 88 88 88 88 |... .......F....|
00000010 20 00 00 08 24 00 00 08 88 88 88 88 34 12 34 12 | ...$.......4.4.|
00000020 22 22 22 22 |""""|
00000024
S00A0000736F2E7372656338
S315080000000010002009000008FEE7C0468888888896
S3110800001020000008240000088888888862
S3090800001C3412341246
S309080000202222222246
S70508000000F2
_mstart 位于 0x0800020,但 .rodata 也在那里...并且 .rawdata 现在不在该转储中,它不转储可能没问题,但也许不是。
理想情况下,名称是任意的,请记住这一点
.one : { *(.two*) } > rom
.two 是创建对象的工具的名称,一些名称。 .one 是您要使用链接器更改/转换/输出的名称。
可惜这个世界不是我想要的任何名字。有特殊的名字。如果这就是你发现的,我怀疑这就是你着陆的地方。
或许和这个有关
secname 必须满足您的输出格式的限制。在仅支持有限数量的部分的格式中,例如 a.out,名称必须是该格式支持的名称之一(例如,a.out 只允许 .text、.data 或 .bss)。
使用添加或保留部分到 objcopy 不会改变/帮助。
.text :
{
. = ALIGN(4);
_mstart = .;
KEEP(*(.rawdata))
. = ALIGN(4);
_mend = .;
} > rom
00000000 00 10 00 20 09 00 00 08 fe e7 c0 46 88 88 88 88 |... .......F....|
00000010 1c 00 00 08 20 00 00 08 88 88 88 88 44 33 22 11 |.... .......D3".|
00000020 34 12 34 12 22 22 22 22 |4.4.""""|
00000028
S00A0000736F2E7372656338
S315080000000010002009000008FEE7C0468888888896
S315080000101C000008200000088888888844332211BC
S309080000203412341242
S309080000242222222242
S70508000000F2
我们看到数据就在那里,在正确的位置,并且开始和结束都是正确的。
.data :
{
. = ALIGN(4);
_mstart = .;
KEEP(*(.rawdata))
. = ALIGN(4);
_mend = .;
} > rom
00000000 00 10 00 20 09 00 00 08 fe e7 c0 46 88 88 88 88 |... .......F....|
00000010 20 00 00 08 24 00 00 08 88 88 88 88 34 12 34 12 | ...$.......4.4.|
00000020 44 33 22 11 22 22 22 22 |D3".""""|
00000028
也有效。
如果您使用 openocd 读取 ELF 文件,那么现在它就变成了该工具解释 elf 和可加载部分的方式。精灵没有以我期望加载的方式标记此部分。
这样的事情也没有帮助。
fun (L) : ORIGIN = 0x08001000, LENGTH = 0x1000
...
.rawdata :
{
. = ALIGN(4);
_mstart = .;
KEEP(*(.rawdata))
. = ALIGN(4);
_mend = .;
} > fun
使用 .text 或 .data (或 .rodata 也可以)作为输出节名称似乎可以毫不费力地工作。我会采取这条路径,除非您用来读取 elf 文件的工具专门寻找您正在创建的部分。如果这就是您想要一个新的部分名称(在链接的输出上)的原因,那么只需在您的 elf 阅读工具中添加该支持即可。如果您只想有一个用于链接目的的新部分,那么我会将输出部分名称作为基础知识之一。如果你想要两者兼而有之,那么我会制作一个 elf 工具来修改 elf 以使其可加载或任何 openocd 想要的东西,但这只是我,因为我可以在比我能弄清楚是否有某种工具更短的时间内制作那个 elf 工具存在以及如何让它完成这项任务。 YMMV.
我认为您遇到的情况是,虽然我们希望工具是通用的,但某些部分名称是已知的且特殊的并且......只是工作。