更简单:
闪存.s
.global _start
_start:
ldr sp,=0x11000
bl main
b .
闪存.ld
ENTRY(_start)
MEMORY
{
ram : ORIGIN = 0x10000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > ram
.rodata : { *(.rodata*) } > ram
.bss : { *(.bss*) } > ram
.data : { *(.data*) } > ram
}
so.c
int main ( void )
{
return 5;
}
建造
arm-none-eabi-as --warn --fatal-warnings flash.s -o flash.o
arm-none-eabi-gcc -c -Wall -O2 -ffreestanding so.c -o so.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o so.o -o one.elf
arm-none-eabi-objdump -D one.elf > one.list
arm-none-eabi-objcopy -O binary one.elf one.bin
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld so.o flash.o -o two.elf
arm-none-eabi-objdump -D two.elf > two.list
arm-none-eabi-objcopy -O binary two.elf two.bin
检查:
one.elf: file format elf32-littlearm
Disassembly of section .text:
00010000 <_start>:
10000: e3a0da11 mov sp, #69632 ; 0x11000
10004: eb000000 bl 1000c <main>
10008: eafffffe b 10008 <_start+0x8>
0001000c <main>:
1000c: e3a00005 mov r0, #5
10010: e12fff1e bx lr
two.elf: file format elf32-littlearm
Disassembly of section .text:
00010000 <main>:
10000: e3a00005 mov r0, #5
10004: e12fff1e bx lr
00010008 <_start>:
10008: e3a0da11 mov sp, #69632 ; 0x11000
1000c: ebfffffb bl 10000 <main>
10010: eafffffe b 10010 <_start+0x8>
如果您将其作为 .bin 文件运行,则需要将 C 引导代码位于地址 0x10000。如果您没有指定部分或对象名称,或者以某种方式告诉链接器专门将某些内容放在那里,那么该工具将按照您在命令行上提供的内容进行处理,并按顺序处理这些内容。因此,如果引导代码首先在命令行上,则该入口点将起作用,但如果您将其他内容放在首位,那么这根本不会起作用,理想情况下会以某种方式崩溃。
现在 qemu 允许 elf 文件,它可能支持也可能不支持 elf 文件中的入口点,如果您在链接描述文件中指定入口点,这可能会起作用,但当然当您随后获取原始二进制映像版本时(-O binary..... .bin) 版本,它将在硬件上失败。除非代码是由精灵加载器或类似的东西(操作系统,像这样支持所有 cr@p 的 sim 环境)加载的,否则只需正确构建文件。(现在了解 cortex-m sims qemu 确实/确实查看了条目的 lsbit 以正确启动 cortex-m,所以你需要它)。
arm-none-eabi-nm -a one.elf | grep start
00010000 T _start
arm-none-eabi-nm -a two.elf | grep start
00010008 T _start
您应该能够删除上面示例中的 ENTRY 并让 one.bin 正常工作。但是 two.bin 不会。也许使用 ENTRY() two.elf 会起作用,但不是你应该依赖的。
在构建裸机时,您应该始终根据硬件(或 sim)检查代码的入口点,以查看您在尝试执行之前是否正确构建了二进制文件。任何新项目或构建基础设施中的任何更改...检查工具链输出。
请注意,如果您正在控制链接描述文件,那么您不需要 _start,即使您不是 ( something-ld -Ttext=0x1000 -Tdata=0x2000
) 您也不需要它,它可能会发出警告(对于后者),但谁在乎。_start 被定义为股票链接器脚本中的入口点,一旦您自己制作而不使用股票链接器脚本,您可以根据需要选择入口点的名称和其他名称。
我觉得这很浪费,因为仅仅让命令行正确是微不足道的,但你会看到人们这样做:
闪存.s
.section .init
ldr sp,=0x11000
bl main
b .
.section .text
hello:
b hello
闪存.ld
MEMORY
{
ram : ORIGIN = 0x10000, LENGTH = 0x1000
}
SECTIONS
{
.init : { *(.init*) } > ram
.text : { *(.text*) } > ram
.rodata : { *(.rodata*) } > ram
.bss : { *(.bss*) } > ram
.data : { *(.data*) } > ram
}
构建是相同的:
one.elf: file format elf32-littlearm
Disassembly of section .init:
00010000 <.init>:
10000: e3a0da11 mov sp, #69632 ; 0x11000
10004: eb000001 bl 10010 <main>
10008: eafffffe b 10008 <hello-0x4>
Disassembly of section .text:
0001000c <hello>:
1000c: eafffffe b 1000c <hello>
00010010 <main>:
10010: e3a00005 mov r0, #5
10014: e12fff1e bx lr
two.elf: file format elf32-littlearm
Disassembly of section .init:
00010000 <.init>:
10000: e3a0da11 mov sp, #69632 ; 0x11000
10004: eb000000 bl 1000c <main>
10008: eafffffe b 10008 <main-0x4>
Disassembly of section .text:
0001000c <main>:
1000c: e3a00005 mov r0, #5
10010: e12fff1e bx lr
00010014 <hello>:
10014: eafffffe b 10014 <hello>
您可以看到基于命令行 (.text) 的 hello 和 main 交换,但在 .text 之前的链接器脚本中专门调用了 .init。
我觉得这是一个丑陋的黑客,YMMV。一个更丑陋的黑客是这样的:
闪存.s
ldr sp,=0x11000
bl main
b .
闪存.ld
MEMORY
{
ram : ORIGIN = 0x10000, LENGTH = 0x1000
}
SECTIONS
{
.hello : { flash.o (.text*) } > ram
.text : { *(.text*) } > ram
.rodata : { *(.rodata*) } > ram
.bss : { *(.bss*) } > ram
.data : { *(.data*) } > ram
}
给出:
one.elf: file format elf32-littlearm
Disassembly of section .hello:
00010000 <.hello>:
10000: e3a0da11 mov sp, #69632 ; 0x11000
10004: eb000000 bl 1000c <main>
10008: eafffffe b 10008 <main-0x4>
Disassembly of section .text:
0001000c <main>:
1000c: e3a00005 mov r0, #5
10010: e12fff1e bx lr
two.elf: file format elf32-littlearm
Disassembly of section .hello:
00010000 <.hello>:
10000: e3a0da11 mov sp, #69632 ; 0x11000
10004: eb000000 bl 1000c <main>
10008: eafffffe b 10008 <main-0x4>
Disassembly of section .text:
0001000c <main>:
1000c: e3a00005 mov r0, #5
10010: e12fff1e bx lr
正如从一开始就提到的:如果您在链接描述文件中明确调用某些内容,它会更改内容,否则它会使用命令行(现在我已经看到了例外情况)。在一天结束时,总是在创建新项目或更改构建时检查反汇编,以查看它正在生成可以运行的二进制文件。(如果地址固定,则入口点在正确的位置,手动组装零件的互通是正确的,等等)。
注意:
.text : { *(.text*) } > ram
左侧的 .text 名称是您想要的任何名称,大多数人保留该名称,因为它以传统方式表示某些东西,但您可以在左侧命名这些您想要的名称。编译器使用 .text、.bss、.data 或其他格式,因此您必须正确设置右侧。
MEMORY
{
ram : ORIGIN = 0x10000, LENGTH = 0x1000
}
SECTIONS
{
.hello : { flash.o (.text*) } > ram
.world : { *(.text*) } > ram
}
Disassembly of section .hello:
00010000 <.hello>:
10000: e3a0da11 mov sp, #69632 ; 0x11000
10004: eb000000 bl 1000c <main>
10008: eafffffe b 10008 <main-0x4>
Disassembly of section .world:
0001000c <main>:
1000c: e3a00005 mov r0, #5
10010: e12fff1e bx lr
nm 和 readelf 和其他人对此很好。诸如操作系统之类的加载器工具或带有 elf 文件的 qemu 可能希望也可能不希望查看 .bss、.data 等...必须根据具体情况处理。大多数人只是使用传统的名称。
请注意,内存部分上的 ram 名称是您想要的任何名称,您也可以将其称为香蕉,而不是 ram 或 rom 或 flash 或……您看到其他人使用的名称。