Assembly bx lr在Raspberry Pi上的臂组件中不工作
我正在编写一个程序,使用ARM组件作为气泵的接口。用户选择他们想要的汽油等级,然后声明他们想要花费的金额。由于无论汽油的等级如何,都会对该输入执行相同的检查,因此我希望使用相同的代码块。如图所示,我希望我的代码使用Assembly bx lr在Raspberry Pi上的臂组件中不工作,assembly,raspberry-pi,arm,Assembly,Raspberry Pi,Arm,我正在编写一个程序,使用ARM组件作为气泵的接口。用户选择他们想要的汽油等级,然后声明他们想要花费的金额。由于无论汽油的等级如何,都会对该输入执行相同的检查,因此我希望使用相同的代码块。如图所示,我希望我的代码使用bl和bx-lr,但我做错了什么。以下是我的代码示例: get_grade: cmpne r1, #'R' beq pump_regular cmpne r1, #'P' beq pump_premium pump_regular: ldr r
bl
和bx-lr
,但我做错了什么。以下是我的代码示例:
get_grade:
cmpne r1, #'R'
beq pump_regular
cmpne r1, #'P'
beq pump_premium
pump_regular:
ldr r0, =strSelRegular
bl printf
bl prompt_amount
@ do other things
pump_premium:
ldr r0, =strSelPremium
bl printf
bl prompt_amount
@ do other things
prompt_amount:
ldr r0, =numInputPattern
ldr r1, =intBuyAmt
bl scanf
@ check to make sure the input is valid
@ if not, b prompt_amount
bx lr
这个程序可以编译,但没有按照我预期的方式运行。一旦到达
提示符\u amount
的末尾,它将挂起。我已经在论坛帖子和keil文档中搜索了几个小时,但都没有结果。非常感谢您的帮助。BL指令操作
if ConditionPassed(cond) then
LR = address of the instruction after the branch instruction
PC = PC + (SignExtend(signed_immed_24) << 2)
pc点在1处,lr改为点在1处:
one:
此时,lr指向一个ret,因此简单的bx lr将返回
...
bl two
two_ret:
在两点执行此bl lr点后,bx lr将返回两点
从lr的角度来看,返回到一个ret的知识丢失了
two:
bx lr
这个分支到两个ret
two_ret:
bx lr
在这一点上,这是一个无限循环bx lr分支到两个ret…无法返回到一个ret
如果你
...
bl one
one_ret:
...
one:
push {r4,lr}
bl two:
two_ret:
pop {r4,lr}
bx lr
two:
bx lr
推送和弹出保持lr
one:
push {r4,lr} save lr pointing at one_ret on the stack
...
pop {r4,lr} restore lr to point at one_ret
bx lr branch to one_ret
r4在这里不相关,使用该寄存器是常见的。当前的调用约定需要64位对齐堆栈,因此需要推送偶数个寄存器。例如,有时您会看到r3而不是r4,但r3可能是返回的一部分(…可能?)比r4高得多,您会进入一些特殊/保留寄存器…只需使用r4,没有什么神奇之处,但对于当前和以前的调用约定,r4是一个不错的选择…如果您实际上正在保存其他寄存器,请使用它们,最终总共有偶数个寄存器(似乎不必在一个push/pop中,但在调用另一个函数之前)
在thumb mov pc之前,lr是典型的返回分支。然后,对于thumb,所需的指令是bx lr,因为bx可以处理混合模式(arm/thumb)和mov pc,lr没有/没有。您可以在文档中查找互通。此外,pop{pc}现在可以用于互通,但这取决于armv4t不支持pop{pc}用于互通的架构,它必须是从/到相同的模式。raspberry pi aarch32指令集(armv6或armv7-a,具体取决于您使用的内核)都支持pop{pc},因此您可以将其更改为
one:
push {r4,lr}
...
pop {r4,pc}
并保存一条指令
作为一般规则,无论何时BL到像C函数一样的标签,都应该自动键入{和},添加推{R4,LR}和POP{R4,PC},然后在中间填写代码。然后,您可以在两种情况下进行优化:上面的“函数”中不使用bl,因此不需要保留lr,如果您没有其他理由使用堆栈,则可以删除推送/弹出并添加bx lr
请注意,bl不支持混合模式,但某些工具链可能支持混合模式。在gnu binutils中的汇编器和链接器之间,例如,如果正确标记标签:.type two,%function
.globl two
two:
然后链接器可以看到bl two,知道指令处于什么模式,这两个代码是什么模式,并根据需要添加一个蹦床
bl two
...
two:
bl two_from_thumb
...
two:
...
two_from_thumb:
bx two
...
如果bl 2在拇指中,而bl 2在手臂中,那么链接器将基本上替换bl 2以分支到蹦床
bl two
...
two:
bl two_from_thumb
...
two:
...
two_from_thumb:
bx two
...
bx 2无效。它们使用保留寄存器执行一些简单指令,以在功能上执行相同的操作。如果你的工具链没有为你添加蹦床,那么你就不能盲目地将bl添加到你想要的任何东西上……有时你必须以伪代码的形式添加lr、pc、#某物、adr rx、标签和bx rx。通过在函数调用后指向lr,然后使用bx进行分支来模拟bl
我不太懂你的代码。入口在哪里?为什么没有从
get_grade
返回的指令?您需要注意的另一个问题是,每条bl
指令都会覆盖lr
寄存器。因此,为了能够从函数返回,您需要在进入时将lr
保存在堆栈上,并在结束时将其弹出。您可以使用push{lr}
和pop{pc}
来执行此操作,将pop
指令与函数返回相结合。@fuz:为了保持堆栈对齐,您通常需要执行类似于push{r4,lr}
/pop{r4,pc}的操作
即使您实际上不需要保存/恢复额外的呼叫保留寄存器。(IIRC,ARM Linux上的ABI在调用前保持8字节堆栈对齐)您正在用bl
覆盖lr
。由于您通过bx-lr
从函数返回,它一直分支到bx-lr
,唉,它挂起了。