设label1
、label2
是两个指令集,都以RET
指令结束,并且使得label2
通过链接分支到label1
。换句话说,我们有一个看起来像这样的代码(为了清楚起见,我将在下面列举一些位置):
label1:
# Some operations...
# (1)
RET
label2:
#Some operations
BL label1
#(2)
RET
我想在main
中调用label2
:
main:
# Some operations...
BL label2
#(3)
我希望的行为是,在main
中分支到label2
之后,执行从#(3)
继续。然而,事实并非如此。当在main
中调用BL label2
时,链接寄存器保存所需返回点#(3)
的地址,并执行label2
。但是,在label2
内部,我调用BL label1
,将链接寄存器替换为#(2)#
。这使得#(1)
之后的RET
指令执行到#(2)
,而#(2)
之后的RET
指令再次指向#(2)
。你可以看到问题所在。
高级编程语言允许嵌套使用函数。我可以定义一个函数f
,在其中调用一个函数g
,每个函数都有return
语句。所以我想要的功能必须以某种方式实现。如何对BL some_label
和RET
进行顺序或嵌套调用,并返回到调用BL
的第一个位置?
我是个新手,所以如果问题有点小,请原谅我。
1条答案
按热度按时间deikduxw1#
你基本上标记了多个架构。所以我会选择一个建筑。
简短的回答是你保存返回地址到堆栈。
如果没有嵌套调用,则不需要保存返回地址(在此汇编语言中显示为lr或链接寄存器)。额外的寄存器r4并不是因为r4在这里是特殊的,而是因为编译器使用的调用约定规定了一个64位对齐的堆栈,所以他们扔在一些其他的寄存器,这不会影响代码/约定,使其对齐。
第二个调用了一个函数,所以有两种情况,在这个例子中,它可能做了一个尾巴?优化,并做到这一点
但事实并非如此也许只有少数人知道,但我使用的是一个旧的gcc(不是最先进的),并保留了默认的armv 4 t。所以也许是因为这个原因,工具链不愿意处理手臂/拇指模式切换,但是对于bling,他们会在链接器中为你放置一个单板/蹦床。
第三个我强迫它不能做尾部优化,这类似于你通常看到的那种东西,大多数人会写,如果手工完成。
实际上,这是你期望编译器产生的结果:
在函数的开始和结束处使用此堆栈帧
然后填充函数的内部。
相同的gcc,但不同的手臂架构,随着时间的推移,更多的拇指交互支持被添加。
如果手工编写,可以不使用堆栈框架,在嵌套的分支之前保存需要恢复的内容,然后在恢复之后保存需要恢复的内容,这样就可以在遍历代码时保存和恢复,而不是在函数的边缘使用堆栈框架。这没有什么错,一个非常手工的汇编方式来做它,而不是一个高级语言编译的方式来做它。
其他架构,例如ARM,具有不同的架构;调用和返回可以例如使用堆栈而不是返回寄存器。因此,在这种情况下,只要返回地址去,你只是继续拨打电话(8088/86为例)。您最终将需要保留其他内容,以便进行嵌套调用,而不是丢弃返回地址以外的内容。这是编译语言制定/选择调用约定的地方,作为一组规则,允许以通用的方式构造函数,以便您可以无限嵌套或递归。