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

查看:169
本文介绍了Arm Assembly在子例程中推入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;i = bar()+ 4;返回我};

有许多方法可以实现这些调用.以下是一些示例,它们不是在ARM汇编器中实现结尾和序言的唯一方法.

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 可以自由使用/销毁它们. 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.

尾部呼叫

如果您的函数是 leaf ,除了对另一个函数的最终调用之外,您可以使用以下快捷方式

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汇编器中,有许多不同的方法可以执行 epilogue prologue .

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在子例程中推入POP链接寄存器和pc并在子例程中调用子例程的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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