引言
在汇编当中不光光需要了解一些寄存器,汇编代码,也需要了解一下内存是怎么分区域的
列表:
- 代码区 特点:可读、可写、可执行
- 栈区域 放参数和局部变量
- 堆区域 动态申请、可读可写
- 全局 可读可写
- 常量 可读
以上都是在编译器中完成的,编译器规定的。
在汇编当中这些都是可读、可写的,因为汇编直接修改的是地址中的值
分析代码
int g = 20;
int func(int a,int b){
printf("wode");
int c = a + g;
return c;
}
int main(int argc, char * argv[]) {
printf("%d",func(1, 2));
return 0;
}
转换汇编
main函数
0x104fe28c4 <+0>: sub sp, sp, #0x30 ; =0x30
0x104fe28c8 <+4>: stp x29, x30, [sp, #0x20]
0x104fe28cc <+8>: add x29, sp, #0x20 ; =0x20
0x104fe28d0 <+12>: orr w8, wzr, #0x1
0x104fe28d4 <+16>: orr w9, wzr, #0x2
0x104fe28d8 <+20>: stur wzr, [x29, #-0x4]
0x104fe28dc <+24>: stur w0, [x29, #-0x8]
0x104fe28e0 <+28>: str x1, [sp, #0x10]
-> 0x104fe28e4 <+32>: mov x0, x8
0x104fe28e8 <+36>: mov x1, x9
0x104fe28ec <+40>: bl 0x104fe2874 ; func at main.m:14
0x104fe28f0 <+44>: mov x30, x0
0x104fe28f4 <+48>: mov x10, sp
0x104fe28f8 <+52>: str x30, [x10]
0x104fe28fc <+56>: adrp x0, 1
0x104fe2900 <+60>: add x0, x0, #0xf2d ; =0xf2d
0x104fe2904 <+64>: bl 0x104fe2bf8 ; symbol stub for: printf
0x104fe2908 <+68>: mov w8, #0x0
0x104fe290c <+72>: str w0, [sp, #0xc]
0x104fe2910 <+76>: mov x0, x8
0x104fe2914 <+80>: ldp x29, x30, [sp, #0x20]
0x104fe2918 <+84>: add sp, sp, #0x30 ; =0x30
0x104fe291c <+88>: ret
func函数
0x1008ce874 <+0>: sub sp, sp, #0x20 ; =0x20
0x1008ce878 <+4>: stp x29, x30, [sp, #0x10]
0x1008ce87c <+8>: add x29, sp, #0x10 ; =0x10
0x1008ce880 <+12>: stur w0, [x29, #-0x4]
0x1008ce884 <+16>: str w1, [sp, #0x8]
-> 0x1008ce888 <+20>: adrp x0, 1
0x1008ce88c <+24>: add x0, x0, #0xf28 ; =0xf28
0x1008ce890 <+28>: bl 0x1008cebf8 ; symbol stub for: printf
0x1008ce894 <+32>: adrp x30, 2
0x1008ce898 <+36>: add x30, x30, #0xd10 ; =0xd10
0x1008ce89c <+40>: ldur w1, [x29, #-0x4]
0x1008ce8a0 <+44>: ldr w8, [x30]
0x1008ce8a4 <+48>: add w8, w1, w8
0x1008ce8a8 <+52>: str w8, [sp, #0x4]
0x1008ce8ac <+56>: ldr w8, [sp, #0x4]
0x1008ce8b0 <+60>: str w0, [sp]
0x1008ce8b4 <+64>: mov x0, x8
0x1008ce8b8 <+68>: ldp x29, x30, [sp, #0x10]
0x1008ce8bc <+72>: add sp, sp, #0x20 ; =0x20
0x1008ce8c0 <+76>: ret
汇编分析
在main函数汇编中:(只写大概的意思就不一一分析了)
- 前三句,分配栈空间,保存栈底 跟 回家的路地址ret后的地址
- 赋值给w8 w9常量也就是func(1, 2)中的1和2
- stur wzr, [x29, #-0x4] wzr 的值存入 x29 - 0x4
- stur w0, [x29, #-0x8] w0的值存入到x29 - 0x8
- str x1, [sp, #0x10] x1的值存入到sp + 0x10
- mov x0, x8 x0 = x8 x0是参数 func中的参数
- mov x1, x9 x1 = x9 x1是参数 func中的参数
后面的不做分析
在func函数汇编中
- 拉伸栈空间
- 保护:x29、x30入栈
- 将 x29 = sp + 0x10 sp地址向上移动16个字节
- 将w0 存入到 x29 - 0x4的地址中
- 将w1 存入到sp + 0x8的地址中
- adrp x0,1
- 1左移动12位 0001 0000 0000 0000
0x1000 - 将pc寄存器的低12位清0
0x102fbe000 - 将pc低12位清0后的值 = 0x1000 赋值给x0寄存器
0x102fbe000
总结
adrp找到的是一个目标数据偏移的相对地址,他是一个不准确的地址,偏移的误差有4KB
为什么偏移的误差是4KB呐? 在地址总线上面,10条地址总线的寻址能力是1024,12条地址总线的能力是4*1024 4KB
- 1左移动12位 0001 0000 0000 0000
-
add x0, x0, #0xf28 x0 =0x102fbff28 这个应该是printf中打印的常量参数
- printf打印上面的常量参数
- adrp x30, 2
- 2左移动12位 0002 0000 0000 0000
0x2000 - 将pc寄存器的低12位清0
0x10013a000 - 将pc低12位清0后的值 = 0x2000 赋值给x0寄存器
0x10013c000
- 2左移动12位 0002 0000 0000 0000
-
add x30, x30, #0xd10 x30 = 0x10013cd10 x30的值是一个地址,int*类型, 因为是地址所以需要去值 *(int *)
- ldur w1, [x29, #-0x4] x29 - 0x4 = w1 值
- ldr w8, [x30] w8 = x30 值
- add w8, w1, w8 w8 = w1 + w8 w1 是参数a w8是全局变量 20
- str w8, [sp, #0x4] w8的值存入 sp+0x4这个地址当中
- ldr w8, [sp, #0x4] 从地址 sp+0x4读取数据赋值给w8 这两句废话
- str w0, [sp] 将w0的值存入sp地址中
- mov x0, x8 x0 = x8
- ldp x29, x30, [sp, #0x10] 从sp+0x10开始分别读取存入x29、x30 回家的路
- add sp, sp, #0x20 收回栈空间 也就是栈平衡
- ret 回家