为了说明C语言如何被编译成汇编语言,我们可以通过一个简单的C程序,并使用ARM编译器来生成相应的汇编代码。
// simple.c
int add(int a, int b) {return a + b;
}int main() {int result = add(5, 3);return result;
}
使用ARM编译器生成汇编代码
我们使用 arm-none-eabi-gcc(GNU ARM Embedded Toolchain)来编译这个C程序并生成汇编代码。以下是在Linux或类似Unix系统上的操作步骤:
-
安装ARM编译工具链: 如果未安装,可以通过以下命令安装:
sudo apt-get install gcc-arm-none-eabi -
编译生成汇编代码: 使用
-S选项来生成汇编代码。arm-none-eabi-gcc -S simple.c -o simple.s -
查看生成的汇编代码: 打开生成的
simple.s文件。
生成的汇编代码(simple.s)
.cpu arm7tdmi.eabi_attribute 20, 1.eabi_attribute 21, 1.eabi_attribute 23, 3.eabi_attribute 24, 1.eabi_attribute 25, 1.eabi_attribute 26, 1.eabi_attribute 30, 6.eabi_attribute 34, 0.eabi_attribute 18, 4.file "simple.c".text.align 2.global add.arch armv4t.syntax unified.arm.fpu softvfp.type add, %function
add:@ Function supports interworking.@ args = 0, pretend = 0, frame = 8@ frame_needed = 1, uses_anonymous_args = 0@ link register save eliminated.str fp, [sp, #-4]!add fp, sp, #0sub sp, sp, #12str r0, [fp, #-8]str r1, [fp, #-12]ldr r2, [fp, #-8]ldr r3, [fp, #-12]add r3, r2, r3mov r0, r3add sp, fp, #0@ sp neededldr fp, [sp], #4bx lr.size add, .-add.align 2.global main.syntax unified.arm.fpu softvfp.type main, %function
main:@ Function supports interworking.@ args = 0, pretend = 0, frame = 8@ frame_needed = 1, uses_anonymous_args = 0push {fp, lr}add fp, sp, #4sub sp, sp, #8mov r1, #3mov r0, #5bl addstr r0, [fp, #-8]ldr r3, [fp, #-8]mov r0, r3sub sp, fp, #4@ sp neededpop {fp, lr}bx lr.size main, .-main.ident "GCC: (GNU Arm Embedded Toolchain 9-2020-q2-update) 9.3.1 20200408 (release)"
代码分析
add 函数
str fp, [sp, #-4]!:将当前帧指针fp的值保存到栈中,并将栈指针sp向下偏移4字节。!表示更新后sp的值。add fp, sp, #0:将sp的值赋给fp,即建立当前函数的栈帧。sub sp, sp, #12:为局部变量分配12字节的空间。str r0, [fp, #-8]和str r1, [fp, #-12]:分别将函数参数a和b存储到栈帧中。ldr r2, [fp, #-8]和ldr r3, [fp, #-12]:从栈帧中加载参数a和b的值到寄存器r2和r3。add r3, r2, r3:计算a + b,结果存储在r3中。mov r0, r3:将计算结果r3设置为函数返回值。add sp, fp, #0:恢复栈指针sp到函数调用前的位置。ldr fp, [sp], #4:恢复帧指针fp的值。bx lr:返回到调用该函数的地方。lr寄存器保存了返回地址。
main 函数
push {fp, lr}:将当前的帧指针fp和链接寄存器lr保存到栈中。add fp, sp, #4:建立当前函数的栈帧。sub sp, sp, #8:为局部变量分配8字节的空间。mov r1, #3和mov r0, #5:将常数3和5分别加载到寄存器r1和r0中,作为add函数的参数。bl add:调用add函数。str r0, [fp, #-8]:将add函数的返回值存储到栈帧中。ldr r3, [fp, #-8]:从栈帧中加载add函数返回值到寄存器r3。mov r0, r3:将r3设置为main函数的返回值。sub sp, fp, #4:恢复栈指针sp到函数调用前的位置。pop {fp, lr}:恢复帧指针fp和链接寄存器lr的值。bx lr:返回到调用该函数的地方。
总结
通过上述步骤,我们展示了如何使用ARM编译器将一个简单的C程序编译成汇编代码。这个过程包括安装编译工具链、编写和编译C代码、生成和分析汇编代码。生成的汇编代码展示了C程序如何在低级别实现,包括函数调用、参数传递和返回值处理等细节。