三、代码反汇编简析汇编 汇编文件转换为目标文件(里面是机器码,机器码是给cpu使用的,烧录保存在flash空间的就是机器码)。反汇编 可执行文件(目标文件,里面是机器码),转换为汇编文件。3.1 不同编译器的反汇编3.1.1 keil下面生成反汇编文件fromelf –text -a -c –output=(改成你想生成的反汇编名字一般是工程名字).dis (需要的axf文件,根据你工程生成axf的路径填写).axf设置好以后编译之后就会生成反汇编.dis文件:
打开如下所示:对于上图中的红色圈出来的语句,我们可以根据本文 第 二 章节的第2小节 arm汇编格式中的介绍来分析一下:
简单分析如下(立即数就不分析了= =!):
3.1.2 gcc下生成反汇编文件在x86架构下的电脑上生成arm架构的汇编代码有两种方式:
使用交叉编译工具链 指定-s选项可以生成汇编中间文件。ex:gcc -s test.c使用 objdump 反汇编 arm二进制文件。上述两种方法的区别为:
(1)反汇编可以生成arm指令操作码,-s生成的汇编没有指令码 (2)反汇编的代码是经过编译器优化过的。(3)反汇编代码量很大。
对于arm cortex-m,使用的是 arm-none-eabi-objdump,常用指令如下:
arm-none-eabi-objdump -d -s(可省) a1.o 查看a1.o反汇编可执行段代码arm-none-eabi-objdump -d -s(可省) a1.o 查看a1.o反汇编所有段代码arm-none-eabi-objdump -d -b binary -m arm ab.bin 查看ab.bin反汇编所有代码段对于使用 arm-none-eabi-gcc 工具链(以stm32cubemx)的内核来说,使用如下方式生成反汇编文件:
$(objdump) -d -b binary -m arm (需要的elf文件,一般是工程名字).elf > (改成你想生成的反汇编名字,一般是工程名字).dis # objdump = arm-none-eabi-objdump
-d表示对全部文件进行反汇编,-b表示二进制,-m表示指令集架构
makefile修改如下:
...target = d6tpir######################################## paths######################################## build pathbuild_dir = build...prefix = arm-none-eabi-...objdump = $(prefix)objdumpdis: $(objdump) -d -b binary -m arm $(build_dir)/$(target).elf > $(build_dir)/$(target).dis# $(objdump) -d -b binary -m arm $(build_dir)/$(target).bin > $(build_dir)/$(target).dis执行 make dis 即可生成 .dis 文件:打开文件查看,发现怎么这个汇编语言有点不一样:经过研究了一段时间,加上了-m force-thumb后稍微有点样子了:! 在网上有各种参考,但是我都测试过了,并没有找到合适的生成完全和标准汇编一致的那种,-m后面的参数也不能乱加,需要根据自己的交叉编译器,因为这里用的是 arm-none-eabi-gcc,所以可以通过arm-none-eabi-objdump --help 查看能用的命令和参数: gcc工具链下的汇编还是不太熟悉,所以我们下面反汇编文件与 c语言的对比,使用keil下的反汇编进行说明。
3.2 c 和 汇编 比较分析前面介绍了那么多,最终用一个简单的程序对比一下c语言反汇编后的汇编语言,加深一下印象,当作个实战总结。
基于stm32l051(cortex-m0)内核,目的是为了比较c和汇编,用了个最简单的程序来分析,没有用到任务外设,程序如下:
//前面省略...void delay(u32 count){ while(count--);}u32 add(u16 val1,u16 val2){ u32 add_val; add_val = val1 + val2; return add_val;} int main(void) { u16 a,b; u32 c; a = 12345; b = 45678; c = add(a,b); while(1) { c--; delay(200000); } }反汇编的代码对应部分如下(因为基于硬件平台,其他异常中断,堆,栈,包括其他一些也有汇编代码,这里省略):
;省略前面 delay 0x080001ae: bf00 .. nop 0x080001b0: 1e01 .. subs r1,r0,#0 0x080001b2: f1a00001 .... sub r0,r0,#1 0x080001b6: d1fb .. bne 0x80001b0 ; delay + 2 0x080001b8: 4770 pg bx lr add 0x080001ba: 4602 .f mov r2,r0 0x080001bc: 1850 p. adds r0,r2,r1 0x080001be: 4770 pg bx lr main 0x080001c0: f2430439 c.9. mov r4,#0x3039 0x080001c4: f24b256e k.n% mov r5,#0xb26e 0x080001c8: 4629 )f mov r1,r5 0x080001ca: 4620 f mov r0,r4 0x080001cc: f7fffff5 .... bl add ; 0x80001ba 0x080001d0: 4606 .f mov r6,r0 0x080001d2: e003 .. b 0x80001dc ; main + 28 0x080001d4: 1e76 v. subs r6,r6,#1 0x080001d6: 4804 .h ldr r0,[pc,#16] ; [0x80001e8] = 0x30d40 0x080001d8: f7ffffe9 .... bl delay ; 0x80001ae 0x080001dc: e7fa .. b 0x80001d4 ; main + 20 $d 0x080001de: 0000 .. dcw 0 0x080001e0: e000ed0c .... dcd 3758157068 0x080001e4: 05fa0000 .... dcd 100270080 0x080001e8: 00030d40 @... dcd 200000;省略后面3.2.1 mov后面 立即数的疑问在对比分析这段代码前,在 main 函数中的第一句:
0x080001c0: f2430439 c.9. mov r4,#0x3039就有一个大大的疑问, mov r4,#0x3039中 0x3039 并不是立即数(按照我们第二章 立即数的说明) ,包括接下来的 0xb26e 也不是立即数,怎么可以直接用 mov,按理来说需要用 ldr伪指令的??
至于这个问题,网上简单查找了一下,找到一篇有关说明的文章:arm 汇编的mov操作立即数的疑问 其中有说到,在 keil 公司方网站里关于arm汇编的说明里有这么一段:
syntax mov{cond} rd, #imm16 where: imm16 is any value in the range 0-65535.
所以是不是在 keil 中的arm汇编 立即数可以使16位的?
为了验证一下,我稍微修改了一下程序,就是把a的值赋值超过16位(当然定义函数之类的也要跟着改,测试代码中a为u16的无符号整形),测试了一下。
a赋值为 65535,结果如下(65535不是立即数,也可以直接mov):
0x080001c0: f64f75ff o..u mov r5,#0xffffa赋值为 65536,结果如下(65536是立即数,可以直接mov):
0x080001c0: f44f3580 o..5 mov r5,#0x10000a赋值为一个大于16位的,不是立即数的数,比如:0x1ffff :
0x080001c0: 4d08 .m ldr r5,[pc,#32] ; [0x80001e4] = 0x1ffff果然,最后当 a 大于16位,不是立即数时候,会使用伪指令 ldr,所以我们可以得出结论:
在 keil 中的arm汇编中,16位内(包括16位)的数都直接使用 mov 赋值,大于16位,如果是立即数,直接使用mov,不是立即数用ldr (立即数的判断方式还是前面讲的那样)
3.2.2 反汇编文件解析对于上面的示例程序的汇编码,简单解析如下:添加一个有意思的测试对于delay函数中的语句,上图是while(count--);改成while(--count);后汇编代码如下:
对于上面的测试程序,汇编中并没有使用到 push 和 pop 指令,因为程序太简单了,不需要使用到栈,为了能够熟悉下单片机中必须且经常需要用到的 栈,我们稍微修改一下add函数,在add函数中调用了delay函数:
u32 add(u16 val1,u16 val2){ u32 add_val; add_val = val1 + val2; delay(10); return add_val;}对于的add函数汇编代码如下:
add 0x080001ba: b530 0. push {r4,r5,lr} ;把r4 r5 lr的值入栈 0x080001bc: 4603 .f mov r3,r0 0x080001be: 460c .f mov r4,r1 0x080001c0: 191d .. adds r5,r3,r4 0x080001c2: 200a . movs r0,#0xa 0x080001c4: f7fffff3 .... bl delay ; 0x80001ae 0x080001c8: 4628 (f mov r0,r5 0x080001ca: bd30 0. pop {r4,r5,pc} ;把r4 r5 lr的值出栈,(汇编中可以看到指令后面后面加了个s ,movs 、adds,这就是我们前面说到的,带了s 会影响 xpsr 寄存器中的值)
可以看到,因为存在函数的多次调用,main函数中调用add函数,add函数中调用delay函数,所以在add函数运行之前,通过 push 把 r4,r5,lr 寄存器的值先存入栈中,等待程序执行完(函数调用结束)再吧 r4,r5,lr 寄存器的值恢复。
上面的程序虽然简单,但是通过我们c程序 与 汇编程序的对比分析,能够让我们更加深入的理解汇编语言。
仍有希望:微星注册RTX 3080 20GB型号
新迪数字携企业级研发设计协同解决方案亮相广东智能装备产业发展大会
柔性制造和刚性制造的区别
STRATACACHE集成了NVIDIA Holodeck沉浸式虚拟现实环境的VR购物体验
WiFi信号或干扰地铁系统:地铁WiFi还得再等等
深度剖析ARM内核寄存器及基本汇编语言3
大电流弹片微针模组可提高手机摄像头的测试效率
印制线路板问题(问答)
印制导线的宽度及间距是多少
从Intel的衰退中看破坏性创新
Google编程风格指南(四)
[组图]“一装即响”的微型集成电路收音机
起航 智能手机的新时代就要到了
美再发推文对墨祭出关税 台湾代工厂在墨西哥的业务影响不大
2023年高端蓝牙耳机排行榜前十名,哪个牌子的蓝牙耳机性价比高!
无线电用语的名词解释
苏姿丰谈Zen4和Zen5架构细节
中美AI医疗之战或将打响 医疗领域融资中国遥遥领先
艾力奋RFID人脸识别闸机助力小鹏汽车发布会【技术篇】
给演示效果令人震撼的微软HoloLens浇冷水