我有ARM的汇编代码。
.text
.global main
fib:
push { r4}
MOV r1, #1
MOV r2, #0
MOV r3, #1
loop:
CMP r3, r0
BGE exit
mov r4,r1
ADD r1, r1,r2
mov r2, r4
add r3, r3, #1
B loop
exit:
pop {r4}
mov r0, r1
MOV PC, lr
main:
mov r0, #13
push {lr}
BL fib
pop {lr}
mov r1, r0
ldr r0, =output_string
push {lr}
bl printf
pop {lr}
MOV PC, lr
@ The 'data' section contains static data for our program
.data
output_string:
.asciz "%d\n"
但是我想知道,为什么我需要“.global main”来编译它?我在一个答案here上读到,这告诉编译器它将对链接器可见,因为其他目标文件将使用它。但是我这里不是只有一个目标文件吗?
它是否也告诉我们程序应该从那里开始,因此没有它就不能工作?
2条答案
按热度按时间cu6pst1q1#
关于“但是我这里不是只有一个对象文件吗?",您仍然需要给予一个入口点。.text使以下行属于“code”部分(不可修改),而.data部分包含静态定义的变量。
在.text中,应该有一个入口点。在您的例子中,它是“main”程序。在其他情况下,例如,当您使用汇编程序而不是GCC时,_start是入口点,您应该使用
o8x7eapl2#
下面是简短的回答...
你使用了compile而不是assemble这个词,这就把你正在做的事情联系起来了。你还使用了main而没有任何引导程序,这也意味着你希望别人提供入口点和引导程序。
这就是你如何把它汇编成一个对象。gcc程序本身不是一个编译器,它是一个调用其他程序的shell程序一个C预处理器(例如扩展包含和定义)一个C编译器,一个汇编器和链接器。除非你告诉它不要这样做。
所以
在我的情况下,这说明了更多的故事。
我猜这就是你的故事吧。
您基本上是在尝试“编译”一个C程序,但使用了一些汇编和名称main(),这是C语言特有的。
大多数人不会构建自己的gnu工具链,他们只是下载一个,即使他们至少为一个本地(而不是交叉)编译器构建了工具链,也会尝试检测主机并为其构建,包括操作系统特定的项目,也许还有一个C库。
不幸的是,C库(printf,memcpy等)倾向于在引导程序上加载东西,特别是链接器脚本。因此,链接器脚本和引导程序(这种结合通常必须存在,才能有一个正确的C引导程序)以及C库(不太多)之间有这种亲密的结合。
有人早在不管出于什么原因(不要问“为什么”的问题,假设没有答案)时就显然用了这个标签开头:然后变成了_start。至少对于GNU相关的C库引导程序来说,它已经卡住了。你也可以免费或作为行李(旁观者的眼睛)得到的东西之一是默认的链接器脚本,如果你没有指定一个,那么(至少有一个)默认的。
该默认值包含以下行
您可以使用grep查找这些链接器脚本。
然后是引导程序,至少在gnu相关的世界里通常被称为crt0.S,也许还有其他的,因为有太多的人不知道。
That _start最终会导致对main()的调用。这取决于工具链或库或两者的组合,以及需要多少步骤才能到达那里。
我可以把上面的链接,变成一个无聊的程序。
它使用默认链接器脚本,并且在任何对象中都没有找到_start入口点(全局标签),因此它发出警告,指出它将只使用. text的开头。
我们可以用一个同样无用的引导程序继续这段旅程,但它将把各个点联系起来。
所以我们需要做两件事
英国标准
所以
现在,工具很高兴,它正在构建一个基于链接器脚本的程序,我们没有控制,但我们已经控制了所有的代码。
如果我们回到这里,让gcc用默认值调用所有的程序。
有一吨的东西在那里...你可以看到你自己,你的小主要和其他功能是埋在这一切
如果使用readelf
操作系统需要知道。
并且像这样的系统/目标/工具链所期望的那样存在_start。
main在这里(就在它从所有被链接的东西中着陆的地方)
不管是什么原因。
r 0包含到main的地址,我将让您解开其余部分,这只是多年/数十年积累的痛苦。
如果我们选择这样做:
so.ld
因为gcc想吸收它所知道的默认自举,因此彼得的评论。
现在我们已经使用了一个编译器,它与一个目标有很强的关联,可以用作汇编器和链接器。
它在这种情况下是有效的,但要解决这个问题,我需要连接所有的点。
英国标准
它又固定同样:
型
破碎的
所以
已修复。
您已经创建了两个标注(小心,因为你可能会陷入其他麻烦,见下文)(它们不是“函数”)。其中一个主要暗示你想用一些汇编来开发C工具。你也推动lr来允许嵌套函数,但在你的代码中没有任何地方有引导程序(至少设置堆栈指针并调用main(),还要准备目标所需的.bss和.data,再加上printf后面堆积如山的东西)。所以你想要所有的C库开销,并期望在我们假设的操作系统上运行它(从你不完整的问题中)。
所以你想从引导程序中调用main,所以你必须使它全局化,这样被调用者才能看到它。
这里你会遇到的另一个问题是你的汇编语言是32位arm还是thumb取决于命令行。
啊手臂不是拇指:
存储器
但举个例子
armv 5 t或更高版本,但不是armv7
以ARMv 7s开始(以及以它们结束,因为ARMv 8-a是64位,具有可能的ARMv7模式)
所以
这将崩溃
它是在手臂和拇指之间弹跳,没有适当的蹦床(单板)。它没有很好地混合手臂和拇指模式。
所以
它不需要蹦床,因为blx很好,但是我们可以看到,它确实正确地看到了对fun()的调用,就像在gcc生成的程序集中一样
型
两种解决方案都被使用(对于thumb模式,您可以在标签之前使用.thumb_func(它看到的下一个标签),对于arm,没有快捷方式,但正如您所看到的,.globl或.global和.type不像.thumb_func那样是位置特定的),并且该工具知道如何处理fun,但直到我们指定了more_fun才知道。
要查看蹦床下降到armv6或更早版本
型
因此您可能需要添加
您的代码以及.globl或.global(您的首选项)。不是为了使工具链满意,而是为了使代码可能执行。
使用bx lr而不是mov pc,lr。手臂只有armv4和更老的(?)日子早已过去。
或者你可以
显示器
而不是这两个
或
你也许可以摆脱与mov pc,lr今天,但很长一段时间,这是一个问题。将不得不查找互通的核心架构,你正在使用,随着时间的推移,越来越多的指令可以使用。
为什么我需要“.global main”来编译这个程序?
使用“compile”一词意味着使用的是gcc而不是汇编程序(和连接器)(您也有一个main())。Gcc使用默认链接器脚本和默认引导程序调用链接器(crt0.o),这取决于为该系统构建gcc的方式。crt0.o调用main().您正在将对象与调用它得程序链接,但尚未提供名为main得外部标签,因此链接器将找不到main()在任何地方建造并因此抱怨。
您并不是只有一个对象,您有crt0.o和C库。printf()本身就吸收了大量的C库。C库很可能会作为一个库提供给链接器(something.a)对象文件的列表并不长,但它就像拥有了一吨多的对象文件。如果你有C库作为对象,我们需要一个非常非常长的目标文件列表来让链接器满意。
你的几行代码就像上面所示的那样吸收了大量的东西。它不仅仅是你的一个对象文件。