Assembly bx lr在Raspberry Pi上的臂组件中不工作

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

我正在编写一个程序,使用ARM组件作为气泵的接口。用户选择他们想要的汽油等级,然后声明他们想要花费的金额。由于无论汽油的等级如何,都会对该输入执行相同的检查,因此我希望使用相同的代码块。如图所示,我希望我的代码使用
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
,唉,它挂起了。