Arm装配正确的方式PUSH POP链接寄存器和pc子程序并调用子程序中的子程序

时间:2016-11-05 06:45:48

标签: assembly stack arm subroutine

用于ARM程序集

我在子例程中一直在执行以下操作:

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

这是从子程序返回并继续使用主函数中的代码的正确方法吗?我已经看到人们正在做以下事情:

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

但是我不知道为什么当你推动LR时会弹出PC。这是正确的方法和原因?

此外,如果在子例程中调用子例程,是否执行以下操作:

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 个答案:

答案 0 :(得分:3)

您应该注意三种情况。

  1. Leaf:void foo(void) {};
  2. 尾巴电话:int foo(void) { return bar(); };
  3. 中级:int foo(void) { int i; i = bar() + 4; return i; };
  4. 实现这些调用的方法有很多种。下面是一些示例,并不是在ARM汇编程序中实现结语和序言的唯一方法。

    LEAF功能

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

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

    此外,通常使用r1和r2传递参数, SubRoutine 可以自由使用/销毁它们。 ARM calling conventions 这将是如果你打电话给C'汇编程序的功能。所以通常情况下,没有人会保存r1和r2但是因为它是汇编程序,你可以随心所欲地做任何事情(即使这是一个坏主意)。因此,如果您遵循标准,实际上该示例仅为bx lr

    留言

    如果您的函数是 leaf ,除了最后调用另一个函数外,您可以使用以下捷径,

    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即可返回原来的来电者。

    中级功能

    这是最复杂的。这是一个可能的序列,

    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 Link and frame registers问题中。您可以使用此约定。在ARM汇编程序中执行结尾序言的方法有很多种。

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

相关问题