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