我的书谈到了“动态数据段”和“全局数据段”,在下面的arm代码中,字符串“Hello World!”保存在哪里,它是如何保存的?每个字母是一个字节吗?如果是的话,它如何知道在哪里开始和结束?
.text
.global main
main:
push {lr}
ldr r0, =string
bl printf
mov r0, $0
pop {lr}
bx lr
.data
string: .asciz "Hello World!\n"
3条答案
按热度按时间i34xakig1#
听起来你应该得到一本更好的书!这个程序是不正确的,因为它在堆栈未对齐的情况下调用printf函数。ARM平台上使用的所有主要ABI都要求在调用函数时堆栈是8字节对齐的。
为了回答你的问题,如果你用C语言编写程序,那么它取决于你的编译器把字符串放在哪里,尽管有一些既定的约定。因为你的程序是用汇编语言编写的,你必须告诉它把它放在哪里。这里
.data
指令把字符串放在.data
部分。这可能就是你那本不可靠的书所称的“全局数据段”。如果要我猜的话,我会认为它使用术语“动态数据段”来指代堆,堆实际上并不是输出程序中的一个段,而是通过malloc
之类的函数来访问的。wlsrxk512#
这不是编译器的选择,而是你这个程序员最终选择这些东西去哪里。如果你选择使用一个预先构建的捆绑包,比如你的平台的gnu工具。对于gnu来说,C库和引导程序以及链接器脚本都是密切相关的,并且什么地址空间是由链接器脚本定义的。
您可以看到.asciz,意思是ASCII,您可以很容易地搜索它,并查看这些字符是如何以二进制表示的。
是的,未对齐的堆栈不符合当前的ARM ABI,但此代码仍将进行汇编。与其他代码一样,令人惊讶的是,$0代替#0工作,只是更多地证明了汇编语言是特定于工具而不是目标的。
我删除了printf,使这个例子简单,因为它并不重要。
装拆
我使用了一个反汇编程序,所以它试图把ASCII数据反汇编为指令,你可以看到字节,并将其与网上可以找到的数据进行比较。
这是未链接的,所以段还没有基址,所以它们对于对象来说是零。你可以看到伪语言ldr r 0,=string变成了一个邻近单词的pc相对负载,因为汇编程序在汇编时不知道这个值。我们可以用这样简单的东西来链接它
给予
所以你可以看到,作为程序员,我选择了这些东西的位置,你也可以看到,在代码中,链接器已经填充了字符串的地址。
很明显,这不是一个我们可以运行的可执行文件,你有你需要的引导代码和一些其他的东西。
地址空间是特定于目标的,因此,当我们作为程序员控制内容的位置时,操作系统对内容的位置有规则,如果操作系统设置了.data和.bss,或者我们必须在引导程序中进行设置,等等。当然,如果您选择使用C库,与操作系统的连接非常紧密,因为大多数调用都需要系统调用,而系统调用对于操作系统(和版本)和目标(处理器/架构)。因此,引导程序、C库和链接器脚本是不可分割的,您无法混合和匹配并期望获得很大成功。如果您的工具链安装了C库并与之关联,则如果您为同一计算机/操作系统/处理器选择不同的工具链。然后,我们并不假设每个链接器脚本都将使用确切的内存位置。因为它们可以从操作系统的规则中自由选择应用程序的地址空间。(同样,很明显,汇编语言不希望在同一系统上从一个工具链移植到另一个工具链,所以您可能必须进行修改或尝试使用int 5;返回(0);}以查看链接器执行的操作。
字符串的二进制格式,很明显,你指定了它。链接器将对象链接在一起,每个规则必须符合目标,无论是操作系统还是微控制器地址空间,等等。
它怎么知道从哪里开始和结束呢,我们在上面讨论过start,End,你调用了一个C函数,并传递给它一个C字符串,所以这就涵盖了这个问题,而且你在代码中指定了字符串的终止,所以你基本上已经知道了end是怎么定义的。
vh0rcniy3#
指令告诉程序段的位置,然后实现为这些段分配一个起始地址。例如,.text表示后面是代码/指令,.data表示后面是数据。现在,实现(在硬件上)可能因计算机而异。例如,.text可能从地址0x 00000000开始,而.data则从地址0x 00010000开始。这要视情况而定!
你问题的第二部分,关于它如何知道它在哪里结束。在汇编中,你在一个非常低的层次上工作。是的,每个字符都是一个字节(8位)。机器不知道字符串在哪里结束,对机器来说,内存中的所有内容都是0和1。然而,通常一个字符串以空字符结尾。2所以有一个机制可以打印字符直到到达空值,然后停止。printf函数打印字符直到到达空值,这就是它知道字符串结束的原因。
请注意,在您的程式码区段中:
4、数据
string:.asciz“您好,世界!\n”
它使用.asciz指令,该指令会自动在字符串末尾插入空字符。