gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 10:53:08 26 4
gpt4 key购买 nike

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

基本上,各节中将如何以及何时放置地址,以及如何在OS中以及嵌入式系统中的ROM或RAM中加载地址。

最佳答案

特定的操作系统具有特定的规则集,或者可能具有多个规则集,可在其中加载兼容程序。为该平台制作的包括默认链接描述文件(如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或其他位置)。

关于c - 内存地址如何放置在二进制文件中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52921061/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com