1.mov指令
mov指令主要用于寄存器之间搬移,也可用于立即数搬移到寄存器;
将立即数搬移到寄存器只有如下两种情况:
(1)16位立即数
(2)16位立即数左移16位/32位/48位后的立即数
2.ldr指令
(1) 基于基地址的寻址模式
基地址保存在寄存器中,常用的指令格式如下:
ldr reg1, [reg2]
(2)变基模式
在基于基地址的寻址模式之上,可以修改reg2中的地址值,常用的指令格式如下:
ldr reg1, [reg2, #8]! #前变基模式
ldr reg1, [reg2], #8 #后变基模式
(3)PC相对地址模式
将pc+offset对应地址处的值加载到寄存器中:
ldr reg0, offset(一个立即数)
立即数的值要在0~32KB之间。
源代码:
.global ldr_test
ldr_test:
// lab1. 测试ldr地址偏移模式
mov x1, 0x80000
mov x3, 16
/* 读取0x80000地址的值到x0寄存器*/
ldr x0, [x1]
ldr x7, 0x20
/* 读取0x80008地址的值*/
ldr x2, [x1, #8]
/* 读取x1+x3 地址的值*/
ldr x4, [x1, x3]
/* 读取(x1+ x3<<3) 地址的值*/
ldr x5, [x1, x3, lsl #3]
// lab2:观察ldr前変基模式和后变基模式
/* 前变基模式*/
ldr x6, [x1, #8]!
/* 后变基模式 */
ldr x7, [x1], #8
//lab3: 观察前变基和后变基的str指令
/* 观测前变基的str,观察x2的值,地址0x400000的值 */
mov x2, 0x400000
ldr x6, =0x1234abce
str x6, [x2, #8]!
/* 观测后变基的str,观察x2的值,地址0x500000的值 */
mov x2, 0x500000
str x6, [x2], #8
ret
反汇编:
(gdb) disassemble
Dump of assembler code for function ldr_test:
0x00000000000802b0 <+0>: mov x1, #0x80000 // #524288
0x00000000000802b4 <+4>: mov x3, #0x10 // #16
0x00000000000802b8 <+8>: ldr x0, [x1]
0x00000000000802bc <+12>: ldr x7, 0x802dc <ldr_test+44>
=> 0x00000000000802c0 <+16>: ldr x2, [x1, #8]
0x00000000000802c4 <+20>: ldr x4, [x1, x3]
0x00000000000802c8 <+24>: ldr x5, [x1, x3, lsl #3]
0x00000000000802cc <+28>: ldr x6, [x1, #8]!
0x00000000000802d0 <+32>: ldr x7, [x1], #8
0x00000000000802d4 <+36>: mov x2, #0x400000 // #4194304
0x00000000000802d8 <+40>: ldr x6, 0x802f0 <ldr_test+64>
0x00000000000802dc <+44>: str x6, [x2, #8]!
0x00000000000802e0 <+48>: mov x2, #0x500000 // #5242880
0x00000000000802e4 <+52>: str x6, [x2], #8
0x00000000000802e8 <+56>: ret
0x00000000000802ec <+60>: .inst 0x00000000 ; undefined
0x00000000000802f0 <+64>: and w14, w30, #0xf07ff07f
0x00000000000802f4 <+68>: .inst 0x00000000 ; undefined
End of assembler dump.
显然, ldr x7, 0x20指令被编译器替换成了ldr x7, 0x802dc <ldr_test+44>,该条指令对应的pc值是0x00000000000802bc,正好满足0x802dc = pc+0x20。
验证:
(gdb) info registers x7
x7 0xd2a00a02f8008c46 -3269602321603982266
(gdb) x/zg 0x00000000000802dc
0x802dc <ldr_test+44>: 0xd2a00a02f8008c46
可见,内存内容和加载到寄存器中的内容一样。
3.ldr伪指令
会被编译器编译为其他指令来实现功能。
.global ldr_test
ldr_test:
// lab1. 测试ldr地址偏移模式
mov x1, 0x80000
mov x3, 16
/* 读取0x80000地址的值到x0寄存器*/
ldr x0, [x1]
/* 读取0x80008地址的值*/
ldr x2, [x1, #8]
/* 读取x1+x3 地址的值*/
ldr x4, [x1, x3]
/* 读取(x1+ x3<<3) 地址的值*/
ldr x5, [x1, x3, lsl #3]
// lab2:观察ldr前変基模式和后变基模式
/* 前变基模式*/
ldr x6, [x1, #8]!
/* 后变基模式 */
ldr x7, [x1], #8
//lab3: 观察前变基和后变基的str指令
/* 观测前变基的str,观察x2的值,地址0x400000的值 */
mov x2, 0x400000
ldr x6, =0x1234abce
str x6, [x2, #8]!
/* 观测后变基的str,观察x2的值,地址0x500000的值 */
mov x2, 0x500000
str x6, [x2], #8
ret
关注ldr x6, =0x1234abce这条伪指令。
反汇编:
(gdb) disassemble
Dump of assembler code for function ldr_test:
0x00000000000802b0 <+0>: mov x1, #0x80000 // #524288
0x00000000000802b4 <+4>: mov x3, #0x10 // #16
0x00000000000802b8 <+8>: ldr x0, [x1]
0x00000000000802bc <+12>: ldr x2, [x1, #8]
0x00000000000802c0 <+16>: ldr x4, [x1, x3]
0x00000000000802c4 <+20>: ldr x5, [x1, x3, lsl #3]
0x00000000000802c8 <+24>: ldr x6, [x1, #8]!
0x00000000000802cc <+28>: ldr x7, [x1], #8
0x00000000000802d0 <+32>: mov x2, #0x400000 // #4194304
0x00000000000802d4 <+36>: ldr x6, 0x802e8 <ldr_test+56>
0x00000000000802d8 <+40>: str x6, [x2, #8]!
=> 0x00000000000802dc <+44>: mov x2, #0x500000 // #5242880
0x00000000000802e0 <+48>: str x6, [x2], #8
0x00000000000802e4 <+52>: ret
0x00000000000802e8 <+56>: and w14, w30, #0xf07ff07f
0x00000000000802ec <+60>: .inst 0x00000000 ; undefined
可见0x1234abce被编译器放在了0x802e8这个地址处,这个值又被反汇编器解释成了:and w14, w30, #0xf07ff07f,至于为什么是这条指令我们暂且不管,这里我们只关注0x1234abce这个值。
验证:
(gdb) x/zg ldr_test+56
0x802e8 <ldr_test+56>: 0x000000001234abce