内存地址如何放置在二进制文件中?

时间:2018-10-22 00:23:52

标签: c assembly memory embedded microcontroller

无法理解elf文件中的节如何加载到内存中以及如何选择地址?嵌入式系统通常为代码分配特定的地址,但是该地址放在哪里?

基本上,这些部分中将如何以及何时放置地址,以及如何在操作系统中以及嵌入式系统中的ROM或RAM中加载地址。

1 个答案:

答案 0 :(得分:5)

特定的操作系统具有一组特定的规则,或者可能具有多组规则,可用于在其中加载兼容程序。为该平台制作的包括默认链接程序脚本(认为是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会插入到舞会程序员中。原始二进制图像也可以工作,但很快就会变大,将在一秒钟内显示出来。如上所述,没有.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

相反,人们只会使用工具链生成的elf文件,或者使用ihex或srecord。

S00F00006E6F746D61696E2E737265631F
S3150000000001D1A0E301DA8DE3000000EBFEFFFFEA79
S30D000000100000A0E31EFF2FE132
S3094000000005000000B1
S70500000000FA

所有我需要的信息,但没有那么大字节的大文件。

这不是一个硬性规定,但如今移动数据变得更容易了(比将软盘从一台计算机上转移到另一台装有Prom程序员的计算机上)。特别是i,如果您有捆绑的IDE,那么供应商可能会使用工具链的默认格式,但是即使不支持elf和其他类似格式,也不必走原始二进制文件,ihex或srec的路线。但是它仍然取决于采用“二进制”并将其编程到MCU上的ROM(/ FLASH)中的工具。

现在我欺骗了上面的大文件问题,但是当它不是仅RAM的系统时,您必须做更多的工作。如果您觉得需要将.data或将.bss清零,则需要编写或使用更复杂的链接描述文件,以帮助您了解位置和边界。链接器脚本与使用链接器生成的信息执行那些任务的引导程序结合在一起。基本上,.data的副本需要保留在非易失性内存(ROM / FLASH)中,但它不能在运行时保存在其中。.data是可读写的,因此理想/通常情况下,您使用链接程序脚本语言/魔术来声明。数据读/写空间是空白的,并且该地址和该大小的闪存空间为boo,因此引导程序可以从该地址的闪存复制该数量的数据以进行ram。对于.bss,链接器脚本会生成变量,然后将其保存到Flash中,这些变量告诉引导程序从该地址到该地址的零RAM。

因此,操作系统定义了内存空间,如果您希望程序运行,则链接描述文件会与之匹配。系统设计人员或芯片设计人员确定嵌入式对象的地址空间,并且链接程序脚本与此匹配。引导程序与该构建和目标的链接描述文件结合在一起。

编辑-------------

工具链基础知识...

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 / firmware或其他位置)。