我正在看一个由gcc生成的arm汇编代码,我注意到GCC用以下代码编译了一个函数:
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,但为什么?
发布于 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遍历各种架构类型,它可能会改变返回的方式。
编辑
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略微修改了一下
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,而不会出现未对齐并保存指令时刻。哦好吧..。
https://stackoverflow.com/questions/38768226
复制相似问题