一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

LINUX匯編(匯編語言程序設(shè)計讀書筆記)

 jijo 2009-08-19
http://blog./u2/63996/showart_499472.html
原文鏈接:http:///bbs/showthread.php?t=289776


獻給與我一樣喜歡學(xué)習(xí)的人!?。?^_^, 用了不到一個星期的時間把匯編語言程序設(shè)計這本書給大致
看了一遍, 其中下面這些省略了浮點數(shù)及IA-32如SSE FPU等特殊的指令集部分, 我覺得重要的是學(xué)習(xí)
linux匯編的語法及編譯原理和程序控制流程, 具體的指令細節(jié)就不那么重要了。 有什么問題大家可以
一起交流: aishen944@163.com

################################################################################################
# 一, IA-32 硬件特性
################################################################################################

寄存器:
1, 通用寄存器, 用于存放正在處理的數(shù)據(jù)
EAX 用于操作數(shù)和結(jié)果數(shù)的累加器
EBX 指向數(shù)據(jù)內(nèi)存斷中的數(shù)據(jù)的指針
ECX 字符串和循環(huán)操作的計數(shù)器
EDX IO指針
EDI 用于字符串操作的目標的數(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平臺允許使用3中內(nèi)存模型: 平坦內(nèi)存模式 分段內(nèi)存模式 實地址模式

平坦內(nèi)存: 把全部的系統(tǒng)內(nèi)存表示為連續(xù)的地址空間, 通過線性地址的特定地址
訪問內(nèi)存位置.

分段內(nèi)存: 把系統(tǒng)內(nèi)存劃分為獨立的段組, 通過位于寄存器中的指針進行引用. 每
個段用于包含特定類型的數(shù)據(jù)。 一個段用于包含指令碼, 另一個段包
含數(shù)據(jù)元素, 第三個段包含數(shù)據(jù)堆棧。
段中的內(nèi)存位置是通過邏輯地址引用的, 邏輯地址是由段地址加上偏移
量構(gòu)成, 處理器把邏輯地址轉(zhuǎn)換為相應(yīng)的線性地址以便訪問。


段寄存器:
CS 代碼段
DS 數(shù)據(jù)段
SS 堆棧段
ES 附加段指針
FS 附加段指針
GS 附加段指針

每個段寄存器都是16位的, 包含指向內(nèi)存特定段起始位置的指針,程序不能
顯示加載或改變CS寄存器, DS, ES, FS, GS都用于指向數(shù)據(jù)段, 通過4個獨立
的段, 程序可以分隔數(shù)據(jù)元素, 確保他們不會重疊, 程序必須加載帶有段的
正確指針值的數(shù)據(jù)段寄存器, 并且使用偏移值引用各個內(nèi)存的位置。
SS段寄存器用于指向堆棧段, 堆棧包含傳遞給函數(shù)和過程的數(shù)據(jù)值。

實地址: 如果實地址模式, 所有段寄存器都指向線性0地址, 并且都不會被程序改動,
所有的指令碼 數(shù)據(jù)元素 堆棧元素 都是通過他們的線性地址直接訪問的。



3, 指令指針寄存器
是EIP寄存器, 它跟蹤要執(zhí)行程序的下一條指令代碼, 應(yīng)用程序不能修改指令指針本身,不
能指定內(nèi)存地址把它拖放EIP寄存器中,相反必須通過一般的跳轉(zhuǎn)指令來改變預(yù)存取緩存的
下一條指令。

在平坦內(nèi)存模型中, 指令指針包含下一條指令碼的線性地址, 在分段模型中指令指針包含
邏輯地址指針, 通過CS寄存器的內(nèi)存引用。



4, 控制寄存器
CRO 控制操作模式 和 處理器當前狀態(tài)的系統(tǒng)標志
CR1 當前沒有使用
CR2 內(nèi)存頁面錯誤信息
CR3 內(nèi)存頁面目錄信息
CR4 支持處理器特性和說明處理器特性能力的標志

不能直接訪問控制寄存器, 但是能把控制寄存器中的值傳遞給通用寄存器,如果必須改動控制
寄存器的標志, 可以改動通用寄存器的值, 然后把內(nèi)容傳遞給控制寄存器。





標志:
IA-32使用單一的寄存器來包含一組狀態(tài)控制和系統(tǒng)標志, EFLAGS寄存器包含32位標志信息

1, 狀態(tài)標志
標志 位 說明
CF 0 進位標志, 如果無符號數(shù)的數(shù)學(xué)操作產(chǎn)生最高有效位的進位或者借位, 此時值為1
PF 2 奇偶校驗標志, 用于表明數(shù)學(xué)操作的結(jié)果寄存器中的是否包含錯誤數(shù)據(jù)
AF 4 輔助進位標志, 用于二進制編碼的10進制(BCD)的數(shù)學(xué)操作中, 如果用于運算的
寄存器的第三位發(fā)生進位或借位, 該值為1
ZF 6 0標志, 如果操作為0, 則該值為1
SF 7 符號標志, 設(shè)置為結(jié)果的最高有效位, 這一位是符號位表明結(jié)果是正值還是負值
OF 11 溢出標志

2, 控制標志
當前只定義了一個控制標志DF即方向標志, 用于控制處理器處理字符串的方式
如果設(shè)置為1, 字符串指令自動遞減內(nèi)存地址以便到達字符串中的下一字節(jié)。
反之。

3, 系統(tǒng)標志
標志 位 說明
TF 8 陷阱標志, 設(shè)置為1時啟用單步模式, 在單步模式下處理器每次只執(zhí)行一條命令。
IF 9 中斷使能標志, 控制處理器如響應(yīng)從外部源接收到的信號。
IOPL 12和13 IO特權(quán)級別標志, 表明當前正在運行任務(wù)的IO特權(quán)級別, 它定義IO地址空間的
特權(quán)訪問級別, 該值必須小于或者等于訪問I/O地址空間的級別; 否則任何訪問
IO空間的請求都會被拒絕!
NT 14 嵌套任務(wù)標志控制當前運行的任務(wù)是否連接到前一個任務(wù), 它用于連接被中斷
和被調(diào)用的任務(wù).
RF 16 恢復(fù)標志用于控制在調(diào)試模式中如何響應(yīng)異常。
VM 17 虛擬8086模式, 表明處理器在虛擬8086模式中而不是保護模式或者實模式。
AC 18 對準檢查標志, 用于啟用內(nèi)存引用的對準檢查
VIF 19 虛擬中斷標志, 當處理器在虛擬模式中操作時, 該標志起IF標志的作用.
VIP 20 虛擬中斷掛起標志, 在虛擬模式操作時用于表示一個中斷正在被掛起。
ID 21 表示CPU是否支持cpuid指令, 如果處理器能夠設(shè)置或者清零這個標志, 表示
處理器支持該指令。






################################################################################################
# 二,GNU匯編工具系列
################################################################################################
1, 二進制工具系列
addr2line 把地址轉(zhuǎn)換成文件名或者行號

ar 創(chuàng)建 修改或者展開文件存檔

as 把匯編語言代碼匯編成目標代碼
常用選項:
-a -> 指定輸出中包含那些清單
-D -> 包含它用于向下兼容 但是被忽略
--defsym -> 在匯編代碼之前定義符號和值
-f -> 快速匯編跳過注釋和空白
--gstabs -> 包含每行源代碼的調(diào)試信息
--gstats+ -> 包含gdb專門的調(diào)試信息
-I -> 指定包含文件的目錄
-J -> 不警告帶符號溢出
-L -> 在符號表中保存本地符號
-o -> 給定輸出目標名
-R -> 把數(shù)據(jù)段合并進文本段
--statistics -> 顯示匯編使用的最大空間和總時間
-v -> 顯示as的版本號
-W -> 不顯示警告信息

c++filt 還原c++符號的過濾器

gprof 顯示程序簡檔信息的程序

ld 把目標代碼文件轉(zhuǎn)換成可執(zhí)行文件的轉(zhuǎn)換器
常用選項:
-d -> 指定目標代碼輸入文件的格式
-Bstatic -> 只使用靜態(tài)庫
-Bdynamic -> 只使用動態(tài)庫
-Bsymbolic-> 把引用捆綁到共享庫中的全局符號
-c -> 從指定的命令文件讀取命令
-cref -> 創(chuàng)建跨引用表
-defsym -> 在輸出文件中創(chuàng)建指定的全局符號
-demangle -> 在錯誤消息中還原符號名稱
-e -> 使用指定的符號作為程序的初始執(zhí)行點
-E -> 對于elf文件把所有的符號添加到動態(tài)符號表
-share -> 創(chuàng)建共享庫
-Ttext -> 使用指定的地址作為文本段的起始點
-Tdata -> 使用指定的地址作為數(shù)據(jù)段的起始點
-Tbss -> 使用指定的地址作為bss段的起始點
-L -> 把指定的路徑添加到庫搜索清單
-O -> 生成優(yōu)化的輸出文件
-o -> 指定輸出名
-oformat -> 指定輸出文件的二進制格式
-R -> 從指定的文件讀取符號和地址
-rpath -> 把指定的位置添加到運行時庫搜索路徑
-rpath-link-> 指定搜索運行時共享庫的路徑
-X -> 刪除本地所有臨時符號
-x -> 刪除本地所有符號

nm 列出目標文件中的符號

objcopy 復(fù)制或翻譯目標文件

objdump 顯示來自目標文件的信息

ranlib 生成存檔文件內(nèi)容的索引

readelf 按照elf格式顯示目標文件信息

size 列出目標文件或者存檔文件的段長度

strings 顯示目標文件中可打印字符串

strip 丟棄符號

windres 編譯Microsoft Windows資源文件

2, GNU編譯器
gcc
常用選項:
-c 編譯或者匯編代碼但不進行連接
-S 編譯后停止但不進行匯編
-E 預(yù)處理后停止但不進行編譯
-o 指定輸出文件名
-v 顯示每個編譯階段使用的命令
-std 指定使用的語言標準
-g 生成調(diào)試信息
-pg 生成gprof制作簡檔要使用的額外代碼
-O 優(yōu)化可執(zhí)行代碼
-W 設(shè)置編譯器警告級別
-I 指定包含文件清單
-L 指定庫文件目錄
-D 預(yù)定義源代碼中使用的宏
-U 取消任何定義了的宏
-f 指定控制編譯器行為的選項
-m 指定與硬件相關(guān)的選項

3, GNU調(diào)試程序
gdb
常用選項:
-d 指定遠程調(diào)試時串行接口的線路速度
-batch 以批處理模式運行
-c 指定要分析的核心轉(zhuǎn)儲文件
-cd 指定工作目錄
-d 指定搜索源文件的目錄
-e 指定要執(zhí)行的文件
-f 調(diào)試時以標準格式輸出文件名和行號
-q 安靜模式
-s 指定符號的文件名
-se 指定符號和要執(zhí)行的文件名
-tty 設(shè)置標準輸出和輸入設(shè)備
-x 從指定的文件執(zhí)行g(shù)db命令

由于gnu調(diào)試時忽略開始處斷點, 需要在開始標簽處執(zhí)行一個空指令
如:
.globl _start
_start:
nop
此時斷點可以設(shè)置成 break *_start+1
查看寄存器狀態(tài)info registers
使用print命令查看特定寄存器或者變量的值, 加上修飾符可以得到不同的輸出格式:
print/d 顯示十進制數(shù)字
print/t 顯示二進制數(shù)字
print/x 顯示16進制數(shù)字
使用x命令可以查看特定內(nèi)存的值:
x/nyz
其中 n為要顯示的字段數(shù)
y時輸出格式, 它可以是:
c 用于字符, d用于十進制, x用于16進制
z是要顯示的字段長度, 它可以是:
b用于字節(jié), h用于16字節(jié), w用于32位字
如:
x/42cb 用于顯示前42字節(jié)


################################################################################################
# 三, GNU匯編語言結(jié)構(gòu)
################################################################################################
主要包括三個常用的段:
data 數(shù)據(jù)段 聲明帶有初始值的元素
bss 數(shù)據(jù)段 聲明使用0或者null初始化的元素
text 正文段 包含的指令, 每個匯編程序都必須包含此段

使用.section 指令定義段, 如:
.section .data
.section .bss
.section .text

起始點:
gnu匯編器使用_start標簽表示默認的起始點, 此外如果想要匯編內(nèi)部的標簽?zāi)軌虮煌獠砍绦蛟L問,
需要使用.globl 指令, 如:.globl _start


使用通用庫函數(shù)時可以使用:
ld -dynamic-linker /lib/ld-linux.so.2




################################################################################################
# 四, 數(shù)據(jù)傳遞
################################################################################################
1, 數(shù)據(jù)段
使用.data聲明數(shù)據(jù)段, 這個段中聲明的任何數(shù)據(jù)元素都保留在內(nèi)存中并可以被匯編程序的指令讀取,
此外還可以使用.rodata聲明只讀的數(shù)據(jù)段, 在聲明一個數(shù)據(jù)元素時, 需要使用標簽和命令:

標簽:用做引用數(shù)據(jù)元素所使用的標記, 它和c語言的變量很相似, 它對于處理器是沒有意義的, 它
只是用做匯編器試圖訪問內(nèi)存位置時用做引用指針的一個位置。

指令:這個名字指示匯編器為通過標簽引用的數(shù)據(jù)元素保留特定數(shù)量的內(nèi)存, 聲明命令之后必須給出
一個或多個默認值。

聲明指令:
.ascii 文本字符串
.asciz 以空字符結(jié)尾的字符串
.byte 字節(jié)值
.double 雙精度浮點值
.float 單精度浮點值
.int 32位整數(shù)
.long 32位整數(shù), 和int相同
.octa 16字節(jié)整數(shù)
.quad 8字節(jié)整數(shù)
.short 16位整數(shù)
.single 單精度浮點數(shù)(和float相同)


例子:
output:
.ascii "hello world."

pi:
.float 2.14

聲明可以在一行中定義多個值, 如:
ages:
.int 20, 10, 30, 40


定義靜態(tài)符號:
使用.equ命令把常量值定義為可以在文本段中使用的符號,如:
.section .data
.equ LINUX_SYS_CALL, 0x80
.section .text
movl $LINUX_SYS_CALL, %eax



2, bss段
和data段不同, 無需聲明特定的數(shù)據(jù)類型, 只需聲明為所需目的保留的原始內(nèi)存部分即可。
GNU匯編器使用以下兩個命令聲明內(nèi)存區(qū)域:
.comm 聲明為未初始化的通用內(nèi)存區(qū)域
.lcomm 聲明為未初始化的本地內(nèi)存區(qū)域

兩種聲明很相似, 但.lcomm是為不會從本地匯編代碼之外進行訪問的數(shù)據(jù)保留的, 格式為:
.comm/.lcomm symbol, length

例子:
.section .bss
.lcomm buffer, 1000
該語句把1000字節(jié)的內(nèi)存地址賦予標簽buffer, 在聲明本地通用內(nèi)存區(qū)域的程序之外的函數(shù)是
不能訪問他們的.(不能在.globl命令中使用他們)


在bss段聲明的好處是, 數(shù)據(jù)不包含在可執(zhí)行文件中。在數(shù)據(jù)段中定義數(shù)據(jù)時, 它必須被包含在
可執(zhí)行程序中, 因為必須使用特定值初始化它。 因為不使用數(shù)據(jù)初始化bss段中聲明的數(shù)據(jù)區(qū)域,
所以內(nèi)存區(qū)域被保留在運行時使用, 并且不必包含在最終的程序中




3, 傳送數(shù)據(jù)
move 指令:
格式 movex 源操作數(shù), 目的操作數(shù)。 其中x為要傳送數(shù)據(jù)的長度, 取值有:
l 用于32位的長字節(jié)
w 用于16位的字
b 用于8位的字節(jié)值


立即數(shù)前面要加一個$符號, 寄存器前面要加%符號。

8個通用的寄存器是用于保存數(shù)據(jù)的最常用的寄存器, 這些寄存器的內(nèi)容可以傳遞
給其他的任何可用的寄存器。 和通用寄存器不同, 專用寄存器(控制, 調(diào)試, 段)
的內(nèi)容只能傳送給通用寄存器, 或者接收從通用寄存器傳過來的內(nèi)容。


在對標簽進行引用時:
例:
.section .data
value:
.int 100
_start:
movl value, %eax
movl $value, %eax
movl %ebx, (%edi)
movl %ebx, 4(%edi)

其中:movl value, %eax 只是把標簽value當前引用的內(nèi)存值傳遞給eax
movl $value, %eax 把標簽value當前引用的內(nèi)存地址指針傳遞給eax
movl %ebx, (%edi) 如果edi外面沒有括號那么這個指令只是把ebx中的
值加載到edi中, 如果有了括號就表示把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的取值為:
無符號數(shù):
a/nbe 大于/不小于或者等于
ae/nb 大于或者等于/不小于
nc 無進位
b/nae 小于/不大于等于
c 進位
be/na 小于或等于/不大于
e/z 等于/零
ne/nz 不等于/不為零
p/pe 奇偶校驗/偶校驗
np/po 非奇偶校驗/奇校驗

有符號數(shù):
ge/nl 大于或者等于/不小于
l/nge 小于/不大于或者等于
le/ng 小于或者等于/不大于
o 溢出
no 未溢出
s 帶符號(負)
ns 無符號(非負)





交換數(shù)據(jù):
xchg 在兩個寄存器之間或者寄存器和內(nèi)存間交換值
如:
xchg 操作數(shù), 操作數(shù), 要求兩個操作數(shù)必須長度相同且不能同時都是內(nèi)存位置
其中寄存器可以是32,16,8位的


bswap 反轉(zhuǎn)一個32位寄存器的字節(jié)順序

如: bswap %ebx


xadd 交換兩個值 并把兩個值只和存儲在目標操作數(shù)中

如: xadd 源操作數(shù),目標操作數(shù)
其中源操作數(shù)必須是寄存器, 目標操作數(shù)可以是內(nèi)存位置也可以是寄存器
其中寄存器可以是32,16,8位的

cmpxchg
cmpxchg source, destination
其中source必須是寄存器, destination可以是內(nèi)存或者寄存器, 用來比較
兩者的值, 如果相等,就把源操作數(shù)的值加載到目標操作數(shù)中, 如果不等就把
目標操作數(shù)加載到源操作數(shù)中,其中寄存器可以是32,16,8位的, 其中源操作
數(shù)是EAX,AX或者AL寄存器中的值


cmpxchg8b 同cmpxchg, 但是它處理8字節(jié)值, 同時它只有一個操作數(shù)
cmpxchg8b destination
其中destination引用一個內(nèi)存位置, 其中的8字節(jié)值會與EDX和EAX寄存器中
包含的值(EDX高位寄存器, EAX低位寄存器)進行比較, 如果目標值和EDX:EAX
對中的值相等, 就把EDX:EAX對中的64位值傳遞給內(nèi)存位置, 如果不匹配就把
內(nèi)存地址中的值加載到EDX:EAX對中



4, 堆棧
ESP 寄存器保存了當前堆棧的起始位置, 當一個數(shù)據(jù)壓入棧時, 它就會自動遞減,
反之其自動遞增

壓入堆棧操作:
pushx source, x取值為:
l 32位長字
w 16位字

彈出堆棧操作:
popx source
其中source必須是16或32位寄存器或者內(nèi)存位置, 當pop最后一個元素時ESP值應(yīng)該
和以前的相等


5,壓入和彈出所有寄存器
pusha/popa 壓入或者彈出所有16位通用寄存器
pushad/popad 壓入或者彈出所有32位通用寄存器
pushf/popf 壓入或者彈出EFLAGS寄存器的低16位
pushfd/popfd 壓入或者彈出EFLAGS寄存器的全部32位


6,數(shù)據(jù)地址對齊
gas 匯編器支持.align 命令, 它用于在特定的內(nèi)存邊界對準定義的數(shù)據(jù)元素, 在數(shù)據(jù)段
中.align命令緊貼在數(shù)據(jù)定義的前面




#######################################################
#########################################
# 五,控制流程
################################################################################################
無條件跳轉(zhuǎn):
1, 跳轉(zhuǎn)
jmp location 其中l(wèi)ocation為要跳轉(zhuǎn)到的內(nèi)存地址, 在匯編中為定義的標簽

2,調(diào)用
調(diào)用指令分為兩個部分:
1, 調(diào)用call address 跳轉(zhuǎn)到指定位置
2, 返回指令ret, 它沒有參數(shù)緊跟在call指令后面的位置

執(zhí)行call指令時,它把EIP的值放到堆棧中, 然后修改EIP以指向被調(diào)用的函數(shù)地址, 當被調(diào)用
函數(shù)完成后, 它從堆棧獲取過去的EIP的值, 并把控制權(quán)返還給原始程序。

3,中斷
由硬件設(shè)備生成中斷。 程序生成軟件中斷
當一個程序產(chǎn)生中斷調(diào)用時, 發(fā)出調(diào)用的程序暫停, 被調(diào)用的程序接替它運行, 指令指針被轉(zhuǎn)移到
被調(diào)用的函數(shù)地址, 當調(diào)用完成時使用中斷返回指令可以返回調(diào)原始程序。



條件跳轉(zhuǎn):
條件跳轉(zhuǎn)按照EFLAGS中的值來判斷是否該跳轉(zhuǎn), 格式為:

jxx address, 其中xx是1-3個字符的條件代碼, 取值如下:
a 大于時跳轉(zhuǎn)
ae 大于等于
b 小于
be 小于等于
c 進位
cxz 如果CX寄存器為0
ecxz 如果ECS寄存器為0
e 相等
na 不大于
nae 不大于或者等于
nb 不小于
nbe 不小于或等于
nc 無進位
ne 不等于
g 大于(有符號)
ge 大于等于(有符號)
l 小于(有符號)
le 小于等于(有符號)
ng 不大于(有符號)
nge 不大于等于(有符號)
nl 不小于
nle 不小于等于
no 不溢出
np 不奇偶校驗
ns 無符號
nz 非零
o 溢出
p 奇偶校驗
pe 如果偶校驗
po 如果奇校驗
s 如果帶符號
z 如果為零


條件跳轉(zhuǎn)不支持分段內(nèi)存模型下的遠跳轉(zhuǎn), 如果在該模式下進行
程序設(shè)計必須使用程序邏輯確定條件是否存在, 然后實現(xiàn)無條件
跳轉(zhuǎn), 跳轉(zhuǎn)前必須設(shè)置EFLAGS寄存器



比較:
cmp operend1, operend2


進位標志修改指令:
CLC 清空進位標志(設(shè)置為0)
CMC 對進位標志求反(把它改變?yōu)橄喾吹闹?
STC 設(shè)置進位標志(設(shè)置為1)


循環(huán):
loop 循環(huán)直到ECX寄存器為0
loope/loopz 循環(huán)直到ecx寄存器為0 或者沒有設(shè)置ZF標志
loopne/loopnz 循環(huán)直到ecx為0或者設(shè)置了ZF標志

指令格式為: loopxx address 注意循環(huán)指令只支持8位偏移地址



################################################################################################
# 六,數(shù)字
################################################################################################

IA-32平臺中存儲超過一字節(jié)的數(shù)都被存儲為小尾數(shù)的形式但是把數(shù)字傳遞給寄存器時, 寄存器里面保存是按照大尾數(shù)
的形式存儲


把無符號數(shù)轉(zhuǎn)換成位數(shù)更大的值時, 必須確保所有的高位部分都被設(shè)置為零

把有符號數(shù)轉(zhuǎn)換成位數(shù)更大的數(shù)時:
intel 提供了movsx指令它允許擴展帶符號數(shù)并保留符號, 它與movzx相似, 但是它假設(shè)要傳送的字節(jié)是帶符號數(shù)形式


浮點數(shù):
fld 指令用于把浮點數(shù)字傳送入和傳送出FPU寄存器, 格式:
fld source
其中source可以為32 64或者80位整數(shù)值


IA-32使用FLD指令用于把存儲在內(nèi)存中的單精度和雙精度浮點值FPU寄存器堆棧中, 為了區(qū)分這兩種長度GNU匯編器使用
FLDS加載單精度浮點數(shù), FLDL加載雙精度浮點數(shù)

類似FST用于獲取FPU寄存器堆棧中頂部的值, 并且把這個值放到內(nèi)存位置中, 對于單精度使用FSTS, 對于雙精度使用FSTL



################################################################################################
# 七,基本數(shù)學(xué)運算
################################################################################################
1, 加法
ADD source, destination 把兩個整數(shù)相加
其中source可以是立即數(shù)內(nèi)存或者寄存器, destination可以是內(nèi)存或者寄存器, 但是兩者不能同時都是內(nèi)存位置


ADC 和ADD相似進行加法運算, 但是它把前一個ADD指令的產(chǎn)生進位標志的值包含在其中, 在處理位數(shù)大于32(如64)
位的整數(shù)時, 該指令非常有用

2, 減法
SUB source, destination 把兩個整數(shù)相減
NEG 它生成值的補碼
SBB 指令, 和加法操作一樣, 可以使用進位情況幫助執(zhí)行大的無符號數(shù)值的減法運算. SBB在多字節(jié)減法操作中利用
進位和溢出標志實現(xiàn)跨數(shù)據(jù)邊界的的借位特性

3,遞增和遞減
dec destination 遞減
inc destination 遞增

其中dec和inc指令都不會影響進位標志, 所以遞增或遞減計數(shù)器的值都不會影響程序中涉及進位標志的其他任何運算

4, 乘法
mul source 進行無符號數(shù)相乘
它使用隱含的目標操作數(shù), 目標位置總是使用eax的某種形式, 這取決與源操作數(shù)的長度, 因此根據(jù)源操作數(shù)的長度,
目標操作數(shù)必須放在AL, AX, EAX中。 此外由于乘法可能產(chǎn)生很大的值, 目標位置必須是源操作數(shù)的兩倍位置, 源為
8時, 應(yīng)該是16, 源為16時, 應(yīng)該為32, 但是當源為16位時intel為了向下兼容, 目標操作數(shù)不是存放在eax中, 而
是分別存放在DX:AX中, 結(jié)果高位存儲在DX中, 地位存儲在AX中。對于32位的源, 目標操作數(shù)存儲在EDX:EAX中, 其中
EDX存儲的是高32位, EAX存儲的是低32位

imul source 進行有符號數(shù)乘法運算, 其中的目標操作數(shù)和mul的一樣

imul source, destination 也可以執(zhí)行有符號乘法運算, 但是此時可以把目標放在指定的位置, 使用這種格式的缺陷
在與乘法的操作結(jié)果被限制為單一目標寄存器的長度.

imul multiplier, source, destination
其中multiplier是一個立即數(shù), 這種方式允許一個值與給定的源操作數(shù)進行快速的乘法運算, 然后把結(jié)果存儲在通用
寄存器中


5, 除法
div divisor 執(zhí)行無符號數(shù)除法運算
除數(shù)的最大值取決與被除數(shù)的長度, 對于16位被除數(shù) ,除數(shù)只能為8位, 32或64位同上
被除數(shù) 被除數(shù)長度 商 余數(shù)
AX 16位 AL AH
DX:AX 32位 AX DX
EDX:EAX 64位 EAX EDX

idiv divisor 執(zhí)行有符號數(shù)的除法運算, 方式和div一樣

6, 移位
左移位:
sal 向左移位
sal destination 把destination向左移動1位
sal %cl, destination 把destination的值向左移動CL寄存器中指定的位數(shù)
sal shifter, destination 把destination的值向左移動shifter值指定的位數(shù)
向左移位可以對帶符號數(shù)和無符號數(shù)執(zhí)行向左移位的操作, 移位造成的空位用零填充, 移位造成的超過數(shù)據(jù)長度的任何位
都被存放在進位標志中, 然后在下一次移位操作中被丟棄

右移位:
shr向右移位
sar向右移位
SHR指令清空移位造成的空位, 所以它只能對無符號數(shù)進行移位操作
SAR指令根據(jù)整數(shù)的符號位, 要么清空, 要么設(shè)置移位造成的空位, 對于負數(shù), 空位被設(shè)置為1

循環(huán)移位:
和移位指令類似, 只不過溢出的位被存放回值的另一端, 而不是丟棄
ROL 向左循環(huán)移位
ROR 向右循環(huán)移位
RCL 向左循環(huán)移位, 并且包含進位標志
RCR 向右循環(huán)移位, 并且包含進位標志

7, 邏輯運算
AND OR XOR
這些指令使用相同的格式:
and source, destination
其中source可以是8位 16 位或者32位的立即值 寄存器或內(nèi)存中的值, destination可以是8位 16 位或者
32位寄存器或內(nèi)存中的值, 不能同時使用內(nèi)存值作為源和目標。 布爾邏輯功能對源和目標執(zhí)行按位操作。
也就是說使用指定的邏輯功能按照順序?qū)?shù)據(jù)的元素的每個位進行單獨比較。

NOT指令使用單一操作數(shù), 它即是源值也是目標結(jié)果的位置
清空寄存器的最高效方式是使用OR指令對寄存器和它本身進行異或操作.當和本身進行XOR操作時, 每個設(shè)置為
1的位就變?yōu)?, 每個設(shè)置為0的位也變位0。

位測試可以使用以上的邏輯運算指令, 但這些指令會修改destination的值, 因此intel提供了test指令, 它不
會修改目標值而是設(shè)置相應(yīng)的標志



################################################################################################
# 八,字符串處理
################################################################################################
1, 傳送字符串
movs 有三種格式
movsb 傳送單一字節(jié)
movsw 傳送一個字
movsl 傳送雙字

movs指令使用隱含的源和目的操作數(shù), 隱含的源操作數(shù)是ESI, 隱含的目的操作數(shù)是EDI, 有兩種方式加載內(nèi)存地址到
ESI和EDI, 第一種是使用標簽間接尋址 movl $output, %ESI, 第二種是使用lea指令, lea指令加載對象的地址到指定
的目的操作數(shù)如lea output, %esi, 每次執(zhí)行movs指令后, 數(shù)據(jù)傳送后ESI和EDI寄存器會自動改變,為另一次傳送做
準備, ESI和EDI可能隨著標志DF的不同自動遞增或者自動遞減, 如果DF標志為0則movs指令后ESI和EDI會遞增, 反之會
遞減, 為了設(shè)置DF標志, 可以使用一下指令:
CLD 將DF標志清零
STD 設(shè)置DF標志

2,rep前綴
REP 指令的特殊之處在與它不執(zhí)行什么操作, 這條指令用于按照特定次數(shù)重復(fù)執(zhí)行字符串指令, 有ECX寄存器控制,
但不需要額外的loop指令, 如rep movsl

rep的其他格式:
repe 等于時重復(fù)
repne 不等于時重復(fù)
repnz 不為零時重復(fù)
repz 為零時重復(fù)

3, 存儲和加載字符串
LODS 加載字符串, ESI為源, 當一次執(zhí)行完lods時會遞增或遞減ESI寄存器, 然后把字符串值存放到EAX中

STOS 使用lods把字符串值加載到EAX后, 可以使用它把EAX中的值存儲到內(nèi)存中去:
stos使用EDI作為目的操作數(shù), 執(zhí)行stos指令后, 會根據(jù)DF的值自動遞增或者遞減EDI中的值

4, 比較字符串
cmps 和其他的操作字符串的指令一樣, 隱含的源和目標操作數(shù)都為ESI和EDI, 每次執(zhí)行時都會根據(jù)DF的值把
ESI和EDI遞增或者遞減, cmps指令從目標字符串中減去源字符串, 執(zhí)行后會設(shè)置EFLAGS寄存器的狀態(tài).


5,掃描字符串
scas 把EDI作為目標, 它把EDI中的字符串和EAX中的字符串進行比較 ,然后根據(jù)DF的值遞增或者遞減EDI



################################################################################################
# 九,使用函數(shù)
################################################################################################
GNU匯編語言定義函數(shù)的語法:
.type 標簽(也就是函數(shù)名), @function
ret 返回到調(diào)用處



################################################################################################
# 十,linux系統(tǒng)調(diào)用
################################################################################################
linux系統(tǒng)調(diào)用的中斷向量為0x80

1, 系統(tǒng)調(diào)用標識存放在%eax中
2, 系統(tǒng)調(diào)用輸入值:
EBX 第一個參數(shù)
ECX 第二個參數(shù)
EDX 第三個參數(shù)
ESI 第四個參數(shù)
EDI 第五個參數(shù)

需要輸入超過6個輸入?yún)?shù)的系統(tǒng)調(diào)用, EBX指針用于保存指向輸入?yún)?shù)內(nèi)存位置的指針, 輸入?yún)?shù)按照連續(xù)的的順序
存儲, 系統(tǒng)調(diào)用的返回值存放在EAX中

################################################################################################
# 十一,匯編語言的高級功能
################################################################################################
1,gnu內(nèi)聯(lián)匯編的語法:
asm或__asm__("匯編代碼");
指令必須包含在引號里
如果包含的指令超過一行 必須使用新行分隔符分隔


使用c全局變量, 不能在內(nèi)聯(lián)匯編中使用局部變量, 注意在匯編語言代碼中值被用做內(nèi)存位置, 而不是立即數(shù)值

如果不希望優(yōu)化內(nèi)聯(lián)匯編, 則可以volatile修飾符如:__asm__ volatile("code");

2,GCC內(nèi)聯(lián)匯編的擴展語法
__asm__("assembly code":output locations:input operands:changed registers);
第一部分是匯編代碼
第二部分是輸出位置, 包含內(nèi)聯(lián)匯編代碼的輸出值的寄存器和內(nèi)存位置列表
第三部分是輸入操作數(shù),包含內(nèi)聯(lián)匯編代碼輸入值的寄存器和內(nèi)存位置的列表
第四部分是改動的寄存器, 內(nèi)聯(lián)匯編改變的任何其他寄存器的列表
這幾個部分可以不全有, 但是沒有的還必須使用:分隔

1, 指定輸入值和輸出值, 輸入值和輸出值的列表格式為:
"constraint"(variable), 其中variable是程序中聲明的c變量, 在擴展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 對于64位值使用%eax, %edx寄存器
f 使用浮點寄存器
t 使用第一個(頂部)的浮點寄存器
u 使用第二個浮點寄存器
m 使用變量的內(nèi)存位置
o 使用偏移內(nèi)存位置
V 只使用直接內(nèi)存位置
i 使用立即整數(shù)值
n 使用值已知的立即整數(shù)值
g 使用任何可用的寄存器和內(nèi)存位置


除了這些約束之外, 輸出值還包含一個約束修飾符:
輸出修飾符 描述
+ 可以讀取和寫入操作數(shù)
= 只能寫入操作數(shù)
% 如果有必要操作數(shù)可以和下一個操作數(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


在擴展的asm語句塊中如果要使用寄存器必須使用兩個百分號符號


不一定總要在內(nèi)聯(lián)匯編代碼中指定輸出值, 一些匯編指令假定輸入值包含輸出值, 如movs指令


其他擴展內(nèi)聯(lián)匯編知識:
1, 使用占位符
輸入值存放在內(nèi)聯(lián)匯編段中聲明的特定寄存器中, 并且在匯編指令中專門使用這些寄存器.
雖然這種方式能夠很好的處理只有幾個輸入值的情況, 但對于需要很多輸入值的情況, 這
中方式顯的有點繁瑣. 為了幫助解決這個問題, 擴展asm格式提供了占位符, 可以在內(nèi)聯(lián)
匯編代碼中使用它引用輸入和輸出值.

占位符是前面加上百分號的數(shù)字, 按照內(nèi)聯(lián)匯編中列出的每個輸入和輸出值在列表中的位置,
每個值被賦予從0開始的地方. 然后就可以在匯編代碼中引用占位符來表示值。


如果內(nèi)聯(lián)匯編代碼中的輸入和輸出值共享程序中相同的c變量, 則可以指定使用占位符作為
約束值, 如:
__asm__("imull %1, %0"
: "=r"(data2)
: "r"(data1), "0"(data2));
如輸入輸出值中共享相同的變量data2, 而在輸入變量中則可以使用標記0作為輸入?yún)?shù)的約束

2, 替換占位符
如果處理很多輸入和輸出值, 數(shù)字型的占位符很快就會變的很混亂, 為了使條理清晰 ,GNU匯編
器(從版本3.1開始)允許聲明替換的名稱作為占位符.替換的名稱在聲明輸入值和輸出值的段中
定義, 格式如下:
%[name]"constraint"(variable)
定義的值name成為內(nèi)聯(lián)匯編代碼中變量的新的占位符號標識, 如下面的例子:
__asm__("imull %[value1], %[value2]"
: [value2] "=r"(data2)
: [value1] "r"(data1), "0"(data2));

3, 改動寄存器列表
編譯器假設(shè)輸入值和輸出值使用的寄存器會被改動, 并且相應(yīng)的作出處理。程序員不需要在改動的
寄存器列表中包含這些值, 如果這樣做了, 就會產(chǎn)生錯誤消息. 注意改動的寄存器列表中的寄存器
使用完整的寄存器名稱, 而不像輸入和輸出寄存器定義的那樣僅僅是單一字母。 在寄存器名稱前面
使用百分號符號是可選的。

改動寄存器列表的正確使用方法是, 如果內(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)存位置。 記住, 對于要求使用寄存器的匯編指令, 仍然必須使用寄存器, 所以不得不定義
保存數(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)匯編語言代碼也可以包含定義其中位置的標簽。 可以實現(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)匯編代碼中使用標簽時有兩個限制。 第一個限制是只能跳轉(zhuǎn)到相同的asm段內(nèi)的標簽,
不能從-個asm段跳轉(zhuǎn)到另一個asm段中的標簽。第二個限制更加復(fù)雜一點。 以上程序使用
標簽greater和end。 但是, 這樣有個潛在的問題, 查看匯編后的代碼清單, 可以發(fā)現(xiàn)內(nèi)聯(lián)
匯編標簽也被編碼到了最終匯編后的代碼中。 這意味著如果在c代碼中還有另一個asm段, 就
不能再次使用相同的標簽, 否則會因為標簽重復(fù)使用而導(dǎo)致錯誤消息。還有如果試圖整合使用
c關(guān)鍵字(比如函數(shù)名稱或者全局變量)的標簽也會導(dǎo)致錯誤。



################################################################################################
# 十二,優(yōu)化你的代碼
################################################################################################
GNU編譯器提供-O選項供程序優(yōu)化使用:
-O 提供基礎(chǔ)級別的優(yōu)化
-O2 提供更加高級的代碼優(yōu)化
-O3 提供最高級的代碼優(yōu)化
不同的優(yōu)化級別使用的優(yōu)化技術(shù)也可以單獨的應(yīng)用于代碼。 可以使用-f命令行選項引用每個
單獨的優(yōu)化技術(shù)。

1, 編譯器優(yōu)化級別1
在優(yōu)化的第一個級別執(zhí)行基礎(chǔ)代碼的優(yōu)化。 這個級別試圖執(zhí)行9種單獨的優(yōu)化功能:
-fdefer-pop: 這種優(yōu)化技術(shù)與匯編語言代碼在函數(shù)完成時如何進行操作有關(guān)。 一般
情況下, 函數(shù)的輸入值被保存在堆棧種并且被函數(shù)訪問。 函數(shù)返回時, 輸入值還在
堆棧種。 一般情況下, 函數(shù)返回之后, 輸入值被立即彈出堆棧。這樣做會使堆棧種
的內(nèi)容有些雜亂。

-fmerge-constans: 使用這種優(yōu)化技術(shù), 編譯器試圖合并相同的常量. 這一特性有
時候會導(dǎo)致很長的編譯時間, 因為編譯器必須分析c或者c++程序中用到的每個常量,
并且相互比較他們.

-fthread-jumps: 使用這種優(yōu)化技術(shù)與編譯器如果處理匯編代碼中的條件和非條件
分支有關(guān)。 在某些情況下, 一條跳轉(zhuǎn)指令可能轉(zhuǎn)移到另一條分支語句。 通過一連串
跳轉(zhuǎn), 編譯器確定多個跳轉(zhuǎn)之間的最終目標并且把第一個跳轉(zhuǎn)重新定向到最終目標。

-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)化那些確定何時離開循環(huán)的條件分支, 以便減少分支的影響。

-fif-conversion: if-then語句應(yīng)該是應(yīng)用程序中僅次于循環(huán)的最消耗時間的部分。
簡單的if-then語句可能在最終的匯編語言代碼中產(chǎn)生眾多的條件分支。 通過減少
或者刪除條件分支, 以及使用條件傳送 設(shè)置標志和使用運算技巧來替換他們, 編譯
器可以減少if-then語句中花費的時間量。

-fif-conversion2: 這種技術(shù)結(jié)合更加高級的數(shù)學(xué)特性, 減少實現(xiàn)if-then語句所
需的條件分支。

-fdelayed-branch: 這種技術(shù)試圖根據(jù)指令周期時間重新安排指令。 它還試圖把
盡可能多的指令移動到條件分支前, 以便最充分的利用處理器的治理緩存。

-fguess-branch-probability: 就像其名稱所暗示的, 這種技術(shù)試圖確定條件分支最可
能的結(jié)果, 并且相應(yīng)的移動指令, 這和延遲分支技術(shù)類似。 因為在編譯時預(yù)測代碼的安排,
所以使用這一選項兩次編譯相同的c或者c++代碼很可能會產(chǎn)生不同的匯編語言代碼, 這取決
于編譯時編譯器認為會使用那些分支。 因為這個原因, 很多程序員不喜歡采用這個特性, 并且
專門地使用-fno-guess-branch-probability選項關(guān)閉這個特性

-fcprop-registers: 因為在函數(shù)中把寄存器分配給變量, 所以編譯器執(zhí)行第二次檢查以便減少
調(diào)度依賴性(兩個段要求使用相同的寄存器)并且刪除不必要的寄存器復(fù)制操作。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    狠狠亚洲丁香综合久久| 欧美一级黄片免费视频| 国产在线观看不卡一区二区| 精品熟女少妇一区二区三区| 自拍偷拍一区二区三区| 日本一区二区三区黄色| 国产精品偷拍一区二区| 欧美日韩国产午夜福利| 国产一级不卡视频在线观看| 中文字幕精品人妻一区| 国产午夜精品久久福利| 国产小青蛙全集免费看| 日韩中文字幕人妻精品| 毛片在线观看免费日韩| 中文字字幕在线中文乱码二区| 精品熟女少妇av免费久久野外| 最新午夜福利视频偷拍| 日本免费一本一二区三区| 亚洲精品欧美精品日韩精品| 国产高清精品福利私拍| 国产精品香蕉一级免费| 视频一区二区黄色线观看| 男女午夜在线免费观看视频| 精品亚洲香蕉久久综合网| 日韩在线精品视频观看| 亚洲在线观看福利视频| 国产精品一区二区视频| 91精品国产综合久久福利| 国产精品蜜桃久久一区二区| 日韩成人动画在线观看| 亚洲国产成人久久99精品| 久久老熟女一区二区三区福利| 亚洲精品黄色片中文字幕| 麻豆91成人国产在线观看| 亚洲国产成人爱av在线播放下载| 国产色第一区不卡高清| 欧美一区二区在线日韩| 国产精品十八禁亚洲黄污免费观看| 老司机激情五月天在线不卡 | 国产精品国产亚洲看不卡| 久久精品偷拍视频观看|