1. 主要是为了搞清楚原子操作的实现原理。
2. 在 c语言环境下 变量赋值应该是可以被打断。
3. 还有是在c语言中变量赋值操作对应的汇编代码。
4. 原子操作的主要流程 read ---> modify -----> write (RWM )
那么接下来先看一下C 语言代码:
#include
int func()
{
int a = 10;
int b = 0;
b = a;
return b;
}
int main()
{
int a = 10;
int b = a;
b = func();
printf("a %d,b %d \n",a,b);
return 0;
}
反汇编之后的汇编代码,
#反汇编主要这两步
1. arm-none-gcc -c main.c -o main.o
2. arm-none-objdump -S main.o > main.s
# 也可以一步到位
1. arm-none-gcc -S main.c -o main.s
main.o: file format elf32-littlearm
Disassembly of section .text:
00000000 :
0: e52db004 push {fp} ; (str fp, [sp, #-4]!)
4: e28db000 add fp, sp, #0
8: e24dd00c sub sp, sp, #12
c: e3a0300a mov r3, #10
10: e50b300c str r3, [fp, #-12]
14: e3a03000 mov r3, #0
18: e50b3008 str r3, [fp, #-8]
1c: e51b300c ldr r3, [fp, #-12]
20: e50b3008 str r3, [fp, #-8]
24: e51b3008 ldr r3, [fp, #-8]
28: e1a00003 mov r0, r3
2c: e24bd000 sub sp, fp, #0
30: e49db004 pop {fp} ; (ldr fp, [sp], #4)
34: e12fff1e bx lr
@ fp 和 sp 都是栈指针,一个是局部函数类的指针,一个是全局的栈指针,可以着这么理解
00000038 :
38: e92d4800 push {fp, lr} @ 将fp lr 入栈,此时 sp = sp + 4 + 4 = sp + 8
3c: e28db004 add fp, sp, #4 @ fp = sp + 8 + 4 = sp + 12
40: e24dd008 sub sp, sp, #8 @ sp = sp - 8
@ 对照源码进行分析 r3 此时因该保存的局部变量 a存放的内存地址
44: e3a0300a mov r3, #10 @ sp fp 保持不变 r3 = 10
48: e50b300c str r3, [fp, #-12] @ fp - 12 = r3 , sp 和 fp 保持不变
4c: e51b300c ldr r3, [fp, #-12] @ r3 = fp - 12 , fp 和 sp 保持不变
50: e50b3008 str r3, [fp, #-8] @ fp - 8 = r3 ,fp 和 sp 保持不变
54: ebfffffe bl 0
58: e50b0008 str r0, [fp, #-8] @ r0 函数func的返回值 fp - 8 = r0
@ 这里r2 应该是保存的 变量b 的地址
5c: e51b2008 ldr r2, [fp, #-8] @ r2 = fp - 8,相当于 r2 = r0
60: e51b100c ldr r1, [fp, #-12] @ r1 = fp - 12
64: e59f0010 ldr r0, [pc, #16] ; 7c @ arm 三级流水线 ,如果此时正在执行这条指令,那么此时的pc = 当前地址 + 8, r0 = 7c
68: ebfffffe bl 0
6c: e3a03000 mov r3, #0
70: e1a00003 mov r0, r3
74: e24bd004 sub sp, fp, #4
78: e8bd8800 pop {fp, pc}
7c: 00000000 .word 0x00000000
然后有两个问题:
1. 一个函数内部的局部变量地址是怎么保存的? 是否是保存在某些通用的寄存器中?
2. 汇编中 fp 和 sp 区别?
3. C 语言中赋值操作 : 先把变量的值 从保存变量的内存地址中读出来放到某个寄存器中,然后赋值给保存其他变量地址的寄存器。