汇编语言是最接近机器语言的一门语言,汇编指令是最微观,它与大型软件关系类似于细胞核器官的关系,
c语言程序最终都要翻译成汇编代码,按照一定规则组织成可执行程序,然后才可以在硬件上执行。
只有真正理解了汇编代码,才能清楚的知道如何编写c代码效率才能最高,
才能真正写出高质量的c代码。
掌握了汇编代码,会让大家更快的成为真正的编程大师。
本文通过一个基于arm裸机开发工程的简单实例给大家讲解,if else架构的代码最终翻译成什么样的arm汇编代码。
测试环境参考下面文章:
《linux驱动、ARM学习环境搭建》
《4. 从0开始学ARM-ARM汇编指令其实很简单》
《7. 从0学ARM-汇编伪指令、lds详解》
1、c代码
废话不多说,直接上c代码,这个代码非常简单,就不多说了。
/*
* main.c
*
* Created on: 2025-10-23
* Author: pengdan
*/
int main(void)
{
int sum = 0;
int a = 10;
int b = 22;
int flag = 15;
if(flag > 11)
{
sum = a+b;
}else{
sum = a-b;
}
return 0;
}
2、编译代码
最终编译城的程序gcd.bin就可以烧录到arm板子上运行,
为方便理解,我们可以用arm-linux-gnueabihf-objdump去掉程序中符号信息,生成程序gcd.dis
gcd.dis
1
2 gcd.elf: file format elf32-littlearm
3
4
5 Disassembly of section .text:
6
7 40008000 <_start>:
8 40008000: e3a0d207 mov sp, #1879048192 ; 0x70000000
9 40008004: ea00000f b 40008048 <__main_from_arm>
10
11 40008008 <main>:
12 40008008: b480 push {r7}
13 4000800a: b085 sub sp, #20
14 4000800c: af00 add r7, sp, #0
15 4000800e: 2300 movs r3, #0
16 40008010: 60fb str r3, [r7, #12]
17 40008012: 230a movs r3, #10
18 40008014: 60bb str r3, [r7, #8]
19 40008016: 2316 movs r3, #22
20 40008018: 607b str r3, [r7, #4]
21 4000801a: 230f movs r3, #15
22 4000801c: 603b str r3, [r7, #0]
23 4000801e: 683b ldr r3, [r7, #0]
24 40008020: 2b0b cmp r3, #11
25 40008022: dd04 ble.n 4000802e <main+0x26>
26 40008024: 68ba ldr r2, [r7, #8]
27 40008026: 687b ldr r3, [r7, #4]
28 40008028: 4413 add r3, r2
29 4000802a: 60fb str r3, [r7, #12]
30 4000802c: e003 b.n 40008036 <main+0x2e>
31 4000802e: 68ba ldr r2, [r7, #8]
32 40008030: 687b ldr r3, [r7, #4]
33 40008032: 1ad3 subs r3, r2, r3
34 40008034: 60fb str r3, [r7, #12]
35 40008036: 2300 movs r3, #0
36 40008038: 4618 mov r0, r3
37 4000803a: 3714 adds r7, #20
38 4000803c: 46bd mov sp, r7
39 4000803e: f85d 7b04 ldr.w r7, [sp], #4
40 40008042: 4770 bx lr
41 40008044: 0000 movs r0, r0
42 ...
43
3、 汇编代码分析
1)变量压栈
11 40008008 <main>:
12 40008008: b480 push {r7}
13 4000800a: b085 sub sp, #20
14 4000800c: af00 add r7, sp, #0
这几行代码,为我们在栈顶预留了20字节的空间,r7指向分配的栈顶。
并且为变量:flag、b、a、sum分配了对应的栈空间,对应的栈地址如下:
分配过程参考下图:
2)if代码块
if分支代码,对应的汇编代码块,详见下图:
从上图可以得出,if代码块翻译成汇编代码的思路就是,
23行:先将变量从栈中读取到寄存器r3,
24行:通过汇编指令cmp比较r3和立即数11,该指令会影响寄存器cpsr状态位
25、30行:通过汇编指令b.n 根据cpsr的n状态位,决定执行那一段代码块
26-27、31-32行:进入if分治后,从栈中取出a、b的值,分别到r2/r3
28、29行:flag大于11,r3=r2+r3,然后将和存入到栈中sum对应的位置
32、33行:flag小于11,r3=r2-r3,然后将差存入到栈中sum对应的位置
完整分析如下:
4、其他编译文件
gcd.s
.text
.global _start
_start:
ldr sp,=0x70000000 /*get stack top pointer*/
b main
Makefile
TARGET=gcd
TARGETC=main
all:
arm-linux-gnueabihf-gcc -lto -g -c -o $(TARGETC).o $(TARGETC).c
arm-linux-gnueabihf-gcc -lto -g -c -o $(TARGET).o $(TARGET).s
arm-linux-gnueabihf-gcc -lto -g -S -o $(TARGETC).s $(TARGETC).c
arm-linux-gnueabihf-ld $(TARGETC).o $(TARGET).o -Tmap.lds -o $(TARGET).elf
arm-linux-gnueabihf-objcopy -O binary -S $(TARGET).elf $(TARGET).bin
arm-linux-gnueabihf-objdump -D $(TARGET).elf > $(TARGET).dis
clean:
rm -rf *.o *.elf *.dis *.bin
map.lds文件
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x40008000;
. = ALIGN(4);
.text :
{
gcd.o(.text)
*(.text)
}
. = ALIGN(4);
.rodata :
{ *(.rodata) }
. = ALIGN(4);
.data :
{ *(.data) }
. = ALIGN(4);
.bss :
{ *(.bss) }
}
更多嵌入式知识,请加彭老师好友:yikoupen
779