结合实例分析arm指令集中的adds指令与arm内嵌汇编
2017-02-18 16:17
411 查看
qq截图进来的图全部丢失了。。。。。。。。郁闷。。。。。。
以下是ffmpege0.11.1源码中的一个函数,稍微做了点修改
注意:编译器我arm-linux-gcc4.4.1,其他的编译器请读者自行验证
static av_always_inline av_const int32_t av_clipl_int32_arm(int64_t a)
{
int x, y;
#ifndef MELIS
__asm__ ("adds %1, %R2, %Q2, lsr #31 \n\t"
"itet ne \n\t" //反汇编之后发现该条指令不起作用,可以删除
"mvnne %1, #1<<31 \n\t"
"moveq %0, %Q2 \n\t"
"eorne %0, %1, %R2, asr #31 \n\t"
: "=r"(x), "=&r"(y) : "r"(a):"cc");
#else
__asm__
{
adds y, (((int*) &a)[1]), (((unsigned*) &a)[0]), lsr #31
mvnne y, #1<<31
moveq x, (((unsigned*) &a)[0])
eorne x, y, (((int*) &a)[1]), asr #31
}
#endif
return x;
}
分析:
__asm__是内嵌汇编的标志,这个不多说。
%0表示x,%1表示y,%2表示a,%R2表示a的高32位,%Q2表示a的低32位。
%R2表示a的高32位,%Q2表示a的低32位是怎么来的呢?
查看编写测试程序查看反汇编代码,测试程序如下:
#include <stdio.h>
int main()
{
long long int a=0x2200LL; //用long long int a=0x110022002200LL测试会发现输出结果是0x7fffffff,请读者自行测试
printf("0x%x\n", av_clipl_int32_arm(a));
return 0;
}
在linux下用arm-linux-gcc反汇编,反汇编的关键代码如下:
000083ec <av_clipl_int32_arm>:
83ec: e92d0810
push {r4, fp}
83f0: e28db004
add fp, sp, #4
83f4: e24dd010
sub sp, sp, #16
83f8: e50b0014
str r0, [fp, #-20]
83fc: e50b1010
str r1, [fp, #-16]
8400: e24b4014
sub r4, fp, #20
8404: e8940018
ldm r4, {r3, r4}
8408: e0942fa3
adds r2, r4, r3, lsr #31
840c: 13e02102
mvnne r2, #-2147483648; 0x80000000
8410: 01a03003
moveq r3, r3
8414: 10223fc4
eorne r3, r2, r4, asr #31
8418: e50b300c
str r3, [fp, #-12]
841c: e50b2008
str r2, [fp, #-8]
8420: e51b300c
ldr r3, [fp, #-12]
8424: e1a00003
mov r0, r3
8428: e24bd004
sub sp, fp, #4
842c: e8bd0810
pop {r4, fp}
8430: e12fff1e
bx lr
00008434 <main>:
8434: e92d4810
push {r4, fp, lr}
8438: e28db008
add fp, sp, #8
843c: e24dd00c
sub sp, sp, #12
8440: e3a03c22
mov r3, #8704; 0x2200
8444: e3a04000
mov r4, #0
8448: e50b3014
str r3, [fp, #-20]
844c: e50b4010
str r4, [fp, #-16]
8450: e59f402c
ldr r4, [pc, #44]; 8484 <main+0x50>
8454: e24b1014
sub r1, fp, #20
8458: e8910003
ldm r1, {r0, r1}
845c: ebffffe2
bl 83ec <av_clipl_int32_arm>
8460: e1a03000
mov r3, r0
8464: e1a00004
mov r0, r4
8468: e1a01003
mov r1, r3
846c: ebffffae
bl 832c <_init+0x48>
8470: e3a03000
mov r3, #0
8474: e1a00003
mov r0, r3
8478: e24bd008
sub sp, fp, #8
847c: e8bd4810
pop {r4, fp, lr}
8480: e12fff1e
bx lr
8484: 0000850c
.word 0x0000850c
根据代码画出函数的栈帧结构图:设sp的初始地址为0x2400000c
push {r4, fp, lr}-----r4,fp,lr依次入栈:那么栈指针sp指向0x24000000,sp、fp指针值应该是系统分配
add fp, sp, #8------fp
sub sp, sp, #12-----sp下移12byte,留出3个空位
mov r3, #8704; 0x2200 ----r3=0x2200
mov r4, #0 ----r4=0x0
str r3, [fp, #-20] -------将r3的值0x2200存到fp-20字节(fp下移五格)内存处
str r4, [fp, #-16] ----r4=0x0的值存到fp下移4格的内存处
注意:从这里就可以看出来arm-linux-gcc 4.4.1编译器处理器对64位数据结构的处理,用两个连续的32位的寄存器进行存放,低地址的寄存器存低32位数据,高地址的寄存器存高32位数据。
ldr r4, [pc, #44]; 8484 <main+0x50> ----将0x8484内存单元的值赋给r4,r4=0x850c
sub r1, fp, #20 ------------------------r1变成了指针,指向fp-20内存单元
ldm r1, {r0, r1} ------------------------将r1指向的内存单元0x24000000-12开始的连续8个字节内存单元数据依次赋值给r0,r1
此时在看看源码就更清楚了,64字节的a变量,分别用r0,r1来存放。
bl 83ec <av_clipl_int32_arm> ---跳转到子函数
画出子函数的栈帧:
push {r4, fp} ---保存main函数的fp指针=0x24000008
add fp, sp, #4 ------fp上移四个字节
sub sp, sp, #16------sp给函数形参空出栈空间
str r0, [fp, #-20]-----将r0的值0x2200存到内存空间0x24000000-12-24处
str r1, [fp, #-16]-----将r1的值0x0存到内存空间
sub r4, fp, #20 -----r4指向r0所在的内存地址
ldm r4, {r3, r4} -----也就是将r0,r1的值分别赋值给r3,r4那么r3=0x2200,r4=0x0
adds r2, r4, r3, lsr #31 ------此时r3,r4分别等于r0,r1,也即是0x2200逻辑右移31位(为0),0+0=0,由于加了s标志,那么cpu的状态寄存器将改变,计算结果r2为0将设置z标志位,于是eq条件成立。接下来的mvnne和eorne指令将不会运行,只有moveq运行。
840c: 13e02102 mvnne
r2, #-2147483648; 0x80000000
8410: 01a03003 moveq
r3, r3
8414: 10223fc4 eorne
r3, r2, r4, asr #31
8418: e50b300c str
r3, [fp, #-12] ------保存r3
841c: e50b2008 str
r2, [fp, #-8] -------保存r2
8420: e51b300c ldr
r3, [fp, #-12] -------取出r3,r3就是x
8424: e1a00003 mov
r0, r3
8428: e24bd004 sub
sp, fp, #4 ----移动sp,为恢复r4、子函数fp做准备
842c: e8bd0810 pop
{r4, fp} -----恢复r4、子函数fp
8430: e12fff1e bx
lr -----跳转到main函数
后面就不分析了,要注意的地方就是r3就是x。也就是子函数的返回值。
如果a=0x110022002200LL呢?
"adds %1, %R2, %Q2, lsr #31 \n\t" // y= 0x1100 +(0x22002200 >> 31)=0x1100,此处cpu状态寄存器z标志没有发生改变,将运行mvnne和eorne指令
"mvnne %1, #1<<31 \n\t" // y = ~0x80000000 = 0x7fffffff
"moveq %0, %Q2 \n\t" //不执行
"eorne %0, %1, %R2, asr #31 \n\t" //x = y^(0x1100 >>31) = 0x7fffffff^0x0 = 0x7fffffff
: "=r"(x), "=&r"(y) : "r"(a):"cc");
如果a=0x110022002200LL子函数将返回0x7fffffff,也即是说如果a大于32位将返回0x7fffffff,小于32位则返回原值。
以下是ffmpege0.11.1源码中的一个函数,稍微做了点修改
注意:编译器我arm-linux-gcc4.4.1,其他的编译器请读者自行验证
static av_always_inline av_const int32_t av_clipl_int32_arm(int64_t a)
{
int x, y;
#ifndef MELIS
__asm__ ("adds %1, %R2, %Q2, lsr #31 \n\t"
"itet ne \n\t" //反汇编之后发现该条指令不起作用,可以删除
"mvnne %1, #1<<31 \n\t"
"moveq %0, %Q2 \n\t"
"eorne %0, %1, %R2, asr #31 \n\t"
: "=r"(x), "=&r"(y) : "r"(a):"cc");
#else
__asm__
{
adds y, (((int*) &a)[1]), (((unsigned*) &a)[0]), lsr #31
mvnne y, #1<<31
moveq x, (((unsigned*) &a)[0])
eorne x, y, (((int*) &a)[1]), asr #31
}
#endif
return x;
}
分析:
__asm__是内嵌汇编的标志,这个不多说。
%0表示x,%1表示y,%2表示a,%R2表示a的高32位,%Q2表示a的低32位。
%R2表示a的高32位,%Q2表示a的低32位是怎么来的呢?
查看编写测试程序查看反汇编代码,测试程序如下:
#include <stdio.h>
int main()
{
long long int a=0x2200LL; //用long long int a=0x110022002200LL测试会发现输出结果是0x7fffffff,请读者自行测试
printf("0x%x\n", av_clipl_int32_arm(a));
return 0;
}
在linux下用arm-linux-gcc反汇编,反汇编的关键代码如下:
000083ec <av_clipl_int32_arm>:
83ec: e92d0810
push {r4, fp}
83f0: e28db004
add fp, sp, #4
83f4: e24dd010
sub sp, sp, #16
83f8: e50b0014
str r0, [fp, #-20]
83fc: e50b1010
str r1, [fp, #-16]
8400: e24b4014
sub r4, fp, #20
8404: e8940018
ldm r4, {r3, r4}
8408: e0942fa3
adds r2, r4, r3, lsr #31
840c: 13e02102
mvnne r2, #-2147483648; 0x80000000
8410: 01a03003
moveq r3, r3
8414: 10223fc4
eorne r3, r2, r4, asr #31
8418: e50b300c
str r3, [fp, #-12]
841c: e50b2008
str r2, [fp, #-8]
8420: e51b300c
ldr r3, [fp, #-12]
8424: e1a00003
mov r0, r3
8428: e24bd004
sub sp, fp, #4
842c: e8bd0810
pop {r4, fp}
8430: e12fff1e
bx lr
00008434 <main>:
8434: e92d4810
push {r4, fp, lr}
8438: e28db008
add fp, sp, #8
843c: e24dd00c
sub sp, sp, #12
8440: e3a03c22
mov r3, #8704; 0x2200
8444: e3a04000
mov r4, #0
8448: e50b3014
str r3, [fp, #-20]
844c: e50b4010
str r4, [fp, #-16]
8450: e59f402c
ldr r4, [pc, #44]; 8484 <main+0x50>
8454: e24b1014
sub r1, fp, #20
8458: e8910003
ldm r1, {r0, r1}
845c: ebffffe2
bl 83ec <av_clipl_int32_arm>
8460: e1a03000
mov r3, r0
8464: e1a00004
mov r0, r4
8468: e1a01003
mov r1, r3
846c: ebffffae
bl 832c <_init+0x48>
8470: e3a03000
mov r3, #0
8474: e1a00003
mov r0, r3
8478: e24bd008
sub sp, fp, #8
847c: e8bd4810
pop {r4, fp, lr}
8480: e12fff1e
bx lr
8484: 0000850c
.word 0x0000850c
根据代码画出函数的栈帧结构图:设sp的初始地址为0x2400000c
push {r4, fp, lr}-----r4,fp,lr依次入栈:那么栈指针sp指向0x24000000,sp、fp指针值应该是系统分配
add fp, sp, #8------fp
sub sp, sp, #12-----sp下移12byte,留出3个空位
mov r3, #8704; 0x2200 ----r3=0x2200
mov r4, #0 ----r4=0x0
str r3, [fp, #-20] -------将r3的值0x2200存到fp-20字节(fp下移五格)内存处
str r4, [fp, #-16] ----r4=0x0的值存到fp下移4格的内存处
注意:从这里就可以看出来arm-linux-gcc 4.4.1编译器处理器对64位数据结构的处理,用两个连续的32位的寄存器进行存放,低地址的寄存器存低32位数据,高地址的寄存器存高32位数据。
ldr r4, [pc, #44]; 8484 <main+0x50> ----将0x8484内存单元的值赋给r4,r4=0x850c
sub r1, fp, #20 ------------------------r1变成了指针,指向fp-20内存单元
ldm r1, {r0, r1} ------------------------将r1指向的内存单元0x24000000-12开始的连续8个字节内存单元数据依次赋值给r0,r1
此时在看看源码就更清楚了,64字节的a变量,分别用r0,r1来存放。
bl 83ec <av_clipl_int32_arm> ---跳转到子函数
画出子函数的栈帧:
push {r4, fp} ---保存main函数的fp指针=0x24000008
add fp, sp, #4 ------fp上移四个字节
sub sp, sp, #16------sp给函数形参空出栈空间
str r0, [fp, #-20]-----将r0的值0x2200存到内存空间0x24000000-12-24处
str r1, [fp, #-16]-----将r1的值0x0存到内存空间
sub r4, fp, #20 -----r4指向r0所在的内存地址
ldm r4, {r3, r4} -----也就是将r0,r1的值分别赋值给r3,r4那么r3=0x2200,r4=0x0
adds r2, r4, r3, lsr #31 ------此时r3,r4分别等于r0,r1,也即是0x2200逻辑右移31位(为0),0+0=0,由于加了s标志,那么cpu的状态寄存器将改变,计算结果r2为0将设置z标志位,于是eq条件成立。接下来的mvnne和eorne指令将不会运行,只有moveq运行。
840c: 13e02102 mvnne
r2, #-2147483648; 0x80000000
8410: 01a03003 moveq
r3, r3
8414: 10223fc4 eorne
r3, r2, r4, asr #31
8418: e50b300c str
r3, [fp, #-12] ------保存r3
841c: e50b2008 str
r2, [fp, #-8] -------保存r2
8420: e51b300c ldr
r3, [fp, #-12] -------取出r3,r3就是x
8424: e1a00003 mov
r0, r3
8428: e24bd004 sub
sp, fp, #4 ----移动sp,为恢复r4、子函数fp做准备
842c: e8bd0810 pop
{r4, fp} -----恢复r4、子函数fp
8430: e12fff1e bx
lr -----跳转到main函数
后面就不分析了,要注意的地方就是r3就是x。也就是子函数的返回值。
如果a=0x110022002200LL呢?
"adds %1, %R2, %Q2, lsr #31 \n\t" // y= 0x1100 +(0x22002200 >> 31)=0x1100,此处cpu状态寄存器z标志没有发生改变,将运行mvnne和eorne指令
"mvnne %1, #1<<31 \n\t" // y = ~0x80000000 = 0x7fffffff
"moveq %0, %Q2 \n\t" //不执行
"eorne %0, %1, %R2, asr #31 \n\t" //x = y^(0x1100 >>31) = 0x7fffffff^0x0 = 0x7fffffff
: "=r"(x), "=&r"(y) : "r"(a):"cc");
如果a=0x110022002200LL子函数将返回0x7fffffff,也即是说如果a大于32位将返回0x7fffffff,小于32位则返回原值。
相关文章推荐
- 结合PXA270 Bootloader实例详细分析ARM映象文件
- 结合PXA270 Bootloader实例详细分析ARM映象文件
- VC实例分析:VC++通过汇编获取代码运行时间
- 从一个实例的vs2005汇编代码分析变量在段上的分配--数据段,代码段,BSS段
- ARM汇编中^、!、cxsf符号和movs等指令使用
- 8051、ARM和DSP指令周期的测试与分析
- ARM汇编伪指令
- 实例分析: 如何对比JIT优化前后的汇编代码
- 2009-2010网络最热的 嵌入式学习|ARM|Linux|wince|ucos|经典资料与实例分析 完整版
- 汇编语言AAA指令多字节加法代码分析(5)
- ARM 汇编中的跳转指令小记
- ARM 汇编伪指令
- C++中的虚函数调用原理的反汇编实例分析(1)
- 一步步学习汇编(10)之jmp指令原理分析(破解软件的必修课)
- Arm汇编实例解析
- ARM GCC 内嵌(inline)汇编手册
- 实例分析: 如何对比JIT优化前后的汇编代码
- 【翻译】intel指令格式与长度反汇编引擎ADE32分析——来自看雪软件安全网站
- 在C++中内嵌汇编代码分析
- 分析ARM ANDROID市场及技术结合点