特定操作系统具有一组特定的规则或可能的多组规则,用于可以加载兼容程序的位置。包含为该平台制作的默认链接描述文件(即gcc hello.c -o hello)的工具链符合这些规则。
因此,例如,我决定为具有MMU的平台创建操作系统。因为它有一个MMU,我可以创建操作系统,使每个程序看到相同的(虚拟)地址空间。所以我可以决定对于我的操作系统上的应用程序,内存空间从0x00000000开始,但入口点必须是0x00001000。我们支持的二进制文件格式是motorola s-record。
所以用简单的链接描述文件来做一个简单的程序
MEMORY { ram : ORIGIN = 0x1000, LENGTH = 0x10000 } SECTIONS { .text : { *(.text*) } > ram }
我的简单程序的反汇编
00001000 <_start>: 1000: e3a0d902 mov sp, #32768 ; 0x8000 1004: eb000001 bl 1010 <main> 1008: e3a00000 mov r0, #0 100c: ef000000 svc 0x00000000 00001010 <main>: 1010: e3a00000 mov r0, #0 1014: e12fff1e bx lr
而“二进制”文件恰好是人类可读的:
S00F00006E6F746D61696E2E737265631F S3150000100002D9A0E3010000EB0000A0E3000000EF1E S30D000010100000A0E31EFF2FE122 S70500001000EA
你可能会或可能不会注意到地址确实在描述事物发生的二进制文件中。
作为一个基于操作系统的程序加载到ram我们不必玩太多带内存的游戏,我们可以假设一个单位所有ram(读/写)所以如果有.data,.bss等,它们都可以打包在那里。
对于真实的操作系统,希望二进制包括可能与程序大小相关的附加信息。因此,您可以围绕各种常见的文件格式进行谷歌搜索,看看这是如何完成的,要么是简单的预先我需要这么多,要么是单独定义的一对多部分。是的,再次“二进制”不仅仅是操作码和数据,我假设您理解这一点。
我使用的工具链默认输出elf格式的文件,但是objcopy可以用来创建许多不同的格式,其中一种是原始内存映像(不包含任何地址/位置信息)许多/大部分其余包含机器代码和数据以及调试器/反汇编程序的标签或此数据块希望存储在内存空间中的地址等。
现在,当你说嵌入式并使用单词ROM和RAM时,我认为你的意思就像微控制器一样,但即使你的意思是启动x86或全尺寸ARM或任何相同的东西适用。在MCU的情况下,芯片设计者可能根据处理器的规则或他们自己的选择确定了存储器空间的规则。就像操作系统将规定规则一样。我们有点作弊,因为我们今天使用的很多工具(基于gnu)并不是真的设计用于裸机,而是因为通用编译器是通用编译器,更重要的是工具链适合这种可移植性,我们可以使用这样的工具。理想情况下使用交叉编译器意味着输出机器代码不一定意味着在生成该输出机器代码的计算机上运行。重要的区别在于我们想要控制链接和库,不要在基于主机操作系统的库中链接,让我们控制或为此工具链提供一个以我们的MCU为目标的默认链接描述文件。因此,假设我有一个基于ARM7TDMI的MCU,芯片设计师说我需要二进制文件,使得ROM从地址0x00000000开始并且具有一定的大小,并且RAM从0x40000000开始并且具有一定的大小。作为ARM7,处理器通过获取地址0x00000000处的指令开始执行,并且芯片设计者已将该0x00000000映射到ROM。
所以现在我的简单程序
unsigned int xyz; int notmain ( void ) { xyz=5; return(0); }
像这样链接
MEMORY { bob : ORIGIN = 0x00000000, LENGTH = 0x1000 ted : ORIGIN = 0x40000000, LENGTH = 0x1000 } SECTIONS { .text : { *(.text*) } > bob .bss : { *(.bss*) } > ted }
给出了这个的反汇编
Disassembly of section .text: 00000000 <_start>: 0: e3a0d101 mov sp, #1073741824 ; 0x40000000 4: e38dda01 orr sp, sp, #4096 ; 0x1000 8: eb000000 bl 10 <notmain> c: eafffffe b c <_start+0xc> 00000010 <notmain>: 10: e3a02005 mov r2, #5 14: e59f3008 ldr r3, [pc, #8] ; 24 <notmain+0x14> 18: e3a00000 mov r0, #0 1c: e5832000 str r2, [r3] 20: e12fff1e bx lr 24: 40000000 andmi r0, r0, r0 Disassembly of section .bss: 40000000 <xyz>: 40000000: 00000000 andeq r0, r0, r0
这将是一个非常有效的程序,没有太多有趣的,但仍然是一个完美有效的程序。
首先,如果你省略_start,工具链会发出警告,但仍然可以正常工作。 (嗯,实际上并没有警告那段时间,有趣)
arm-none-eabi-as --warn --fatal-warnings vectors.s -o vectors.o arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c notmain.c -o notmain.o arm-none-eabi-ld vectors.o notmain.o -T memmap -o notmain.elf arm-none-eabi-objdump -D notmain.elf > notmain.list arm-none-eabi-objcopy --srec-forceS3 notmain.elf -O srec notmain.srec arm-none-eabi-objcopy notmain.elf -O binary notmain.bin
现在你有了加载问题。每个MCU都与您如何加载可用工具和/或制作自己的工具的方式不同。 Ihex和srec在舞会程序员中很受欢迎,你在处理器旁边说了一个单独的rom和/或通孔mcu会插入到prom程序员中。原始二进制图像也可以工作,但可以很快变大,如一秒钟所示。如上所述,有.bss但没有.data所以
ls -al notmain.bin -rwxr-xr-x 1 user user 40 Oct 21 22:05 notmain.bin
40个字节。但是,如果我这样做是为了演示目的,即使它不能正常工作:
unsigned int xyz=5; int notmain ( void ) { return(0); }
同
MEMORY { bob : ORIGIN = 0x00000000, LENGTH = 0x1000 ted : ORIGIN = 0x40000000, LENGTH = 0x1000 } SECTIONS { .text : { *(.text*) } > bob .bss : { *(.bss*) } > ted .data : { *(.data*) } > ted }
给
Disassembly of section .text: 00000000 <notmain-0x10>: 0: e3a0d101 mov sp, #1073741824 ; 0x40000000 4: e38dda01 orr sp, sp, #4096 ; 0x1000 8: eb000000 bl 10 <notmain> c: eafffffe b c <notmain-0x4> 00000010 <notmain>: 10: e3a00000 mov r0, #0 14: e12fff1e bx lr Disassembly of section .data: 40000000 <xyz>: 40000000: 00000005 andeq r0, r0, r5
和
-rwxr-xr-x 1 user user 1073741828 Oct 21 22:08 notmain.bin
哎哟! 0x40000004字节,这是预期的,我要求一个内存映像,我在一个地址(机器代码)定义东西,在另一个地址(0x40000000)定义几个字节,因此原始内存映像必须是整个范围。
hexdump notmain.bin 0000000 d101 e3a0 da01 e38d 0000 eb00 fffe eaff 0000010 0000 e3a0 ff1e e12f 0000 0000 0000 0000 0000020 0000 0000 0000 0000 0000 0000 0000 0000 * 40000000 0005 0000 40000004
相反,人们只会使用工具链生成的精灵文件,或者使用ihex或srecord。
S00F00006E6F746D61696E2E737265631F S3150000000001D1A0E301DA8DE3000000EBFEFFFFEA79 S30D000000100000A0E31EFF2FE132 S3094000000005000000B1 S70500000000FA
我需要的所有信息,但不是一个如此少的字节的巨大文件。
这不是一个硬性规则,但今天移动数据更容易(而不是一台计算机上的软盘与其上的舞台程序员)。特别是如果你有一个捆绑的IDE供应商可能使用工具链默认格式,但即使不支持elf和其他类似的格式,你也不必去原始二进制或ihex或srec的路由。但它仍然依赖于采用“二进制”的工具并将其编程到MCU上的ROM(/ FLASH)中。
现在我作弊来演示上面的大文件问题,相反,当它不是一个只有ram的系统时你必须做更多的工作。如果您觉得需要.data或希望将.bss归零,那么您需要编写或使用更复杂的链接描述文件来帮助您了解位置和边界。并且该链接描述文件与引导程序结合使用,该引导程序使用链接器生成的信息来执行这些任务。基本上.data的副本需要保存在非易失性存储器(ROM / FLASH)中,但它不能在运行时存在.data是读/写的理想/通常你使用链接器脚本language / magic来表示。数据读/写空间是等等,并且闪存空间在此地址和此大小处是boo,因此引导程序可以从该地址的闪存中复制该数据量以进行ram。对于.bss,链接器脚本生成我们保存到闪存中的变量,这些变量告诉引导程序从该地址到该地址的零ram。
因此,操作系统定义了内存空间,如果您希望程序正常工作,则链接描述符与之匹配。系统设计人员或芯片设计人员确定嵌入内容的地址空间,链接器脚本与之匹配。引导程序与该构建和目标的linkerscript结合。
编辑-------------
工具链基础知识......
mov sp,#0x40000000 orr sp,sp,#0x1000 bl notmain b . unsigned int xyz; int notmain ( void ) { xyz=5; return(0); } MEMORY { bob : ORIGIN = 0x1000, LENGTH = 0x1000 ted : ORIGIN = 0x2000, LENGTH = 0x1000 } SECTIONS { .text : { *(.text*) } > bob .bss : { *(.bss*) } > ted }
我的引导程序,主程序和链接描述文件
arm-none-eabi-as --warn --fatal-warnings vectors.s -o vectors.o arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -save-temps -c notmain.c -o notmain.o arm-none-eabi-ld vectors.o notmain.o -T memmap -o notmain.elf arm-none-eabi-objdump -D notmain.elf > notmain.list arm-none-eabi-objcopy --srec-forceS3 notmain.elf -O srec notmain.srec arm-none-eabi-objcopy notmain.elf -O binary notmain.bin
有些人会争论,有时候编译不再生成汇编。仍然是理智的做法,你会发现它往往,因为在这种情况下......
引导程序创建了一个我们可以反汇编的对象。
00000000 <.text>: 0: e3a0d101 mov sp, #1073741824 ; 0x40000000 4: e38dda01 orr sp, sp, #4096 ; 0x1000 8: ebfffffe bl 0 <notmain> c: eafffffe b c <.text+0xc>
它没有“链接”,所以这个反汇编程序使用的地址是零基础的,你可以看到对notmain的调用是不完整的,还没有链接。
编译器为C代码生成了程序集
.cpu arm7tdmi .fpu softvfp .eabi_attribute 20, 1 .eabi_attribute 21, 1 .eabi_attribute 23, 3 .eabi_attribute 24, 1 .eabi_attribute 25, 1 .eabi_attribute 26, 1 .eabi_attribute 30, 2 .eabi_attribute 34, 0 .eabi_attribute 18, 4 .file "notmain.c" .text .align 2 .global notmain .type notmain, %function notmain: @ Function supports interworking. @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0 @ link register save eliminated. mov r2, #5 ldr r3, .L2 mov r0, #0 str r2, [r3] bx lr .L3: .align 2 .L2: .word xyz .size notmain, .-notmain .comm xyz,4,4 .ident "GCC: (15:4.9.3+svn231177-1) 4.9.3 20150529 (prerelease)"
它被组装成一个我们也可以拆卸的物体。
Disassembly of section .text: 00000000 <notmain>: 0: e3a02005 mov r2, #5 4: e59f3008 ldr r3, [pc, #8] ; 14 <notmain+0x14> 8: e3a00000 mov r0, #0 c: e5832000 str r2, [r3] 10: e12fff1e bx lr 14: 00000000 andeq r0, r0, r0
现在没有显示,但该对象还包含全局变量xyz及其大小的信息。
联系人的工作可能是你困惑的一部分。它将对象链接在一起,使得结果将是合理的,或者将在最终目标(裸机或操作系统)上工作。
Disassembly of section .text: 00001000 <notmain-0x10>: 1000: e3a0d101 mov sp, #1073741824 ; 0x40000000 1004: e38dda01 orr sp, sp, #4096 ; 0x1000 1008: eb000000 bl 1010 <notmain> 100c: eafffffe b 100c <notmain-0x4> 00001010 <notmain>: 1010: e3a02005 mov r2, #5 1014: e59f3008 ldr r3, [pc, #8] ; 1024 <notmain+0x14> 1018: e3a00000 mov r0, #0 101c: e5832000 str r2, [r3] 1020: e12fff1e bx lr 1024: 00002000 andeq r2, r0, r0 Disassembly of section .bss: 00002000 <xyz>: 2000: 00000000 andeq r0, r0, r0
我创建了这个链接器脚本,以便您可以看到.data和.bss四处移动。链接器已将所有.text填入0x1000地址空间,并修改了对notmain()的调用以及如何到达xyz。它还为0x2000地址空间中的xyz变量分配/定义了空间。
然后到你的下一个问题或混乱。它非常适合加载系统的工具,无论是将程序加载到运行的存储器中的操作系统,还是编程MCU的闪存或编程其他嵌入式系统的ram(例如鼠标等)您可能不知道其中一些固件是从操作系统下载的,而不是所有固件都刻录到闪存/ lib /固件或其他位置)。