Arm Assembly在子程序中PUSH POP链接寄存器和pc并在子程序中调用子程序的正确方法 [英] Arm Assembly proper way to PUSH POP link register and pc in a subroutine and calling a subroutine within a subroutine

查看:27
本文介绍了Arm Assembly在子程序中PUSH POP链接寄存器和pc并在子程序中调用子程序的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于 ARM 程序集

我在我的子程序中一直在做以下事情:

I have been doing the following in my subroutines:

SubRoutine:
  PUSH {r1,r2,lr}
  //code that changes r1 and r2
  POP {r1,r2,lr}
  bx lr

这是从子例程返回并继续执行主函数中的代码的正确方法吗?我在周围看到人们正在做以下事情:

Is this the correct way to return from a subroutine and continue with code in your main function? I have seen around that people are doing the following:

SubRoutine:
  PUSH {r1,r2,lr}
  //code that changes r1 and r2
  POP {r1,r2,pc}
  bx lr

但我不知道为什么你在按下 LR 时会弹出 PC.哪种方法正确,为什么?

But I don't know why you would POP the PC when you PUSHed the LR. Which is the correct way and why?

另外,如果你在一个子程序中调用一个子程序,你会执行以下操作吗:

Also, If you call a subroutine within a subroutine, do you do the following:

SubRoutine:
   PUSH {r1,r2,lr}

  //code that changes r1 and r2
  PUSH {lr}
  bl AnotherRoutine (where bx lr will be used to return from it)
  POP {lr}

  POP {r1,r2,pc}
  bx lr

或者你会这样做吗:

SubRoutine:
   PUSH {r1,r2,lr}

  //code that changes r1 and r2
  PUSH {lr}
  bl AnotherRoutine(where bx lr will be used to return from it)
  POP {pc}

  POP {r1,r2,pc}
  bx lr

推荐答案

您应该注意三种情况.

  1. 叶子:void foo(void) {};
  2. 尾调用:int foo(void) { return bar();};
  3. 中级:int foo(void) { int i;我 = 酒吧() + 4;返回我;};

有很多方法可以实现这些调用.下面是一些示例,并不是在 ARM 汇编器中实现 Epilogue 和 Prologue 的唯一方法.

There are many ways to implement these calls. Below are some samples and are not the only way to implement epilogue and prologue in ARM assembler.

LEAF 函数

许多函数是 leaf 类型,不需要保存 lr.您只需使用 bx lr 即可返回.例如,

Many functions are the leaf type and do not require saving of the lr. You simply use the bx lr to return. For example,

SubRoutine:
  PUSH {r1,r2}
  //code that changes r1 and r2
  POP {r1,r2}
  bx lr

此外,r1 和 r2 通常用于传递参数,SubRoutine 可以免费使用/销毁它们.ARM 调用约定 如果您从汇编程序调用C"函数,就会出现这种情况.所以通常情况下,没有人会保存 r1 和 r2,但由于它是汇编程序,所以你可以做任何你喜欢的事情(即使这是一个坏主意).所以实际上这个例子只是 bx lr 如果你遵循标准.

Also, it is typical that r1 and r2 are used to pass parameters and a SubRoutine is free to use/destroy them.ARM calling conventions This will be the case if you call 'C' function from assembler. So typically, no one would save r1 and r2 but as it is assembler you can do what ever you like (even if it is a bad idea). So actually the example is only bx lr if you follow the standard.

尾随调用

如果你的函数是一个叶子,除了对另一个函数的最终调用,你可以使用以下快捷方式,

If your function is a leaf except for a final call to another function you can use the following short cut,

Sub_w_tail:
// Save callee-saved regs (for whatever calling convention you need)
// Leave LR as is.
// ... do stuff
B  tail_call

LR被调用者保存到Sub_w_tail,你直接跳转到tail_call,返回原来的调用者.

The LR is saved by the caller to Sub_w_tail and you just jump directly to tail_call which returns to the original caller.

中间函数

这是最复杂的.这是一个可能的顺序,

This is the most complex. Here is a possible sequence,

SubRoutine:
   PUSH {r1,r2,lr}

  //code that changes r1 and r2
  bl AnotherRoutine (where bx lr will be used to return from it)

  // more code
  POP {r1,r2,pc}   // returns to caller of 'SubRoutine'

旧调用约定的一些细节在 ARM 链接和帧寄存器问题.您可以使用此约定.在 ARM 汇编器中执行 epilogueprologue 有许多不同的方法.

Some details of an older calling convention are in the ARM Link and frame registers question. You can use this convention. There are many different ways to perform the epilogue and prologue in ARM assembler.

最后一个相当复杂;或者至少编码乏味.让编译器确定要使用哪些寄存器以及将哪些内容放在堆栈上要好得多.但是,编写汇编程序时,通常只需要知道如何编写第一个(LEAF 函数).仅在汇编程序中编写从高级语言调用的优化子例程是最有效率的.了解它们如何工作以理解已编译的代码很有用.您还应该考虑内联汇编,这样您就不必处理这些细微差别.

The last is quite complex; or at least tedious to code. It is a lot better to let a compiler determine what registers to use and what to place on the stack. However, usually you only need to know how to code the first (LEAF function) when writing assembler. It is most productive only to code an optimized sub-routine called from a higher level language in assembler. It is useful to know how all of them work to understand compiled code. You should also consider inline assembler so you don't have to deal with these nuances.

这篇关于Arm Assembly在子程序中PUSH POP链接寄存器和pc并在子程序中调用子程序的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆