獻(xiàn)給與我一樣喜歡學(xué)習(xí)的人?。。?^_^, 用了不到一個(gè)星期的時(shí)間把匯編語言程序設(shè)計(jì)這本書給大致
看了一遍, 其中下面這些省略了浮點(diǎn)數(shù)及IA-32如SSE FPU等特殊的指令集部分, 我覺得重要的是學(xué)習(xí)
linux匯編的語法及編譯原理和程序控制流程, 具體的指令細(xì)節(jié)就不那么重要了。 有什么問題大家可以
一起交流:
aishen944@163.com################################################################################################
# 一, IA-32 硬件特性
################################################################################################
寄存器:
1, 通用寄存器, 用于存放正在處理的數(shù)據(jù)
EAX 用于操作數(shù)和結(jié)果數(shù)的累加器
EBX 指向數(shù)據(jù)內(nèi)存斷中的數(shù)據(jù)的指針
ECX 字符串和循環(huán)操作的計(jì)數(shù)器
EDX IO指針
EDI 用于字符串操作的目標(biāo)的數(shù)據(jù)指針
ESI 用于字符串操作的源的數(shù)據(jù)指針
ESP 堆棧指針
EBP 堆棧數(shù)據(jù)指針
其中寄存器EAX, EBX, ECX, EDX又可以通過16位和8位寄存器名稱引用
如EAX, AX 引用EAX低16位, AL 引用EAX低8位, AH 引用AL之后的高8位
2, 段寄存器:
IA-32平臺(tái)允許使用3中內(nèi)存模型: 平坦內(nèi)存模式 分段內(nèi)存模式 實(shí)地址模式
平坦內(nèi)存: 把全部的系統(tǒng)內(nèi)存表示為連續(xù)的地址空間, 通過線性地址的特定地址
訪問內(nèi)存位置.
分段內(nèi)存: 把系統(tǒng)內(nèi)存劃分為獨(dú)立的段組, 通過位于寄存器中的指針進(jìn)行引用. 每
個(gè)段用于包含特定類型的數(shù)據(jù)。 一個(gè)段用于包含指令碼, 另一個(gè)段包
含數(shù)據(jù)元素, 第三個(gè)段包含數(shù)據(jù)堆棧。
段中的內(nèi)存位置是通過邏輯地址引用的, 邏輯地址是由段地址加上偏移
量構(gòu)成, 處理器把邏輯地址轉(zhuǎn)換為相應(yīng)的線性地址以便訪問。
段寄存器:
CS 代碼段
DS 數(shù)據(jù)段
SS 堆棧段
ES 附加段指針
FS 附加段指針
GS 附加段指針
每個(gè)段寄存器都是16位的, 包含指向內(nèi)存特定段起始位置的指針,程序不能
顯示加載或改變CS寄存器, DS, ES, FS, GS都用于指向數(shù)據(jù)段, 通過4個(gè)獨(dú)立
的段, 程序可以分隔數(shù)據(jù)元素, 確保他們不會(huì)重疊, 程序必須加載帶有段的
正確指針值的數(shù)據(jù)段寄存器, 并且使用偏移值引用各個(gè)內(nèi)存的位置。
SS段寄存器用于指向堆棧段, 堆棧包含傳遞給函數(shù)和過程的數(shù)據(jù)值。
實(shí)地址: 如果實(shí)地址模式, 所有段寄存器都指向線性0地址, 并且都不會(huì)被程序改動(dòng),
所有的指令碼 數(shù)據(jù)元素 堆棧元素 都是通過他們的線性地址直接訪問的。
3, 指令指針寄存器
是EIP寄存器, 它跟蹤要執(zhí)行程序的下一條指令代碼, 應(yīng)用程序不能修改指令指針本身,不
能指定內(nèi)存地址把它拖放EIP寄存器中,相反必須通過一般的跳轉(zhuǎn)指令來改變預(yù)存取緩存的
下一條指令。
在平坦內(nèi)存模型中, 指令指針包含下一條指令碼的線性地址, 在分段模型中指令指針包含
邏輯地址指針, 通過CS寄存器的內(nèi)存引用。
4, 控制寄存器
CRO 控制操作模式 和 處理器當(dāng)前狀態(tài)的系統(tǒng)標(biāo)志
CR1 當(dāng)前沒有使用
CR2 內(nèi)存頁面錯(cuò)誤信息
CR3 內(nèi)存頁面目錄信息
CR4 支持處理器特性和說明處理器特性能力的標(biāo)志
不能直接訪問控制寄存器, 但是能把控制寄存器中的值傳遞給通用寄存器,如果必須改動(dòng)控制
寄存器的標(biāo)志, 可以改動(dòng)通用寄存器的值, 然后把內(nèi)容傳遞給控制寄存器。
標(biāo)志:
IA-32使用單一的寄存器來包含一組狀態(tài)控制和系統(tǒng)標(biāo)志, EFLAGS寄存器包含32位標(biāo)志信息
1, 狀態(tài)標(biāo)志
標(biāo)志 位 說明
CF 0 進(jìn)位標(biāo)志, 如果無符號(hào)數(shù)的數(shù)學(xué)操作產(chǎn)生最高有效位的進(jìn)位或者借位, 此時(shí)值為1
PF 2 奇偶校驗(yàn)標(biāo)志, 用于表明數(shù)學(xué)操作的結(jié)果寄存器中的是否包含錯(cuò)誤數(shù)據(jù)
AF 4 輔助進(jìn)位標(biāo)志, 用于二進(jìn)制編碼的10進(jìn)制(BCD)的數(shù)學(xué)操作中, 如果用于運(yùn)算的
寄存器的第三位發(fā)生進(jìn)位或借位, 該值為1
ZF 6 0標(biāo)志, 如果操作為0, 則該值為1
SF 7 符號(hào)標(biāo)志, 設(shè)置為結(jié)果的最高有效位, 這一位是符號(hào)位表明結(jié)果是正值還是負(fù)值
OF 11 溢出標(biāo)志
2, 控制標(biāo)志
當(dāng)前只定義了一個(gè)控制標(biāo)志DF即方向標(biāo)志, 用于控制處理器處理字符串的方式
如果設(shè)置為1, 字符串指令自動(dòng)遞減內(nèi)存地址以便到達(dá)字符串中的下一字節(jié)。
反之。
3, 系統(tǒng)標(biāo)志
標(biāo)志 位 說明
TF 8 陷阱標(biāo)志, 設(shè)置為1時(shí)啟用單步模式, 在單步模式下處理器每次只執(zhí)行一條命令。
IF 9 中斷使能標(biāo)志, 控制處理器如響應(yīng)從外部源接收到的信號(hào)。
IOPL 12和13 IO特權(quán)級(jí)別標(biāo)志, 表明當(dāng)前正在運(yùn)行任務(wù)的IO特權(quán)級(jí)別, 它定義IO地址空間的
特權(quán)訪問級(jí)別, 該值必須小于或者等于訪問I/O地址空間的級(jí)別; 否則任何訪問
IO空間的請(qǐng)求都會(huì)被拒絕!
NT 14 嵌套任務(wù)標(biāo)志控制當(dāng)前運(yùn)行的任務(wù)是否連接到前一個(gè)任務(wù), 它用于連接被中斷
和被調(diào)用的任務(wù).
RF 16 恢復(fù)標(biāo)志用于控制在調(diào)試模式中如何響應(yīng)異常。
VM 17 虛擬8086模式, 表明處理器在虛擬8086模式中而不是保護(hù)模式或者實(shí)模式。
AC 18 對(duì)準(zhǔn)檢查標(biāo)志, 用于啟用內(nèi)存引用的對(duì)準(zhǔn)檢查
VIF 19 虛擬中斷標(biāo)志, 當(dāng)處理器在虛擬模式中操作時(shí), 該標(biāo)志起IF標(biāo)志的作用.
VIP 20 虛擬中斷掛起標(biāo)志, 在虛擬模式操作時(shí)用于表示一個(gè)中斷正在被掛起。
ID 21 表示CPU是否支持cpuid指令, 如果處理器能夠設(shè)置或者清零這個(gè)標(biāo)志, 表示
處理器支持該指令。
################################################################################################
# 二,GNU匯編工具系列
################################################################################################
1, 二進(jìn)制工具系列
addr2line 把地址轉(zhuǎn)換成文件名或者行號(hào)
ar 創(chuàng)建 修改或者展開文件存檔
as 把匯編語言代碼匯編成目標(biāo)代碼
常用選項(xiàng):
-a -> 指定輸出中包含那些清單
-D -> 包含它用于向下兼容 但是被忽略
--defsym -> 在匯編代碼之前定義符號(hào)和值
-f -> 快速匯編跳過注釋和空白
--gstabs -> 包含每行源代碼的調(diào)試信息
--gstats+ -> 包含gdb專門的調(diào)試信息
-I -> 指定包含文件的目錄
-J -> 不警告帶符號(hào)溢出
-L -> 在符號(hào)表中保存本地符號(hào)
-o -> 給定輸出目標(biāo)名
-R -> 把數(shù)據(jù)段合并進(jìn)文本段
--statistics -> 顯示匯編使用的最大空間和總時(shí)間
-v -> 顯示as的版本號(hào)
-W -> 不顯示警告信息
c++filt 還原c++符號(hào)的過濾器
gprof 顯示程序簡(jiǎn)檔信息的程序
ld 把目標(biāo)代碼文件轉(zhuǎn)換成可執(zhí)行文件的轉(zhuǎn)換器
常用選項(xiàng):
-d -> 指定目標(biāo)代碼輸入文件的格式
-Bstatic -> 只使用靜態(tài)庫(kù)
-Bdynamic -> 只使用動(dòng)態(tài)庫(kù)
-Bsymbolic-> 把引用捆綁到共享庫(kù)中的全局符號(hào)
-c -> 從指定的命令文件讀取命令
-cref -> 創(chuàng)建跨引用表
-defsym -> 在輸出文件中創(chuàng)建指定的全局符號(hào)
-demangle -> 在錯(cuò)誤消息中還原符號(hào)名稱
-e -> 使用指定的符號(hào)作為程序的初始執(zhí)行點(diǎn)
-E -> 對(duì)于elf文件把所有的符號(hào)添加到動(dòng)態(tài)符號(hào)表
-share -> 創(chuàng)建共享庫(kù)
-Ttext -> 使用指定的地址作為文本段的起始點(diǎn)
-Tdata -> 使用指定的地址作為數(shù)據(jù)段的起始點(diǎn)
-Tbss -> 使用指定的地址作為bss段的起始點(diǎn)
-L -> 把指定的路徑添加到庫(kù)搜索清單
-O -> 生成優(yōu)化的輸出文件
-o -> 指定輸出名
-oformat -> 指定輸出文件的二進(jìn)制格式
-R -> 從指定的文件讀取符號(hào)和地址
-rpath -> 把指定的位置添加到運(yùn)行時(shí)庫(kù)搜索路徑
-rpath-link-> 指定搜索運(yùn)行時(shí)共享庫(kù)的路徑
-X -> 刪除本地所有臨時(shí)符號(hào)
-x -> 刪除本地所有符號(hào)
nm 列出目標(biāo)文件中的符號(hào)
objcopy 復(fù)制或翻譯目標(biāo)文件
objdump 顯示來自目標(biāo)文件的信息
ranlib 生成存檔文件內(nèi)容的索引
readelf 按照elf格式顯示目標(biāo)文件信息
size 列出目標(biāo)文件或者存檔文件的段長(zhǎng)度
strings 顯示目標(biāo)文件中可打印字符串
strip 丟棄符號(hào)
windres 編譯Microsoft Windows資源文件
2, GNU編譯器
gcc
常用選項(xiàng):
-c 編譯或者匯編代碼但不進(jìn)行連接
-S 編譯后停止但不進(jìn)行匯編
-E 預(yù)處理后停止但不進(jìn)行編譯
-o 指定輸出文件名
-v 顯示每個(gè)編譯階段使用的命令
-std 指定使用的語言標(biāo)準(zhǔn)
-g 生成調(diào)試信息
-pg 生成gprof制作簡(jiǎn)檔要使用的額外代碼
-O 優(yōu)化可執(zhí)行代碼
-W 設(shè)置編譯器警告級(jí)別
-I 指定包含文件清單
-L 指定庫(kù)文件目錄
-D 預(yù)定義源代碼中使用的宏
-U 取消任何定義了的宏
-f 指定控制編譯器行為的選項(xiàng)
-m 指定與硬件相關(guān)的選項(xiàng)
3, GNU調(diào)試程序
gdb
常用選項(xiàng):
-d 指定遠(yuǎn)程調(diào)試時(shí)串行接口的線路速度
-batch 以批處理模式運(yùn)行
-c 指定要分析的核心轉(zhuǎn)儲(chǔ)文件
-cd 指定工作目錄
-d 指定搜索源文件的目錄
-e 指定要執(zhí)行的文件
-f 調(diào)試時(shí)以標(biāo)準(zhǔn)格式輸出文件名和行號(hào)
-q 安靜模式
-s 指定符號(hào)的文件名
-se 指定符號(hào)和要執(zhí)行的文件名
-tty 設(shè)置標(biāo)準(zhǔn)輸出和輸入設(shè)備
-x 從指定的文件執(zhí)行g(shù)db命令
由于gnu調(diào)試時(shí)忽略開始處斷點(diǎn), 需要在開始標(biāo)簽處執(zhí)行一個(gè)空指令
如:
.globl _start
_start:
nop
此時(shí)斷點(diǎn)可以設(shè)置成 break *_start+1
查看寄存器狀態(tài)info registers
使用print命令查看特定寄存器或者變量的值, 加上修飾符可以得到不同的輸出格式:
print/d 顯示十進(jìn)制數(shù)字
print/t 顯示二進(jìn)制數(shù)字
print/x 顯示16進(jìn)制數(shù)字
使用x命令可以查看特定內(nèi)存的值:
x/nyz
其中 n為要顯示的字段數(shù)
y時(shí)輸出格式, 它可以是:
c 用于字符, d用于十進(jìn)制, x用于16進(jìn)制
z是要顯示的字段長(zhǎng)度, 它可以是:
b用于字節(jié), h用于16字節(jié), w用于32位字
如:
x/42cb 用于顯示前42字節(jié)
################################################################################################
# 三, GNU匯編語言結(jié)構(gòu)
################################################################################################
主要包括三個(gè)常用的段:
data 數(shù)據(jù)段 聲明帶有初始值的元素
bss 數(shù)據(jù)段 聲明使用0或者null初始化的元素
text 正文段 包含的指令, 每個(gè)匯編程序都必須包含此段
使用.section 指令定義段, 如:
.section .data
.section .bss
.section .text
起始點(diǎn):
gnu匯編器使用_start標(biāo)簽表示默認(rèn)的起始點(diǎn), 此外如果想要匯編內(nèi)部的標(biāo)簽?zāi)軌虮煌獠砍绦蛟L問,
需要使用.globl 指令, 如:.globl _start
使用通用庫(kù)函數(shù)時(shí)可以使用:
ld -dynamic-linker /lib/ld-linux.so.2
################################################################################################
# 四, 數(shù)據(jù)傳遞
################################################################################################
1, 數(shù)據(jù)段
使用.data聲明數(shù)據(jù)段, 這個(gè)段中聲明的任何數(shù)據(jù)元素都保留在內(nèi)存中并可以被匯編程序的指令讀取,
此外還可以使用.rodata聲明只讀的數(shù)據(jù)段, 在聲明一個(gè)數(shù)據(jù)元素時(shí), 需要使用標(biāo)簽和命令:
標(biāo)簽:用做引用數(shù)據(jù)元素所使用的標(biāo)記, 它和c語言的變量很相似, 它對(duì)于處理器是沒有意義的, 它
只是用做匯編器試圖訪問內(nèi)存位置時(shí)用做引用指針的一個(gè)位置。
指令:這個(gè)名字指示匯編器為通過標(biāo)簽引用的數(shù)據(jù)元素保留特定數(shù)量的內(nèi)存, 聲明命令之后必須給出
一個(gè)或多個(gè)默認(rèn)值。
聲明指令:
.ascii 文本字符串
.asciz 以空字符結(jié)尾的字符串
.byte 字節(jié)值
.double 雙精度浮點(diǎn)值
.float 單精度浮點(diǎn)值
.int 32位整數(shù)
.long 32位整數(shù), 和int相同
.octa 16字節(jié)整數(shù)
.quad 8字節(jié)整數(shù)
.short 16位整數(shù)
.single 單精度浮點(diǎn)數(shù)(和float相同)
例子:
output:
.ascii "hello world."
pi:
.float 2.14
聲明可以在一行中定義多個(gè)值, 如:
ages:
.int 20, 10, 30, 40
定義靜態(tài)符號(hào):
使用.equ命令把常量值定義為可以在文本段中使用的符號(hào),如:
.section .data
.equ LINUX_SYS_CALL, 0x80
.section .text
movl $LINUX_SYS_CALL, %eax
2, bss段
和data段不同, 無需聲明特定的數(shù)據(jù)類型, 只需聲明為所需目的保留的原始內(nèi)存部分即可。
GNU匯編器使用以下兩個(gè)命令聲明內(nèi)存區(qū)域:
.comm 聲明為未初始化的通用內(nèi)存區(qū)域
.lcomm 聲明為未初始化的本地內(nèi)存區(qū)域
兩種聲明很相似, 但.lcomm是為不會(huì)從本地匯編代碼之外進(jìn)行訪問的數(shù)據(jù)保留的, 格式為:
.comm/.lcomm symbol, length
例子:
.section .bss
.lcomm buffer, 1000
該語句把1000字節(jié)的內(nèi)存地址賦予標(biāo)簽buffer, 在聲明本地通用內(nèi)存區(qū)域的程序之外的函數(shù)是
不能訪問他們的.(不能在.globl命令中使用他們)
在bss段聲明的好處是, 數(shù)據(jù)不包含在可執(zhí)行文件中。在數(shù)據(jù)段中定義數(shù)據(jù)時(shí), 它必須被包含在
可執(zhí)行程序中, 因?yàn)楸仨毷褂锰囟ㄖ党跏蓟?因?yàn)椴皇褂脭?shù)據(jù)初始化bss段中聲明的數(shù)據(jù)區(qū)域,
所以內(nèi)存區(qū)域被保留在運(yùn)行時(shí)使用, 并且不必包含在最終的程序中
3, 傳送數(shù)據(jù)
move 指令:
格式 movex 源操作數(shù), 目的操作數(shù)。 其中x為要傳送數(shù)據(jù)的長(zhǎng)度, 取值有:
l 用于32位的長(zhǎng)字節(jié)
w 用于16位的字
b 用于8位的字節(jié)值
立即數(shù)前面要加一個(gè)$符號(hào), 寄存器前面要加%符號(hào)。
8個(gè)通用的寄存器是用于保存數(shù)據(jù)的最常用的寄存器, 這些寄存器的內(nèi)容可以傳遞
給其他的任何可用的寄存器。 和通用寄存器不同, 專用寄存器(控制, 調(diào)試, 段)
的內(nèi)容只能傳送給通用寄存器, 或者接收從通用寄存器傳過來的內(nèi)容。
在對(duì)標(biāo)簽進(jìn)行引用時(shí):
例:
.section .data
value:
.int 100
_start:
movl value, %eax
movl $value, %eax
movl %ebx, (%edi)
movl %ebx, 4(%edi)
其中:movl value, %eax 只是把標(biāo)簽value當(dāng)前引用的內(nèi)存值傳遞給eax
movl $value, %eax 把標(biāo)簽value當(dāng)前引用的內(nèi)存地址指針傳遞給eax
movl %ebx, (%edi) 如果edi外面沒有括號(hào)那么這個(gè)指令只是把ebx中的
值加載到edi中, 如果有了括號(hào)就表示把ebx中的內(nèi)容
傳送給edi中包含的內(nèi)存位置。
movl %ebx, 4(%edi) 表示把edi中的值放在edi指向的位置之后的4字節(jié)內(nèi)存位置中
movl %ebx, -4(%edi) 表示把edi中的值放在edi指向的位置之前的4字節(jié)內(nèi)存位置中
cmove 指令(條件轉(zhuǎn)移):
cmovex 源操作數(shù), 目的操作數(shù). x的取值為:
無符號(hào)數(shù):
a/nbe 大于/不小于或者等于
ae/nb 大于或者等于/不小于
nc 無進(jìn)位
b/nae 小于/不大于等于
c 進(jìn)位
be/na 小于或等于/不大于
e/z 等于/零
ne/nz 不等于/不為零
p/pe 奇偶校驗(yàn)/偶校驗(yàn)
np/po 非奇偶校驗(yàn)/奇校驗(yàn)
有符號(hào)數(shù):
ge/nl 大于或者等于/不小于
l/nge 小于/不大于或者等于
le/ng 小于或者等于/不大于
o 溢出
no 未溢出
s 帶符號(hào)(負(fù))
ns 無符號(hào)(非負(fù))
交換數(shù)據(jù):
xchg 在兩個(gè)寄存器之間或者寄存器和內(nèi)存間交換值
如:
xchg 操作數(shù), 操作數(shù), 要求兩個(gè)操作數(shù)必須長(zhǎng)度相同且不能同時(shí)都是內(nèi)存位置
其中寄存器可以是32,16,8位的
bswap 反轉(zhuǎn)一個(gè)32位寄存器的字節(jié)順序
如: bswap %ebx
xadd 交換兩個(gè)值 并把兩個(gè)值只和存儲(chǔ)在目標(biāo)操作數(shù)中
如: xadd 源操作數(shù),目標(biāo)操作數(shù)
其中源操作數(shù)必須是寄存器, 目標(biāo)操作數(shù)可以是內(nèi)存位置也可以是寄存器
其中寄存器可以是32,16,8位的
cmpxchg
cmpxchg source, destination
其中source必須是寄存器, destination可以是內(nèi)存或者寄存器, 用來比較
兩者的值, 如果相等,就把源操作數(shù)的值加載到目標(biāo)操作數(shù)中, 如果不等就把
目標(biāo)操作數(shù)加載到源操作數(shù)中,其中寄存器可以是32,16,8位的, 其中源操作
數(shù)是EAX,AX或者AL寄存器中的值
cmpxchg8b 同cmpxchg, 但是它處理8字節(jié)值, 同時(shí)它只有一個(gè)操作數(shù)
cmpxchg8b destination
其中destination引用一個(gè)內(nèi)存位置, 其中的8字節(jié)值會(huì)與EDX和EAX寄存器中
包含的值(EDX高位寄存器, EAX低位寄存器)進(jìn)行比較, 如果目標(biāo)值和EDX:EAX
對(duì)中的值相等, 就把EDX:EAX對(duì)中的64位值傳遞給內(nèi)存位置, 如果不匹配就把
內(nèi)存地址中的值加載到EDX:EAX對(duì)中
4, 堆棧
ESP 寄存器保存了當(dāng)前堆棧的起始位置, 當(dāng)一個(gè)數(shù)據(jù)壓入棧時(shí), 它就會(huì)自動(dòng)遞減,
反之其自動(dòng)遞增
壓入堆棧操作:
pushx source, x取值為:
l 32位長(zhǎng)字
w 16位字
彈出堆棧操作:
popx source
其中source必須是16或32位寄存器或者內(nèi)存位置, 當(dāng)pop最后一個(gè)元素時(shí)ESP值應(yīng)該
和以前的相等
5,壓入和彈出所有寄存器
pusha/popa 壓入或者彈出所有16位通用寄存器
pushad/popad 壓入或者彈出所有32位通用寄存器
pushf/popf 壓入或者彈出EFLAGS寄存器的低16位
pushfd/popfd 壓入或者彈出EFLAGS寄存器的全部32位
6,數(shù)據(jù)地址對(duì)齊
gas 匯編器支持.align 命令, 它用于在特定的內(nèi)存邊界對(duì)準(zhǔn)定義的數(shù)據(jù)元素, 在數(shù)據(jù)段
中.align命令緊貼在數(shù)據(jù)定義的前面
#######################################################
#########################################
# 五,控制流程
################################################################################################
無條件跳轉(zhuǎn):
1, 跳轉(zhuǎn)
jmp location 其中l(wèi)ocation為要跳轉(zhuǎn)到的內(nèi)存地址, 在匯編中為定義的標(biāo)簽
2,調(diào)用
調(diào)用指令分為兩個(gè)部分:
1, 調(diào)用call address 跳轉(zhuǎn)到指定位置
2, 返回指令ret, 它沒有參數(shù)緊跟在call指令后面的位置
執(zhí)行call指令時(shí),它把EIP的值放到堆棧中, 然后修改EIP以指向被調(diào)用的函數(shù)地址, 當(dāng)被調(diào)用
函數(shù)完成后, 它從堆棧獲取過去的EIP的值, 并把控制權(quán)返還給原始程序。
3,中斷
由硬件設(shè)備生成中斷。 程序生成軟件中斷
當(dāng)一個(gè)程序產(chǎn)生中斷調(diào)用時(shí), 發(fā)出調(diào)用的程序暫停, 被調(diào)用的程序接替它運(yùn)行, 指令指針被轉(zhuǎn)移到
被調(diào)用的函數(shù)地址, 當(dāng)調(diào)用完成時(shí)使用中斷返回指令可以返回調(diào)原始程序。
條件跳轉(zhuǎn):
條件跳轉(zhuǎn)按照EFLAGS中的值來判斷是否該跳轉(zhuǎn), 格式為:
jxx address, 其中xx是1-3個(gè)字符的條件代碼, 取值如下:
a 大于時(shí)跳轉(zhuǎn)
ae 大于等于
b 小于
be 小于等于
c 進(jìn)位
cxz 如果CX寄存器為0
ecxz 如果ECS寄存器為0
e 相等
na 不大于
nae 不大于或者等于
nb 不小于
nbe 不小于或等于
nc 無進(jìn)位
ne 不等于
g 大于(有符號(hào))
ge 大于等于(有符號(hào))
l 小于(有符號(hào))
le 小于等于(有符號(hào))
ng 不大于(有符號(hào))
nge 不大于等于(有符號(hào))
nl 不小于
nle 不小于等于
no 不溢出
np 不奇偶校驗(yàn)
ns 無符號(hào)
nz 非零
o 溢出
p 奇偶校驗(yàn)
pe 如果偶校驗(yàn)
po 如果奇校驗(yàn)
s 如果帶符號(hào)
z 如果為零
條件跳轉(zhuǎn)不支持分段內(nèi)存模型下的遠(yuǎn)跳轉(zhuǎn), 如果在該模式下進(jìn)行
程序設(shè)計(jì)必須使用程序邏輯確定條件是否存在, 然后實(shí)現(xiàn)無條件
跳轉(zhuǎn), 跳轉(zhuǎn)前必須設(shè)置EFLAGS寄存器
比較:
cmp operend1, operend2
進(jìn)位標(biāo)志修改指令:
CLC 清空進(jìn)位標(biāo)志(設(shè)置為0)
CMC 對(duì)進(jìn)位標(biāo)志求反(把它改變?yōu)橄喾吹闹?
STC 設(shè)置進(jìn)位標(biāo)志(設(shè)置為1)
循環(huán):
loop 循環(huán)直到ECX寄存器為0
loope/loopz 循環(huán)直到ecx寄存器為0 或者沒有設(shè)置ZF標(biāo)志
loopne/loopnz 循環(huán)直到ecx為0或者設(shè)置了ZF標(biāo)志
指令格式為: loopxx address 注意循環(huán)指令只支持8位偏移地址
################################################################################################
# 六,數(shù)字
################################################################################################
IA-32平臺(tái)中存儲(chǔ)超過一字節(jié)的數(shù)都被存儲(chǔ)為小尾數(shù)的形式但是把數(shù)字傳遞給寄存器時(shí), 寄存器里面保存是按照大尾數(shù)
的形式存儲(chǔ)
把無符號(hào)數(shù)轉(zhuǎn)換成位數(shù)更大的值時(shí), 必須確保所有的高位部分都被設(shè)置為零
把有符號(hào)數(shù)轉(zhuǎn)換成位數(shù)更大的數(shù)時(shí):
intel 提供了movsx指令它允許擴(kuò)展帶符號(hào)數(shù)并保留符號(hào), 它與movzx相似, 但是它假設(shè)要傳送的字節(jié)是帶符號(hào)數(shù)形式
浮點(diǎn)數(shù):
fld 指令用于把浮點(diǎn)數(shù)字傳送入和傳送出FPU寄存器, 格式:
fld source
其中source可以為32 64或者80位整數(shù)值
IA-32使用FLD指令用于把存儲(chǔ)在內(nèi)存中的單精度和雙精度浮點(diǎn)值FPU寄存器堆棧中, 為了區(qū)分這兩種長(zhǎng)度GNU匯編器使用
FLDS加載單精度浮點(diǎn)數(shù), FLDL加載雙精度浮點(diǎn)數(shù)
類似FST用于獲取FPU寄存器堆棧中頂部的值, 并且把這個(gè)值放到內(nèi)存位置中, 對(duì)于單精度使用FSTS, 對(duì)于雙精度使用FSTL
################################################################################################
# 七,基本數(shù)學(xué)運(yùn)算
################################################################################################
1, 加法
ADD source, destination 把兩個(gè)整數(shù)相加
其中source可以是立即數(shù)內(nèi)存或者寄存器, destination可以是內(nèi)存或者寄存器, 但是兩者不能同時(shí)都是內(nèi)存位置
ADC 和ADD相似進(jìn)行加法運(yùn)算, 但是它把前一個(gè)ADD指令的產(chǎn)生進(jìn)位標(biāo)志的值包含在其中, 在處理位數(shù)大于32(如64)
位的整數(shù)時(shí), 該指令非常有用
2, 減法
SUB source, destination 把兩個(gè)整數(shù)相減
NEG 它生成值的補(bǔ)碼
SBB 指令, 和加法操作一樣, 可以使用進(jìn)位情況幫助執(zhí)行大的無符號(hào)數(shù)值的減法運(yùn)算. SBB在多字節(jié)減法操作中利用
進(jìn)位和溢出標(biāo)志實(shí)現(xiàn)跨數(shù)據(jù)邊界的的借位特性
3,遞增和遞減
dec destination 遞減
inc destination 遞增
其中dec和inc指令都不會(huì)影響進(jìn)位標(biāo)志, 所以遞增或遞減計(jì)數(shù)器的值都不會(huì)影響程序中涉及進(jìn)位標(biāo)志的其他任何運(yùn)算
4, 乘法
mul source 進(jìn)行無符號(hào)數(shù)相乘
它使用隱含的目標(biāo)操作數(shù), 目標(biāo)位置總是使用eax的某種形式, 這取決與源操作數(shù)的長(zhǎng)度, 因此根據(jù)源操作數(shù)的長(zhǎng)度,
目標(biāo)操作數(shù)必須放在AL, AX, EAX中。 此外由于乘法可能產(chǎn)生很大的值, 目標(biāo)位置必須是源操作數(shù)的兩倍位置, 源為
8時(shí), 應(yīng)該是16, 源為16時(shí), 應(yīng)該為32, 但是當(dāng)源為16位時(shí)intel為了向下兼容, 目標(biāo)操作數(shù)不是存放在eax中, 而
是分別存放在DX:AX中, 結(jié)果高位存儲(chǔ)在DX中, 地位存儲(chǔ)在AX中。對(duì)于32位的源, 目標(biāo)操作數(shù)存儲(chǔ)在EDX:EAX中, 其中
EDX存儲(chǔ)的是高32位, EAX存儲(chǔ)的是低32位
imul source 進(jìn)行有符號(hào)數(shù)乘法運(yùn)算, 其中的目標(biāo)操作數(shù)和mul的一樣
imul source, destination 也可以執(zhí)行有符號(hào)乘法運(yùn)算, 但是此時(shí)可以把目標(biāo)放在指定的位置, 使用這種格式的缺陷
在與乘法的操作結(jié)果被限制為單一目標(biāo)寄存器的長(zhǎng)度.
imul multiplier, source, destination
其中multiplier是一個(gè)立即數(shù), 這種方式允許一個(gè)值與給定的源操作數(shù)進(jìn)行快速的乘法運(yùn)算, 然后把結(jié)果存儲(chǔ)在通用
寄存器中
5, 除法
div divisor 執(zhí)行無符號(hào)數(shù)除法運(yùn)算
除數(shù)的最大值取決與被除數(shù)的長(zhǎng)度, 對(duì)于16位被除數(shù) ,除數(shù)只能為8位, 32或64位同上
被除數(shù) 被除數(shù)長(zhǎng)度 商 余數(shù)
AX 16位 AL AH
DX:AX 32位 AX DX
EDX:EAX 64位 EAX EDX
idiv divisor 執(zhí)行有符號(hào)數(shù)的除法運(yùn)算, 方式和div一樣
6, 移位
左移位:
sal 向左移位
sal destination 把destination向左移動(dòng)1位
sal %cl, destination 把destination的值向左移動(dòng)CL寄存器中指定的位數(shù)
sal shifter, destination 把destination的值向左移動(dòng)shifter值指定的位數(shù)
向左移位可以對(duì)帶符號(hào)數(shù)和無符號(hào)數(shù)執(zhí)行向左移位的操作, 移位造成的空位用零填充, 移位造成的超過數(shù)據(jù)長(zhǎng)度的任何位
都被存放在進(jìn)位標(biāo)志中, 然后在下一次移位操作中被丟棄
右移位:
shr向右移位
sar向右移位
SHR指令清空移位造成的空位, 所以它只能對(duì)無符號(hào)數(shù)進(jìn)行移位操作
SAR指令根據(jù)整數(shù)的符號(hào)位, 要么清空, 要么設(shè)置移位造成的空位, 對(duì)于負(fù)數(shù), 空位被設(shè)置為1
循環(huán)移位:
和移位指令類似, 只不過溢出的位被存放回值的另一端, 而不是丟棄
ROL 向左循環(huán)移位
ROR 向右循環(huán)移位
RCL 向左循環(huán)移位, 并且包含進(jìn)位標(biāo)志
RCR 向右循環(huán)移位, 并且包含進(jìn)位標(biāo)志
7, 邏輯運(yùn)算
AND OR XOR
這些指令使用相同的格式:
and source, destination
其中source可以是8位 16 位或者32位的立即值 寄存器或內(nèi)存中的值, destination可以是8位 16 位或者
32位寄存器或內(nèi)存中的值, 不能同時(shí)使用內(nèi)存值作為源和目標(biāo)。 布爾邏輯功能對(duì)源和目標(biāo)執(zhí)行按位操作。
也就是說使用指定的邏輯功能按照順序?qū)?shù)據(jù)的元素的每個(gè)位進(jìn)行單獨(dú)比較。
NOT指令使用單一操作數(shù), 它即是源值也是目標(biāo)結(jié)果的位置
清空寄存器的最高效方式是使用OR指令對(duì)寄存器和它本身進(jìn)行異或操作.當(dāng)和本身進(jìn)行XOR操作時(shí), 每個(gè)設(shè)置為
1的位就變?yōu)?, 每個(gè)設(shè)置為0的位也變位0。
位測(cè)試可以使用以上的邏輯運(yùn)算指令, 但這些指令會(huì)修改destination的值, 因此intel提供了test指令, 它不
會(huì)修改目標(biāo)值而是設(shè)置相應(yīng)的標(biāo)志
################################################################################################
# 八,字符串處理
################################################################################################
1, 傳送字符串
movs 有三種格式
movsb 傳送單一字節(jié)
movsw 傳送一個(gè)字
movsl 傳送雙字
movs指令使用隱含的源和目的操作數(shù), 隱含的源操作數(shù)是ESI, 隱含的目的操作數(shù)是EDI, 有兩種方式加載內(nèi)存地址到
ESI和EDI, 第一種是使用標(biāo)簽間接尋址 movl $output, %ESI, 第二種是使用lea指令, lea指令加載對(duì)象的地址到指定
的目的操作數(shù)如lea output, %esi, 每次執(zhí)行movs指令后, 數(shù)據(jù)傳送后ESI和EDI寄存器會(huì)自動(dòng)改變,為另一次傳送做
準(zhǔn)備, ESI和EDI可能隨著標(biāo)志DF的不同自動(dòng)遞增或者自動(dòng)遞減, 如果DF標(biāo)志為0則movs指令后ESI和EDI會(huì)遞增, 反之會(huì)
遞減, 為了設(shè)置DF標(biāo)志, 可以使用一下指令:
CLD 將DF標(biāo)志清零
STD 設(shè)置DF標(biāo)志
2,rep前綴
REP 指令的特殊之處在與它不執(zhí)行什么操作, 這條指令用于按照特定次數(shù)重復(fù)執(zhí)行字符串指令, 有ECX寄存器控制,
但不需要額外的loop指令, 如rep movsl
rep的其他格式:
repe 等于時(shí)重復(fù)
repne 不等于時(shí)重復(fù)
repnz 不為零時(shí)重復(fù)
repz 為零時(shí)重復(fù)
3, 存儲(chǔ)和加載字符串
LODS 加載字符串, ESI為源, 當(dāng)一次執(zhí)行完lods時(shí)會(huì)遞增或遞減ESI寄存器, 然后把字符串值存放到EAX中
STOS 使用lods把字符串值加載到EAX后, 可以使用它把EAX中的值存儲(chǔ)到內(nèi)存中去:
stos使用EDI作為目的操作數(shù), 執(zhí)行stos指令后, 會(huì)根據(jù)DF的值自動(dòng)遞增或者遞減EDI中的值
4, 比較字符串
cmps 和其他的操作字符串的指令一樣, 隱含的源和目標(biāo)操作數(shù)都為ESI和EDI, 每次執(zhí)行時(shí)都會(huì)根據(jù)DF的值把
ESI和EDI遞增或者遞減, cmps指令從目標(biāo)字符串中減去源字符串, 執(zhí)行后會(huì)設(shè)置EFLAGS寄存器的狀態(tài).
5,掃描字符串
scas 把EDI作為目標(biāo), 它把EDI中的字符串和EAX中的字符串進(jìn)行比較 ,然后根據(jù)DF的值遞增或者遞減EDI
################################################################################################
# 九,使用函數(shù)
################################################################################################
GNU匯編語言定義函數(shù)的語法:
.type 標(biāo)簽(也就是函數(shù)名), @function
ret 返回到調(diào)用處
################################################################################################
# 十,linux系統(tǒng)調(diào)用
################################################################################################
linux系統(tǒng)調(diào)用的中斷向量為0x80
1, 系統(tǒng)調(diào)用標(biāo)識(shí)存放在%eax中
2, 系統(tǒng)調(diào)用輸入值:
EBX 第一個(gè)參數(shù)
ECX 第二個(gè)參數(shù)
EDX 第三個(gè)參數(shù)
ESI 第四個(gè)參數(shù)
EDI 第五個(gè)參數(shù)
需要輸入超過6個(gè)輸入?yún)?shù)的系統(tǒng)調(diào)用, EBX指針用于保存指向輸入?yún)?shù)內(nèi)存位置的指針, 輸入?yún)?shù)按照連續(xù)的的順序
存儲(chǔ), 系統(tǒng)調(diào)用的返回值存放在EAX中
################################################################################################
# 十一,匯編語言的高級(jí)功能
################################################################################################
1,gnu內(nèi)聯(lián)匯編的語法:
asm或__asm__("匯編代碼");
指令必須包含在引號(hào)里
如果包含的指令超過一行 必須使用新行分隔符分隔
使用c全局變量, 不能在內(nèi)聯(lián)匯編中使用局部變量, 注意在匯編語言代碼中值被用做內(nèi)存位置, 而不是立即數(shù)值
如果不希望優(yōu)化內(nèi)聯(lián)匯編, 則可以volatile修飾符如:__asm__ volatile("code");
2,GCC內(nèi)聯(lián)匯編的擴(kuò)展語法
__asm__("assembly code":output locations:input operands:changed registers);
第一部分是匯編代碼
第二部分是輸出位置, 包含內(nèi)聯(lián)匯編代碼的輸出值的寄存器和內(nèi)存位置列表
第三部分是輸入操作數(shù),包含內(nèi)聯(lián)匯編代碼輸入值的寄存器和內(nèi)存位置的列表
第四部分是改動(dòng)的寄存器, 內(nèi)聯(lián)匯編改變的任何其他寄存器的列表
這幾個(gè)部分可以不全有, 但是沒有的還必須使用:分隔
1, 指定輸入值和輸出值, 輸入值和輸出值的列表格式為:
"constraint"(variable), 其中variable是程序中聲明的c變量, 在擴(kuò)展asm格式中, 局部和全局變量都可以使用,
使用constrant(約束)定義把變量存放到哪(輸入)或從哪里傳送變量(輸出)
約束使用單一的字符, 如下:
約束 描述
a 使用%eax, %ax, %al寄存器
b 使用%ebx, %bx, %bl寄存器
c 使用%ecx, %cx, %cl寄存器
d 使用%edx, %dx, %dl寄存器
S 使用%esi, %si寄存器
D 使用%edi, %di寄存器
r 使用任何可用的通用寄存器
q 使用%eax, %ebx, %ecx,%edx之一
A 對(duì)于64位值使用%eax, %edx寄存器
f 使用浮點(diǎn)寄存器
t 使用第一個(gè)(頂部)的浮點(diǎn)寄存器
u 使用第二個(gè)浮點(diǎn)寄存器
m 使用變量的內(nèi)存位置
o 使用偏移內(nèi)存位置
V 只使用直接內(nèi)存位置
i 使用立即整數(shù)值
n 使用值已知的立即整數(shù)值
g 使用任何可用的寄存器和內(nèi)存位置
除了這些約束之外, 輸出值還包含一個(gè)約束修飾符:
輸出修飾符 描述
+ 可以讀取和寫入操作數(shù)
= 只能寫入操作數(shù)
% 如果有必要操作數(shù)可以和下一個(gè)操作數(shù)切換
& 在內(nèi)聯(lián)函數(shù)完成之前, 可以刪除和重新使用操作數(shù)
如:
__asm__("assembly code": "=a"(result):"d"(data1),"c"(data2));
把c變量data1存放在edx寄存器中, 把c變量data2存放到ecx寄存器中, 內(nèi)聯(lián)匯編的結(jié)果
將存放在eax寄存器中, 然后傳送給變量result
在擴(kuò)展的asm語句塊中如果要使用寄存器必須使用兩個(gè)百分號(hào)符號(hào)
不一定總要在內(nèi)聯(lián)匯編代碼中指定輸出值, 一些匯編指令假定輸入值包含輸出值, 如movs指令
其他擴(kuò)展內(nèi)聯(lián)匯編知識(shí):
1, 使用占位符
輸入值存放在內(nèi)聯(lián)匯編段中聲明的特定寄存器中, 并且在匯編指令中專門使用這些寄存器.
雖然這種方式能夠很好的處理只有幾個(gè)輸入值的情況, 但對(duì)于需要很多輸入值的情況, 這
中方式顯的有點(diǎn)繁瑣. 為了幫助解決這個(gè)問題, 擴(kuò)展asm格式提供了占位符, 可以在內(nèi)聯(lián)
匯編代碼中使用它引用輸入和輸出值.
占位符是前面加上百分號(hào)的數(shù)字, 按照內(nèi)聯(lián)匯編中列出的每個(gè)輸入和輸出值在列表中的位置,
每個(gè)值被賦予從0開始的地方. 然后就可以在匯編代碼中引用占位符來表示值。
如果內(nèi)聯(lián)匯編代碼中的輸入和輸出值共享程序中相同的c變量, 則可以指定使用占位符作為
約束值, 如:
__asm__("imull %1, %0"
: "=r"(data2)
: "r"(data1), "0"(data2));
如輸入輸出值中共享相同的變量data2, 而在輸入變量中則可以使用標(biāo)記0作為輸入?yún)?shù)的約束
2, 替換占位符
如果處理很多輸入和輸出值, 數(shù)字型的占位符很快就會(huì)變的很混亂, 為了使條理清晰 ,GNU匯編
器(從版本3.1開始)允許聲明替換的名稱作為占位符.替換的名稱在聲明輸入值和輸出值的段中
定義, 格式如下:
%[name]"constraint"(variable)
定義的值name成為內(nèi)聯(lián)匯編代碼中變量的新的占位符號(hào)標(biāo)識(shí), 如下面的例子:
__asm__("imull %[value1], %[value2]"
: [value2] "=r"(data2)
: [value1] "r"(data1), "0"(data2));
3, 改動(dòng)寄存器列表
編譯器假設(shè)輸入值和輸出值使用的寄存器會(huì)被改動(dòng), 并且相應(yīng)的作出處理。程序員不需要在改動(dòng)的
寄存器列表中包含這些值, 如果這樣做了, 就會(huì)產(chǎn)生錯(cuò)誤消息. 注意改動(dòng)的寄存器列表中的寄存器
使用完整的寄存器名稱, 而不像輸入和輸出寄存器定義的那樣僅僅是單一字母。 在寄存器名稱前面
使用百分號(hào)符號(hào)是可選的。
改動(dòng)寄存器列表的正確使用方法是, 如果內(nèi)聯(lián)匯編代碼使用了沒有被初始化地聲明為輸入或者輸出
值的其他任何寄存器 , 則要通知編譯器。編譯器必須知道這些寄存器, 以避免使用他們。如:
int main(void) {
int data1 = 10;
int result = 20;
__asm__("movl %1, %%eax\n\t"
"addl %%eax, %0"
: "=r"(result)
: "r"(data1), "0"(result)
: "%eax");
printf("The result is %d\n", result);
return 0;
}
4, 使用內(nèi)存位置
雖然在內(nèi)聯(lián)匯編代碼中使用寄存器比較快, 但是也可以直接使用c變量的內(nèi)存位置。 約束m用于引用輸入值
和輸出值中的內(nèi)存位置。 記住, 對(duì)于要求使用寄存器的匯編指令, 仍然必須使用寄存器, 所以不得不定義
保存數(shù)據(jù)的中間寄存器。如:
int main(void) {
int dividentd = 20;
int divisor = 5;
int result;
__asm__("divb %2\n\t"
"movl %%eax, %0"
: "=m"(result)
: "a"(dividend), "m"(divisor));
printf("The result is %d\n", result);
return 0;
}
5, 處理跳轉(zhuǎn)
內(nèi)聯(lián)匯編語言代碼也可以包含定義其中位置的標(biāo)簽。 可以實(shí)現(xiàn)一般的匯編條件分支和無條件分支, 如:
int main(void) {
int a = 10;
int b = 20;
int result;
__asm__("cmp %1, %2\n\t"
"jge greater\n\t"
"movl %1, %0\n\t"
"jmp end\n"
"greater:\n\t"
"movl %2, %0\n"
"end:"
:"=r"(result)
:"r"(a), "r"(b));
printf("The larger value is %d\n", result);
return 0;
}
在內(nèi)聯(lián)匯編代碼中使用標(biāo)簽時(shí)有兩個(gè)限制。 第一個(gè)限制是只能跳轉(zhuǎn)到相同的asm段內(nèi)的標(biāo)簽,
不能從-個(gè)asm段跳轉(zhuǎn)到另一個(gè)asm段中的標(biāo)簽。第二個(gè)限制更加復(fù)雜一點(diǎn)。 以上程序使用
標(biāo)簽greater和end。 但是, 這樣有個(gè)潛在的問題, 查看匯編后的代碼清單, 可以發(fā)現(xiàn)內(nèi)聯(lián)
匯編標(biāo)簽也被編碼到了最終匯編后的代碼中。 這意味著如果在c代碼中還有另一個(gè)asm段, 就
不能再次使用相同的標(biāo)簽, 否則會(huì)因?yàn)闃?biāo)簽重復(fù)使用而導(dǎo)致錯(cuò)誤消息。還有如果試圖整合使用
c關(guān)鍵字(比如函數(shù)名稱或者全局變量)的標(biāo)簽也會(huì)導(dǎo)致錯(cuò)誤。
################################################################################################
# 十二,優(yōu)化你的代碼
################################################################################################
GNU編譯器提供-O選項(xiàng)供程序優(yōu)化使用:
-O 提供基礎(chǔ)級(jí)別的優(yōu)化
-O2 提供更加高級(jí)的代碼優(yōu)化
-O3 提供最高級(jí)的代碼優(yōu)化
不同的優(yōu)化級(jí)別使用的優(yōu)化技術(shù)也可以單獨(dú)的應(yīng)用于代碼。 可以使用-f命令行選項(xiàng)引用每個(gè)
單獨(dú)的優(yōu)化技術(shù)。
1, 編譯器優(yōu)化級(jí)別1
在優(yōu)化的第一個(gè)級(jí)別執(zhí)行基礎(chǔ)代碼的優(yōu)化。 這個(gè)級(jí)別試圖執(zhí)行9種單獨(dú)的優(yōu)化功能:
-fdefer-pop: 這種優(yōu)化技術(shù)與匯編語言代碼在函數(shù)完成時(shí)如何進(jìn)行操作有關(guān)。 一般
情況下, 函數(shù)的輸入值被保存在堆棧種并且被函數(shù)訪問。 函數(shù)返回時(shí), 輸入值還在
堆棧種。 一般情況下, 函數(shù)返回之后, 輸入值被立即彈出堆棧。這樣做會(huì)使堆棧種
的內(nèi)容有些雜亂。
-fmerge-constans: 使用這種優(yōu)化技術(shù), 編譯器試圖合并相同的常量. 這一特性有
時(shí)候會(huì)導(dǎo)致很長(zhǎng)的編譯時(shí)間, 因?yàn)榫幾g器必須分析c或者c++程序中用到的每個(gè)常量,
并且相互比較他們.
-fthread-jumps: 使用這種優(yōu)化技術(shù)與編譯器如果處理匯編代碼中的條件和非條件
分支有關(guān)。 在某些情況下, 一條跳轉(zhuǎn)指令可能轉(zhuǎn)移到另一條分支語句。 通過一連串
跳轉(zhuǎn), 編譯器確定多個(gè)跳轉(zhuǎn)之間的最終目標(biāo)并且把第一個(gè)跳轉(zhuǎn)重新定向到最終目標(biāo)。
-floop-optimize: 通過優(yōu)化如何生成匯編語言中的循環(huán), 編譯器可以在很大程序上
提高應(yīng)用程序的性能。 通常, 程序由很多大型且復(fù)雜的循環(huán)構(gòu)成。 通過刪除在循環(huán)
內(nèi)沒有改變值的變量賦值操作, 可以減少循環(huán)內(nèi)執(zhí)行指令的數(shù)量, 在很大程度上提高
性能。 此外優(yōu)化那些確定何時(shí)離開循環(huán)的條件分支, 以便減少分支的影響。
-fif-conversion: if-then語句應(yīng)該是應(yīng)用程序中僅次于循環(huán)的最消耗時(shí)間的部分。
簡(jiǎn)單的if-then語句可能在最終的匯編語言代碼中產(chǎn)生眾多的條件分支。 通過減少
或者刪除條件分支, 以及使用條件傳送 設(shè)置標(biāo)志和使用運(yùn)算技巧來替換他們, 編譯
器可以減少if-then語句中花費(fèi)的時(shí)間量。
-fif-conversion2: 這種技術(shù)結(jié)合更加高級(jí)的數(shù)學(xué)特性, 減少實(shí)現(xiàn)if-then語句所
需的條件分支。
-fdelayed-branch: 這種技術(shù)試圖根據(jù)指令周期時(shí)間重新安排指令。 它還試圖把
盡可能多的指令移動(dòng)到條件分支前, 以便最充分的利用處理器的治理緩存。
-fguess-branch-probability: 就像其名稱所暗示的, 這種技術(shù)試圖確定條件分支最可
能的結(jié)果, 并且相應(yīng)的移動(dòng)指令, 這和延遲分支技術(shù)類似。 因?yàn)樵诰幾g時(shí)預(yù)測(cè)代碼的安排,
所以使用這一選項(xiàng)兩次編譯相同的c或者c++代碼很可能會(huì)產(chǎn)生不同的匯編語言代碼, 這取決
于編譯時(shí)編譯器認(rèn)為會(huì)使用那些分支。 因?yàn)檫@個(gè)原因, 很多程序員不喜歡采用這個(gè)特性, 并且
專門地使用-fno-guess-branch-probability選項(xiàng)關(guān)閉這個(gè)特性
-fcprop-registers: 因?yàn)樵诤瘮?shù)中把寄存器分配給變量, 所以編譯器執(zhí)行第二次檢查以便減少
調(diào)度依賴性(兩個(gè)段要求使用相同的寄存器)并且刪除不必要的寄存器復(fù)制操作。