首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ARM架构(thumb/arm)中R7和R11与链接寄存器的关系调用约定

ARM架构(thumb/arm)中R7和R11与链接寄存器的关系调用约定
EN

Stack Overflow用户
提问于 2016-08-04 21:06:59
回答 1查看 4.2K关注 0票数 2

我正在看一个由gcc生成的arm汇编代码,我注意到GCC用以下代码编译了一个函数:

代码语言:javascript
复制
   0x00010504 <+0>: push    {r7, lr}
   0x00010506 <+2>: sub sp, #24
   0x00010508 <+4>: add r7, sp, #0
   0x0001050a <+6>: str r0, [r7, #4]
=> 0x0001050c <+8>: mov r3, lr
   0x0001050e <+10>:    mov r1, r3
   0x00010510 <+12>:    movw    r0, #1664   ; 0x680
   0x00010514 <+16>:    movt    r0, #1
   0x00010518 <+20>:    blx 0x10378 <printf@plt>
   0x0001051c <+24>:    add.w   r3, r7, #12
   0x00010520 <+28>:    mov r0, r3
   0x00010522 <+30>:    blx 0x10384 <gets@plt>
   0x00010526 <+34>:    mov r3, lr
   0x00010528 <+36>:    mov r1, r3
   0x0001052a <+38>:    movw    r0, #1728   ; 0x6c0
   0x0001052e <+42>:    movt    r0, #1
   0x00010532 <+46>:    blx 0x10378 <printf@plt>
   0x00010536 <+50>:    adds    r7, #24
   0x00010538 <+52>:    mov sp, r7
   0x0001053a <+54>:    pop {r7, pc}

令我感兴趣的是,我看到GCC使用R7将值弹出到PC,而不是LR。我在R11上看到了类似的事情。编译器将r11和LR推送到堆栈,然后将R11弹出到PC。LR不应充当返回地址,而不是R7或R11。为什么这里要使用R7 ( Thumb模式下的帧指针)?如果你看看苹果ios的调用惯例,它甚至是不同的。它使用PC的其他寄存器(例如r4到r7)来返回控制。它不应该使用LR吗?

还是我错过了什么?

另一个问题是,看起来LR、R11或R7值从来都不是返回地址的直接值。而是指向包含返回地址的堆栈的指针。是那么回事吗?

另一件奇怪的事情是,编译器不会对函数epoilogue做同样的事情。例如,它可能会使用bx LR而不是pop到PC,但为什么?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-08-04 22:33:33

首先,他们可能希望保持堆栈在64位边界上对齐。

对于帧指针,R7优于任何更大的值,因为大多数指令不支持寄存器r8至r15。我不得不看一下,我会假设有特殊的pc和sp偏移量加载/存储指令,那么为什么要烧掉r7呢?

我不确定你所有的要求,在拇指上你可以推动lr,但pop pc,我认为这相当于bx lr,但你必须查找每个架构,因为对于一些你不能切换模式与pop。在这种情况下,它似乎假设,而不是用pop r3 bx r3之类的东西来刻录额外的指令。实际上,要做到这一点,可能需要两条额外的指令pop r7,pop r3,bx r3。

因此,这可能是一种情况,一个编译器被告知正在使用什么体系结构,并且可以假设pop pc是安全的,而另一个则不是那么确定。同样,必须阅读各种体系结构的arm体系结构文档,以了解哪些指令可以用来更改模式,哪些不能。也许,如果您使用gnu遍历各种架构类型,它可能会改变返回的方式。

编辑

代码语言:javascript
复制
unsigned int morefun ( unsigned int, unsigned int );
unsigned int fun ( unsigned int x, unsigned int y )
{
    x+=1;
    return(morefun(x,y+2)+3);
}
arm-none-eabi-gcc -O2 -mthumb -c so.c -o so.o
arm-none-eabi-objdump -D so.o 
00000000 <fun>:
   0:   b510        push    {r4, lr}
   2:   3102        adds    r1, #2
   4:   3001        adds    r0, #1
   6:   f7ff fffe   bl  0 <morefun>
   a:   3003        adds    r0, #3
   c:   bc10        pop {r4}
   e:   bc02        pop {r1}
  10:   4708        bx  r1
  12:   46c0        nop         ; (mov r8, r8)

arm-none-eabi-gcc -O2 -mthumb -mcpu=cortex-m3 -march=armv7-m -c so.c -o so.o
arm-none-eabi-objdump -D so.o 
00000000 <fun>:
   0:   b508        push    {r3, lr}
   2:   3102        adds    r1, #2
   4:   3001        adds    r0, #1
   6:   f7ff fffe   bl  0 <morefun>
   a:   3003        adds    r0, #3
   c:   bd08        pop {r3, pc}
   e:   bf00        nop

只需使用不带mcpu的进行图就可以得到相同的结果(不会将lr弹出到r1到bx)。

march=armv5t略微修改了一下

代码语言:javascript
复制
00000000 <fun>:
   0:   b510        push    {r4, lr}
   2:   3102        adds    r1, #2
   4:   3001        adds    r0, #1
   6:   f7ff fffe   bl  0 <morefun>
   a:   3003        adds    r0, #3
   c:   bd10        pop {r4, pc}
   e:   46c0        nop         ; (mov r8, r8)

armv4t如预期的那样做了pop和bx的事情。

armv6-m提供了armv5t所提供的功能。

gcc版本6.1.0使用--target= arm -none-eabi构建,没有任何其他arm说明符。

因此,当操作员询问我是否理解正确时,他们可能看到的是三个指令pop bx而不是单个pop {rx,pc}。或者至少一个编译器与另一个编译器相比是不同的。苹果IOS被提及,因此它很可能默认使用更重的内核,而不是到处都能工作的东西。而他们的gcc和我一样,默认处处工作(包括原版ARMv4T),而不是处处工作,除了原版。我假设,如果您添加一些命令行选项,您将看到gcc编译器的行为与我所演示的不同。

注意:在这些示例中没有使用r3和r4,为什么要保留它们呢?这可能是我提到的在堆栈上保持64位对齐的第一件事。如果对于all thumb variants解决方案,如果您在pops之间获得中断,则中断处理程序正在处理未对齐的堆栈。由于r4无论如何都会被丢弃,所以它们可以分别弹出r1和r2或r2和r3,然后分别弹出bx r2或bx r3,而不会出现未对齐并保存指令时刻。哦好吧..。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38768226

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档