ARM彙編(基於樹莓派3B)

  1. 開始
  2. 數據加載與加法
  3. 有用的工具

寄存器

A program on an ARM processor in user mode has access to 16 registers:
• R0 to R12: 通用寄存器
• R13: 棧指針寄存器
• R14: 連接寄存器。R13 and R14 are used in the context of calling functions, and we’ll explain these in more detail when we cover subroutines.
• R15: 程序計數器。 The memory address of the currently executing instruction.
• Current Program Status Register (CPSR): This 17th register contains bits of information on the last
instruction executed. More on the CPSR when we cover branch instructions (if statements).

Hello world

.global _start

_start: mov R0, #1
        ldr R1, =helloworld
        mov R2, #13
        mov R7, #4
        svc 0
        mov R0, #0
        mov R7, #1
        svc 0

.data
helloworld: .ascii "Hello World!\n"

.global_start定義爲一個全局標號, 這樣鏈接器(the ld command in our build file)纔可以訪問它. 彙編程序把包含 _start的標號作爲程序的入口, then the linker can find it because it has been defined as a global variable. All our programs will contain this somewhere.
• 我們的程序可以有很多個.s 文件, but only one can contain _start.

彙編指令

We only use three different Assembly language statements in this example:

  1. MOV which moves data into a register. In this case, 我們使用了一個以“#”開頭的立即數. So “MOV R1, #4” means move the number 4 into R1. In this case, 4是指令的一部分而不會存儲在內存中的某個位置. 在源程序中, 操作數可以是大寫的也可以是小寫的; I tend to prefer lowercase in my program listings.
  2. LDR R1, =helloworld” statement which loads register 1 with the address of the string we want to print.
  3. SVC 0執行0號軟件中斷.它將控制權交給Linux內核中的中斷處理程序,該處理程序解釋我們在各個寄存器中設置的參數並完成實際工作。

數據(Data)

Next, we have .data which indicates the following instructions are in the data section of the program:
• In this, we have a label “helloworld” followed by an .ascii statement and then the string we want to print.
.ascii語句告訴彙編程序只需將我們的字符串放入數據部分,然後就可以像在LDR語句中一樣通過標號訪問它。稍後我們將討論文本如何用數字表示,此處的編碼方案稱爲ASCII。
• The last “\n” character is how we represent a new line. If we don’t include this, you must press return to see the text in the terminal window.

Linux系統調用

This program makes two Linux system calls to do its work. The first is the Linux write to file command (#4). Normally, we would have to open a file first before using this command, but when Linux runs a program, it opens three files for it:

  1. stdin (input from the keyboard)
  2. stdout (output to the screen)
  3. stderr (also output to the screen)

當在Linux shell使用>,<和|時將會發生重定向。 對於任何Linux系統調用,根據需要,將參數放入寄存器R0–R4中。然後返回碼存放在R0中。每個系統調用都通過將其功能編號放入R7來指定。
執行軟件中斷而不是分支或子程序調用的原因是,我們可以調用Linux,而無需知道該程序在內存中的位置。這意味着隨着Linux的更新以及其程序在內存中的移動,我們無需更改程序中的任何地址。
軟件中斷的另一個好處是提供了一種標準的機制來切換優先級。我們將在第7章“ Linux操作系統服務”中討論Linux系統調用。

ARM指令格式

在這裏插入圖片描述
Condition: Allows the instruction to execute depending on the bits in the CPSR. We’ll examine this in detail when we get to branching instructions.
Operand type: 指定19–0位上的操作數類型. We could have specified some of these bits, since we used two registers and an immediate operand in this example.
Opcode: Which instruction are we performing, like ADD or MUL.
Set condition code:這條指令是否更改CPSR. If we don’t want the result of this instruction to affect following branch instructions, we would set it to 0.
Operand register: One register to use as input.
Destination register: Where to put the result of whatever this instruction does.
Immediate operand: 通常這是一小部分數據,可以在指令中直接指定。因此,如果要將1加到一個寄存器中,則可以將其設爲1,而不是將1放入另一個寄存器中並將兩個寄存器相加。該字段的格式非常複雜,需要較大的部分來解釋所有細節,但這是基本思想。

大小端

ARM CPU之所以稱爲bi-endian,是因爲它可以使用大小端的任一種。 CPSR中有一個程序狀態標誌,指出要使用的字節序。默認情況下,Raspbian和你的程序使用小端存儲(類似於Intel處器)。

小端的優勢

優點是無需任何地址運算就能更改整數的存儲大小。如4字節的數變成1字節表示,取第1個字節即可;變成2字節表示,取前2個字節即可。
儘管Raspbian使用的是小端,但互聯網上使用的許多協​​議(如TCP / IP)使用了大端,因此在將數據從樹莓派遷移到外界時需要進行轉換。

MOV/MVN

In this section, we are going to look at several forms of the MOV instruction:

  1. MOV RD, #imm16
  2. MOVT RD, #imm16
  3. MOV RD, RS
  4. MOV RD, operand2
  5. MVN RD, operand2

我們已經見過了第一種情況,將一小部分存入寄存器。這裏的立即數可以是任何16位的數,它會被放在指定寄存器的低16位中。這種形式的MOV指令非常簡單。因此,我們經常使用它。

MOVT

這條指令用於將16位的立即數加載到指定寄存器的高16位而不會影響它的低16位。例如,如果我們想要將0x4F5D6E3A加載到寄存器R2,我們可以這樣做:
MOV R2, #0x6E3A
MOVT R2, #0x4F5D

Operand2

There are two formats for Operand2:

  1. A register and a shift
  2. A small number and a rotation

Register and Shift

MOV R1, R2, LSL #1
把R2邏輯左移1位的結果保存在R1中。
可以簡寫成:
LSL R1, R2, #1

Small Number and Rotation

這個比較難理解。回顧一下上面的指令格式,立即數操作數使用了11-0共計12位,這12位由兩部分組成:

  1. 一個字節大小的數,需要8位
  2. 一個表示循環右移位數爲0,2,4,6,8 … 30的偶數,需要4位(4位2進制數的值*2)

我們要做的,首先是把這一個字節大小的“小”數擴展爲32位,然後循環右移,然後我們會得到另一個數,比如:
• 0 - 255 [0 - 0xff ]
• 256,260,264,…,1020 [0x100-0x3fc, 步長 4, 通過0x40-0xff 循環右移30位得到]
• 1024,1040,1056,…,4080 [0x400-0xff0, 步長 16, 通過0x40-0xff 循環右移28位得到]
• 4096,4160, 4224,…,16320 [0x1000-0x3fc0, 步長 64, 通過0x40-0xff 循環右移26位得到]

這意味着,很多32位的立即數可以通過一個8位的數+4位的循環位數來表示,也就是說,有些情況下我們把一個32位的立即數通過MOV指令送給寄存器也是被允許的,例如:

@ 超過了16位立即數的範圍但可以被表示
MOV R1, #0xAB000000

這個是被允許的,因爲0xAB000000可以通過AB循環右移8位得到

@ 超過了16位立即數的範圍且不能被表示
MOV R1, #0xABCDEF11

因爲彙編器找不到這樣一個字節數和循環位數的組合來表示它,所以會報錯:
Error: invalid constant (abcdef11) after fixup
要加載它到寄存器,需要搭配使用MOV / MOVT。
仔細思考不難發現,當一個數中1的個數超過8位時,它不能被表示(或取反,往下看)。當然,還有其它條件,這些留給你們慢慢思考。

MVN

和MOV類似,只不過它將源數據取反碼後加載到目的寄存器中。
MVN是一個獨特的操作碼,而不是另一個帶有隱祕參數的指令的別名。 ARM32指令集只有16個操作碼,因此這是一條重要的指令,具有三個主要用途:

  1. 計算反碼。它有其用途,但是否保證有自己的操作碼?
  2. 乘以–1。我們看到,通過移位操作,我們可以乘以或除以2的冪。這條指令使我們可以乘以–1。記住,數字的負數是數字的補碼或反碼加一。這意味着我們可以通過執行此指令將其乘以–1,然後加一。我們爲什麼要這樣做而不是使用乘法(MUL)指令?位移也一樣,爲什麼要這樣做而不是使用MUL?原因是乘法指令太慢了,要花費幾個時鐘週期來完成。位移指令僅僅需要一個時鐘週期,並且使用MVN和ADD,我們乘-1只需要2個時鐘週期。乘-1是很常見的操作,現在我們可以更快的完成它。
  3. 通過額外的位(13位相比於12位),可以獲得的值的數量是原來的兩倍。事實證明,對於MVN和MOV,使用字節數和偶數移位獲得的所有數字都不同。這意味着如果彙編器發現指定的數無法在MOV指令中表示,則它將嘗試將其更改爲MVN指令,反之亦然。因此,你實際上有13位立即數據,而不是12位。注意:它仍然可能無法表示你的數字,可能仍需要使用MOV / MOVT對。

ADD和ADC

ADD R0, R1, #1
把R1和1相加的結果載入R0

ADD R1, #1等價於ADD R1, R1, #1

ADC:帶進位的加法

ADDS:允許這條指令改變CPSR
其它指令也一樣,在後面加上S即可改變CPSR。

GDB

r 執行程序
l 看十行代碼
disassemble _start 看實際的二進制編碼
b _start 斷點
s 單步執行
i r 查看寄存器
c 繼續執行直到下一斷點或程序結束
i b 查看所有斷點的號碼
delete num 刪除num號斷點
x /Nfu addr 查看內存
q 退出GDB
where
N is the number of objects to display
• f is the display format where some common ones are
• t for binary
• x for hexadecimal
• d for decimal
• i for instruction
• s for string
u is unit size, and is any of
• b for bytes
• h for halfwords (16 bits)
• w for words (32 bits)
• g for giant words (64 bits)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章