AT&T匯編


轉自:http://n3719e7533.blog.163.com/blog/static/18943933420111132184813

 

 AT&T 匯編 

  1.Register Reference 

  引用寄存器要在寄存器號前加百分號%,如“movl %eax, %ebx”。 

   有如下寄存器: 

  [1] 8 個 32-bit 寄存器 %eax,%ebx,%ecx,%edx,%edi,%esi,%ebp,%esp; 

  ( 8 個 16-bit 寄存器,它們事實上是上面 8 個 32-bit 寄存器的低 16 位:%ax,%bx, 

  %cx,%dx,%di,%si,%bp,%sp; 

  8 個 8-bit 寄存器:%ah,%al,%bh,%bl,%ch,%cl,%dh,%dl。它們事實上 

  是寄存器%ax,%bx,%cx,%dx 的高 8 位和低 8 位;) 

  [2] 6 個段寄存器:%cs(code),%ds(data),%ss(stack), %es,%fs,%gs; 

  [3] 3 個控制寄存器:%cr0,%cr2,%cr3; 

  [4] 6 個 debug 寄存器:%db0,%db1,%db2,%db3,%db6,%db7; 

  [5] 2 個測試寄存器:%tr6,%tr7; 

  [6] 8 個浮點寄存器棧:%st(0),%st(1),%st(2),%st(3),%st(4),%st(5),%st(6), %st(7)。 

  2. Operator Sequence 

  操作數排列是從源(左)到目的(右),如“movl %eax(源), %ebx(目的)” 

  3. Immediately Operator 

  使用立即數,要在數前面加符號$, 如“movl $0x04, %ebx” 

  或者: 

  para = 0x04 

  movl $para, %ebx 

  指令執行的結果是將立即數 0x04 裝入寄存器 ebx。 

  4. Symbol Constant 

  符號常數直接引用如 

  value: .long 0x12a3f2de 

  movl value , %ebx 

  指令執行的結果是將常數 0x12a3f2de 裝入寄存器 ebx。 

  引用符號地址在符號前加符號$, 如“movl $value, % ebx”則是將符號 value 的地址裝入寄存器 ebx。 

  5. Length of Operator 

  操作數的長度用加在指令后的符號表示 b(byte, 8-bit), w(word, 16-bits), l(long,32-bits) ,如“movb %al, %bl” ,“movw 

  %ax, %bx”,“movl %eax, %ebx ”。 

  如 果沒有指定操作數長度的話,編譯器將按照目標操作數的長度來設置。比如指令“mov %ax, %bx”,由於目標操作數 bx 的長度為 

  word , 那么編譯器將把此指令等同於“ movw %ax,%bx”。同樣道理 , 指令“ mov $4, %ebx”等同於指令“ movl $4, 

  %ebx”,“push %al”等同於“pushb %al”。對於沒有指定操作數長度,但編譯器又無法猜測的指令,編譯器將會報錯,比如指令 

  “push $4”。 

  6. Sign and Zero Extension 

  絕大多數面向 的 AT&T 匯編指令與 Intel 格式的匯編指令都是相同的,但符號擴展指令和零擴展指令有不同格式。符號擴展指令 

  和零擴展指令需要指定源操作數長度和目的操作數長度,即使在某些指令中這些操作數是隱含的。 

  在 AT&T 語法中,符號擴展和零擴展指令的格式為,基本部分"movs"和"movz"(對應 Intel 語法的 movsx 和 movzx),后面跟 

  上源操作數長度和目的操作數長度。 movsbl 意味着 movs (from)byte (to)long;movbw 意味着 movs (from)byte 

  (to)word;movswl 意味着 movs (from)word (to)long。對於 movz 指令也一樣。比如指令“movsbl %al,%edx”意味着將 

  al 寄存器的內容進行符號擴展后放置到 edx 寄存器中。 

  其它的 Intel 格式的符號擴展指令還有: 

  cbw -- sign-extend byte in %al to word in %ax; 

  cwde -- sign-extend word in %ax to long in %eax; 

  cwd -- sign-extend word in %ax to long in %dx:%ax; 

  cdq -- sign-extend dword in %eax to quad in %edx:%eax; 

  對應的 AT&T 語法的指令為 cbtw,cwtl,cwtd,cltd。 

  7. Call and Jump 

  段內調用和跳轉指令為 "call" , "ret" 和 "jmp",段間調用和跳轉指令為 "lcall" , "lret" 和 "ljmp" 。段間調用和跳轉指令的格式為 

  “lcall/ljmp $SECTION, $OFFSET”,而段間返回指令則為“lret $STACK-ADJUST”。 

  8. Prefix 

  操作碼前綴被用在下列的情況: 

  [1]字符串重復操作指令(rep,repne); 

  [2]指定被操作的段(cs,ds,ss,es,fs,gs); 

  [3]進行總線加鎖(lock); 

  [4]指定地址和操作的大小(data16,addr16); 

  在 AT&T 匯編語法中,操作碼前綴通常被單獨放在一行,后面不跟任何操作數。例如,對於重復 scas 指令,其寫法為: 

  repne 

  scas 

  上述操作碼前綴的意義和用法如下: 

  [1]指定被操作的段前綴為 cs,ds,ss,es,fs,和 gs。在 AT&T 語法中,只需要按照 

  section:memory-operand 的格式就指定了相應的段前綴。比如: 

  lcall %cs:realmode_swtch 

  [2]操作數/地址大小前綴是“data16”和"addr16",它們被用來在 32-bit 操作數/地址代碼中指定 16-bit 的操作數/地址。 

  [3]總線加鎖前綴“lock”,它是為了在多處理器環境中,保證在當前指令執行期間禁止一切中斷。這個前綴僅僅對 ADD, ADC, AND, 

  BTC, BTR, BTS, CMPXCHG,DEC, 

  INC, NEG, NOT, OR, SBB, SUB, XOR, XADD,XCHG 指令有效,如果將 Lock 前 

  綴用在其它指令之前,將會引起異常。 

  [4]字符串重復操作前綴"rep","repe","repne"用來讓字符串操作重復“%ecx”次。 

  9. Memory Reference 

  Intel 語法的間接內存引用的格式為: 

  section:[base+index*scale+displacement] 

  而在 AT&T 語法中對應的形式為: 

  section:displacement(base,index,scale) 

  其中,base 和 index 是任意的 32-bit base 和 index 寄存器。scale 可以取值 1,2,4,8。如果不指定 scale 值,則默認值為 1。 

  section 可以指定任意的段寄存器作為段前綴,默認的段寄存器在不同的情況下不一樣。如果在指令中指定了默認的段前綴,則編譯器在 

  目標代碼中不會產生此段前綴代碼。 

  下面是一些例子: 

  -4(%ebp):base=%ebp,displacement=-4,section 沒有指定,由於 base=%ebp,所以默認的 section=%ss,index,scale 

  沒有指定,則 index 為 0。 

  foo(,%eax,4):index=%eax,scale=4,displacement=foo。其它域沒有指定。這里默認的 section=%ds。 

  foo(,1):這個表達式引用的是指針 foo 指向的地址所存放的值。注意這個表達式中沒有 base 和 index,並且只有一個逗號,這是一種 

  異常語法,但卻合法。 

  %gs:foo:這個表達式引用的是放置於%gs 段里變量 foo 的值。 

  如果 call 和 jump 操作在操作數前指定前綴“*”,則表示是一個絕對地址調用/跳轉,也就是說 jmp/call 指令指定的是一個絕對地址。 

  如果沒有指定"*",則操作數是一個相對地址。 

  任何指令如果其操作數是一個內存操作, 則指令必須指定它的操作尺寸 

  (byte,word,long),也就是說必須帶有指令后綴(b,w,l)。 

  Linux 工作在保護模式下,用的是 32 位線性地址,所以在計算地址時不用考慮段基址和偏移量,而是采用如下的地 

  址計算方法: 

  disp + base + index * scale 

  下面是一些內存操作數的例子: 

  AT&T 格式 

  movl -4(%ebp), %eax 

  movl array(, %eax, 4), %eax 

  movw array(%ebx, %eax, 4), %cx 

  movb $4, %fs:(%eax) 

  其中下面這些省略了浮點數及 IA-32 如 SSE FPU 等特殊的指令集部分, 我覺得重要的是學習 

  linux 匯編的語法及編譯原理和程序控制流程, 具體的指令細節就不那么重要了。 

  ################################################## ######################### 

  ##################### 

  # 一, IA-32 硬件特性 

  ################################################## ######################### 

  ##################### 

  寄存器: 

  1, 通用寄存器, 用於存放正在處理的數據 

  EAX 用於操作數和結果數的累加器 

  EBX 指向數據內存斷中的數據的指針 

  ECX 字符串和循環操作的計數器 

  EDX IO 指針 

  EDI 用於字符串操作的目標的數據指針 

  ESI 用於字符串操作的源的數據指針 

  ESP 堆棧指針 

  EBP 堆棧數據指針 

  其中寄存器 EAX, EBX, ECX, EDX 又可以通過 16 位和 8 位寄存器名稱引用如 EAX, AX 引用 EAX 低 16 位, AL 引用 EAX 低 8 位, AH 引用 

  AL 之后的高 8 位 

  2, 段寄存器: 

  IA-32 平台允許使用 3 中內存模型: 平坦內存模式 分段內存模式 實地址模式 

  平坦內存: 把全部的系統內存表示為連續的地址空間, 通過線性地址的特定地址訪問內存位置. 

  分段內存: 把系統內存划分為獨立的段組, 通過位於寄存器中的指針進行引用. 每個段用於包含特定類型的數據。 一個段用於包含指令碼,另 

  一個段包含數據元素, 第三個段包含數據堆棧。 

  段中的內存位置是通過邏輯地址引用的, 邏輯地址是由段地址加上偏移量構成, 處理器把邏輯地址轉換為相應的線性地址以便訪問。 

  段寄存器: 

  CS 代碼段 

  DS 數據段 

  SS 堆棧段 

  ES 附加段指針 

  FS 附加段指針 

  GS 附加段指針 

  每個段寄存器都是 16 位的, 包含指向內存特定段起始位置的指針,程序不能顯示加載或改變 CS 寄存器, DS, ES, FS, GS 都用於指向數據 

  段, 通過 4 個獨立的段, 程序可以分隔數據元素, 確保他們不會重疊, 程序必須加載帶有段的正確指針值的數據段寄存器, 並且使用偏移 

  值引用各個內存的位置。 

  SS 段寄存器用於指向堆棧段, 堆棧包含傳遞給函數和過程的數據值。 

  實地址: 如果實地址模式, 所有段寄存器都指向線性 0 地址, 並且都不會被程序改動, 所有的指令碼 數據元素 堆棧元素 都是通過他們的 

  線性地址直接訪問的。 

  3, 指令指針寄存器 

  是 EIP 寄存器, 它跟蹤要執行程序的下一條指令代碼, 應用程序不能修改指令指針本身,不能指定內存地址把它拖放 EIP 寄存器中,相反必須 

  通過一般的跳轉指令來改變預存取緩存的下一條指令。 

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

  4, 控制寄存器 

  CRO 控制操作模式和 處理器當前狀態的系統標志 

  CR1 當前沒有使用 

  CR2 內存頁面錯誤信息 

  CR3 內存頁面目錄信息 

  CR4 支持處理器特性和說明處理器特性能力的標志 

  不能直接訪問控制寄存器, 但是能把控制寄存器中的值傳遞給通用寄存器,如果必須改動控制寄存器的標志, 可以改動通用寄存器的值,然 

  后把內容傳遞給控制寄存器。 

  標志: 

  IA-32 使用單一的寄存器來包含一組狀態控制和系統標志, EFLAGS 寄存器包含 32 位標志信息 

  1, 狀態標志 

  標志位 說明 

  CF 0 進位標志, 如果無符號數的數學操作產生最高有效位的進位或者借位, 此時值為 1 

  PF 2 奇偶校驗標志, 用於表明數學操作的結果寄存器中的是否包含錯誤數據 

  AF 4 輔助進位標志, 用於二進制編碼的 10 進制(BCD)的數學操作中, 如果用於運算的 

  寄存器的第三位發生進位或借位, 該值為 1 

  ZF 6 0 標志, 如果操作為 0, 則該值為 1 

  SF 7 符號標志, 設置為結果的最高有效位, 這一位是符號位表明結果是正值還是負值 

  OF 11 溢出標志 

  2, 控制標志 

  當前只定義了一個控制標志 DF 即方向標志, 用於控制處理器處理字符串的方式如果設置為 1, 字符串指令自動遞減內存地址以便到達字符串 

  中的下一字節。 

  反之。 

  3, 系統標志 

  標志位 說明 

  TF 8 陷阱標志, 設置為 1 時啟用單步模式, 在單步模式下處理器每次只執行一條命令。 

  IF 9 中斷使能標志, 控制處理器如響應從外部源接收到的信號。 

  IOPL 12 和 13 IO 特權級別標志, 表明當前正在運行任務的 IO 特權級別, 它定義 IO 地址空間的特權訪問級別, 該值必須小於或者等於訪問 

  I/O 地址空間的級別; 否則任何訪問 IO 空間的請求都會被拒絕! 

  NT 14 嵌套任務標志控制當前運行的任務是否連接到前一個任務, 它用於連接被中斷和被調用的任務. 

  RF 16 恢復標志用於控制在調試模式中如何響應異常。 

  VM 17 虛擬 8086 模式, 表明處理器在虛擬 8086 模式中而不是保護模式或者實模式。 

  AC 18 對准檢查標志, 用於啟用內存引用的對准檢查 

  VIF 19 虛擬中斷標志, 當處理器在虛擬模式中操作時, 該標志起 IF 標志的作用. 

  VIP 20 虛擬中斷掛起標志, 在虛擬模式操作時用於表示一個中斷正在被掛起。 

  ID 21 表示 CPU 是否支持 cpuid 指令, 如果處理器能夠設置或者清零這個標志, 表示處理器支持該指令。 

  ################################################## ######################### 

  ##################### 

  # 二,GNU 匯編工具系列 

  ################################################## ######################### 

  ##################### 

  1, 二進制工具系列 

  addr2line 把地址轉換成文件名或者行號 

  ar 創建 修改或者展開文件存檔 

  as 把匯編語言代碼匯編成目標代碼 

  常用選項: 

  -a -> 指定輸出中包含那些清單 

  -D -> 包含它用於向下兼容 但是被忽略 

  --defsym -> 在匯編代碼之前定義符號和值 

  -f -> 快速匯編跳過注釋和空白 

  --gstabs -> 包含每行源代碼的調試信息 

  --gstats+ -> 包含 gdb 專門的調試信息 

  -I -> 指定包含文件的目錄 

  -J -> 不警告帶符號溢出 

  -L -> 在符號表中保存本地符號 

  -o -> 給定輸出目標名 

  -R -> 把數據段合並進文本段 

  --statistics -> 顯示匯編使用的最大空間和總時間 

  -v -> 顯示 as 的版本號 

  -W -> 不顯示警告信息 

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

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

  ld 把目標代碼文件轉換成可執行文件的轉換器 

  常用選項: 

  -d -> 指定目標代碼輸入文件的格式 

  -Bstatic -> 只使用靜態庫 

  -Bdynamic -> 只使用動態庫 

  -Bsymbolic-> 把引用捆綁到共享庫中的全局符號 

  -c -> 從指定的命令文件讀取命令 

  -cref -> 創建跨引用表 

  -defsym -> 在輸出文件中創建指定的全局符號 

  -demangle -> 在錯誤消息中還原符號名稱 

  -e -> 使用指定的符號作為程序的初始執行點 

  -E -> 對於 elf 文件把所有的符號添加到動態符號表 

  -share -> 創建共享庫 

  -Ttext -> 使用指定的地址作為文本段的起始點 

  -Tdata -> 使用指定的地址作為數據段的起始點 

  -Tbss -> 使用指定的地址作為 bss 段的起始點 

  -L -> 把指定的路徑添加到庫搜索清單 

  -O -> 生成優化的輸出文件 

  -o -> 指定輸出名 

  -oformat -> 指定輸出文件的二進制格式 

  -R -> 從指定的文件讀取符號和地址 

  -rpath -> 把指定的位置添加到運行時庫搜索路徑 

  -rpath-link-> 指定搜索運行時共享庫的路徑 

  -X -> 刪除本地所有臨時符號 

  -x -> 刪除本地所有符號 

  nm 列出目標文件中的符號 

  objcopy 復制或翻譯目標文件 

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

  ranlib 生成存檔文件內容的索引 

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

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

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

  strip 丟棄符號 

  windres 編譯 Microsoft Windows 資源文件 

  2, GNU 編譯器 

  gcc 

  常用選項: 

  -c 編譯或者匯編代碼但不進行連接 

  -S 編譯后停止但不進行匯編 

  -E 預處理后停止但不進行編譯 

  -o 指定輸出文件名 

  -v 顯示每個編譯階段使用的命令 

  -std 指定使用的語言標准 

  -g 生成調試信息 

  -pg 生成 gprof 制作簡檔要使用的額外代碼 

  -O 優化可執行代碼 

  -W 設置編譯器警告級別 

  -I 指定包含文件清單 

  -L 指定庫文件目錄 

  -D 預定義源代碼中使用的宏 

  -U 取消任何定義了的宏 

  -f 指定控制編譯器行為的選項 

  -m 指定與硬件相關的選項 

  3, GNU 調試程序 

  gdb 

  常用選項: 

  -d 指定遠程調試時串行接口的線路速度 

  -batch 以批處理模式運行 

  -c 指定要分析的核心轉儲文件 

  -cd 指定工作目錄 

  -d 指定搜索源文件的目錄 

  -e 指定要執行的文件 

  -f 調試時以標准格式輸出文件名和行號 

  -q 安靜模式 

  -s 指定符號的文件名 

  -se 指定符號和要執行的文件名 

  -tty 設置標准輸出和輸入設備 

  -x 從指定的文件執行 gdb 命令 

  由於 gnu 調試時忽略開始處斷點, 需要在開始標簽處執行一個空指令 

  如: 

  .globl _start 

  _start: 

  nop 

  此時斷點可以設置成 break *_start+1 

  查看寄存器狀態 info registers 

  使用 print 命令查看特定寄存器或者變量的值, 加上修飾符可以得到不同的輸出格式: 

  print/d 顯示十進制數字 

  print/t 顯示二進制數字 

  print/x 顯示 16 進制數字 

  使用 x 命令可以查看特定內存的值: 

  x/nyz 

  其中 n 為要顯示的字段數 

  y 時輸出格式, 它可以是: 

  c 用於字符, d 用於十進制, x 用於 16 進制 

  z 是要顯示的字段長度, 它可以是: 

  b 用於字節, h 用於 16 字節, w 用於 32 位字 

  如: 

  x/42cb 用於顯示前 42 字節 

  ################################################## ######################### 

  ##################### 

  # 三, GNU 匯編語言結構 

  ################################################## ######################### 

  ##################### 

  主要包括三個常用的段: 

  data 數據段 聲明帶有初始值的元素 

  bss 數據段 聲明使用 0 或者 null 初始化的元素 

  text 正文段 包含的指令, 每個匯編程序都必須包含此段 

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

  .section .data 

  .section .bss 

  .section .text 

  起始點: 

  gnu 匯編器使用_start 標簽表示默認的起始點, 此外如果想要匯編內部的標簽能夠被外部程序訪問,需要使用.globl 指令, 

  如:.globl _start 

  使用通用庫函數時可以使用: 

  ld -dynamic-linker /lib/ld-linux.so.2 

  ################################################## ######################### 

  ##################### 

  # 四, 數據傳遞 

  ################################################## ######################### 

  ##################### 

  1, 數據段 

  使用.data 聲明數據段, 這個段中聲明的任何數據元素都保留在內存中並可以被匯編程序的指令讀取,此外還可以使用.rodata 聲明只讀的數據 

  段, 在聲明一個數據元素時, 需要使用標簽和命令: 

  標簽:用做引用數據元素所使用的標記, 它和 c 語言的變量很相似, 它對於處理器是沒有意義的, 它只是用做匯編器試圖訪問內存位置時用做 

  引用指針的一個位置。 

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

  聲明指令: 

  .ascii 文本字符串 

  .asciz 以空字符結尾的字符串 

  .byte 字節值 

  .double 雙精度浮點值 

  .float 單精度浮點值 

  .int 32 位整數 

  .long 32 位整數,和 int 相同 

  .octa 16 字節整數 

  .quad 8 字節整數 

  .short 16 位整數 

  .single 單精度浮點數(和 float 相同) 

  例子: 

  output: 

  .ascii "hello world." 

  pi: 

  .float 2.14 

  聲明可以在一行中定義多個值,如: 

  ages: 

  .int 20, 10, 30, 40 

  定義靜態符號: 

  使用.equ 命令把常量值定義為可以在文本段中使用的符號,如: 

  .section .data 

  .equ LINUX_SYS_CALL, 0x80 

  .section .text 

  movl $LINUX_SYS_CALL, %eax 

  2, bss 段 

  和 data 段不同, 無需聲明特定的數據類型, 只需聲明為所需目的保留的原始內存部分即可。女裝 

  GNU 匯編器使用以下兩個命令聲明內存區域: 

  m 聲明為未初始化的通用內存區域 

  .lcomm 聲明為未初始化的本地內存區域 

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

  m/.lcomm symbol, length 

  例子: 

  .section .bss 

  .lcomm buffer, 1000 

  該語句把 1000 字節的內存地址賦予標簽 buffer, 在聲明本地通用內存區域的程序之外的函數是不能訪問他們的.(不能在.globl 命令中使用他 

  們) 

  在 bss 段聲明的好處是, 數據不包含在可執行文件中。 

  在數據段中定義數據時, 它必須被包含在可執行程序中, 因為必須使用特定值初始化它。 

  因為不使用數據初始化 bss 段中聲明的數據區域,所以內存區域被保留在運行時使用, 並且不必包含在最終的程序中 

  3, 傳送數據 

  move 指令: 

  格式 movex 源操作數, 目的操作數。 其中 x 為要傳送數據的長度, 取值有: 

  l 用於 32 位的長字節 

  w 用於 16 位的字 

  b 用於 8 位的字節值 

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

  8 個通用的寄存器是用於保存數據的最常用的寄存器, 這些寄存器的內容可以傳遞給其他的任何可用的寄存器。 和通用寄存器不同, 專用寄存 

  器(控制, 調試,段)的內容只能傳送給通用寄存器, 或者接收從通用寄存器傳過來的內容。 

  在對標簽進行引用時: 

  例: 

  .section .data 

  value: 

  .int 100 

  _start: 

  movl value, %eax 

  movl $value, %eax 

  movl %ebx, (%edi) 

  movl %ebx, 4(%edi) 

  其中:movl value, %eax 只是把標簽 value 當前引用的內存值傳遞給 eax 

  movl $value, %eax 把標簽 value 當前引用的內存地址指針傳遞給 eax 

  movl %ebx, (%edi) 如果 edi 外面沒有括號那么這個指令只是把 ebx 中的 

  值加載到 edi 中, 如果有了括號就表示把 ebx 中的內容 

  傳送給 edi 中包含的內存位置。 

  movl %ebx, 4(%edi) 表示把 edi 中的值放在 edi 指向的位置之后的 4 字節內存位置中 

  movl %ebx, -4(%edi) 表示把 edi 中的值放在 edi 指向的位置之前的 4 字節內存位置中 

  cmove 指令(條件轉移): 

  cmovex 源操作數, 目的操作數. x 的取值為: 

  無符號數: 

  a/nbe 大於/不小於或者等於 

  ae/nb 大於或者等於/不小於 

  nc 無進位 

  b/nae 小於/不大於等於 

  c 進位 

  be/na 小於或等於/不大於 

  e/z 等於/零 

  ne/nz 不等於/不為零 

  p/pe 奇偶校驗/偶校驗 

  np/po 非奇偶校驗/奇校驗 

  有符號數: 

  ge/nl 大於或者等於/不小於 

  l/nge 小於/不大於或者等於 

  le/ng 小於或者等於/不大於 

  o 溢出 

  no 未溢出 

  s 帶符號(負) 

  ns 無符號(非負) 

  交換數據: 

  xchg 在兩個寄存器之間或者寄存器和內存間交換值如: 

  xchg 操作數, 操作數, 要求兩個操作數必須長度相同且不能同時都是內存位置其中寄存器可以是 32,16,8 位的 bswap 反轉一個 32 位寄 

  存器的字節順序如: bswap %ebx 

  xadd 交換兩個值 並把兩個值只和存儲在目標操作數中如: xadd 源操作數,目標操作數 

  其中源操作數必須是寄存器, 目標操作數可以是內存位置也可以是寄存器其中寄存器可以是 32,16,8 位的 

  cmpxchg 

  cmpxchg source, destination 

  其中 source 必須是寄存器, destination 可以是內存或者寄存器, 用來比較兩者的值, 如果相等,就把源操作數的值加載到目標操作數中,如 

  果不等就把目標操作數加載到源操作數中,其中寄存器可以是 32,16,8 位的, 其中源操作數是 EAX,AX 或者 AL 寄存器中的值 

  cmpxchg8b 同 cmpxchg, 但是它處理 8 字節值, 同時它只有一個操作數 

  cmpxchg8b destination 其中 destination 引用一個內存位置, 其中的 8 字節值會與 EDX 和 EAX 寄存器中包含的值(EDX 高位寄存器,EAX 

  低位寄存器)進行比較, 如果目標值和 EDX:EAX 對中的值相等, 就把 EDX:EAX 對中的 64 位值傳遞給內存位置, 如果不匹配就把內存地址中 

  的值加載到 EDX:EAX 對中 

  4, 堆棧 

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

  壓入堆棧操作: 

  pushx source, x 取值為: 

  l 32 位長字 

  w 16 位字 

  彈出堆棧操作: 

  popx source 

  其中 source 必須是 16 或 32 位寄存器或者內存位置,當 pop 最后一個元素時 ESP 值應該和以前的相等 

  5,壓入和彈出所有寄存器 

  pusha/popa 壓入或者彈出所有 16 位通用寄存器 

  pushad/popad 壓入或者彈出所有 32 位通用寄存器 

  pushf/popf 壓入或者彈出 EFLAGS 寄存器的低 16 位 

  pushfd/popfd 壓入或者彈出 EFLAGS 寄存器的全部 32 位 

  6,數據地址對齊 

  gas 匯編器支持.align 命令, 它用於在特定的內存邊界對准定義的數據元素, 在數據段中.align 命令緊貼在數據定義的前面 

  ################################################## ######################### 

  ##################### 

  # 五,控制流程 

  ################################################## ######################### 

  ##################### 

  無條件跳轉: 

  1, 跳轉 

  jmp location 其中 location 為要跳轉到的內存地址, 在匯編中為定義的標簽 

  2,調用 

  調用指令分為兩個部分: 

  1, 調用 call address 跳轉到指定位置 

  2, 返回指令 ret, 它沒有參數緊跟在 call 指令后面的位置 

  執行 call 指令時,它把 EIP 的值放到堆棧中, 然后修改 EIP 以指向被調用的函數地址, 當被調用函數完成后, 它從堆棧獲取過去的 EIP 的 

  值, 並把控制權返還給原始程序。 

  3,中斷 

  由硬件設備生成中斷。 程序生成軟件中斷當一個程序產生中斷調用時, 發出調用的程序暫停, 被調用的程序接替它運行, 指令指針被轉移到 

  被調用的函數地址, 當調用完成時使用中斷返回指令可以返回調原始程序。 

  條件跳轉: 

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

  jxx address, 其中 xx 是 1-3 個字符的條件代碼, 取值如下: 

  a 大於時跳轉 

  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 如果為零 

  條件跳轉不支持分段內存模型下的遠跳轉, 如果在該模式下進行程序設計必須使用程序邏輯確定條件是否存在, 然后實現無條件跳轉, 跳轉 

  前必須設置 EFLAGS 寄存器 

  比較: 

  cmp operend1, operend2 

  進位標志修改指令: 

  CLC 清空進位標志(設置為 0) 

  CMC 對進位標志求反(把它改變為相反的值) 

  STC 設置進位標志(設置為 1) 

  循環: 

  loop 循環直到 ECX 寄存器為 0 

  loope/loopz 循環直到 ecx 寄存器為 0 或者沒有設置 ZF 標志 

  loopne/loopnz 循環直到 ecx 為 0 或者設置了 ZF 標志 

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

  ################################################## ######################### 

  ##################### 

  # 六,數字 

  ################################################## ######################### 

  ##################### 

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

  把無符號數轉換成位數更大的值時, 必須確保所有的高位部分都被設置為零 

  把有符號數轉換成位數更大的數時: 

  intel 提供了 movsx 指令它允許擴展帶符號數並保留符號, 它與 movzx 相似, 但是它假設要傳送的字節是帶符號數形式 

  浮點數: 

  fld 指令用於把浮點數字傳送入和傳送出 FPU 寄存器, 格式: 

  fld source 

  其中 source 可以為 32 64 或者 80 位整數值 

  IA-32 使用 FLD 指令用於把存儲在內存中的單精度和雙精度浮點值 FPU 寄存器堆棧中, 為了區分這兩種長度 GNU 匯編器使用 

  FLDS 加載單精度浮點數, FLDL 加載雙精度浮點數 

  類似 FST 用於獲取 FPU 寄存器堆棧中頂部的值, 並且把這個值放到內存位置中, 對於單精度使用 FSTS, 對於雙精度使用 FSTL 

  ################################################## ######################### 

  ##################### 

  # 七,基本數學運算 

  ################################################## ######################### 

  ##################### 

  1, 加法 

  ADD source, destination 把兩個整數相加 

  其中 source 可以是立即數內存或者寄存器, destination 可以是內存或者寄存器, 但是兩者不能同時都是內存位置 

  ADC 和 ADD 相似進行加法運算, 但是它把前一個 ADD 指令的產生進位標志的值包含在其中, 在處理位數大於 32(如 64) 

  位的整數時, 該指令非常有用 

  2, 減法 

  SUB source, destination 把兩個整數相減 

  NEG 它生成值的補碼 

  SBB 指令, 和加法操作一樣, 可以使用進位情況幫助執行大的無符號數值的減法運算. SBB 在多字節減法操作中利用進位和溢出標志實現跨 

  數據邊界的的借位特性 

  3,遞增和遞減 

  dec destination 遞減 

  inc destination 遞增 

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

  4, 乘法 

  mul source 進行無符號數相乘 

  它使用隱含的目標操作數, 目標位置總是使用 eax 的某種形式, 這取決與源操作數的長度, 因此根據源操作數的長度,目標操作數必須放在 

  AL, AX, EAX 中。 此外由於乘法可能產生很大的值, 目標位置必須是源操作數的兩倍位置, 源為 8 時, 應該是 16, 源為 16 時, 應該為 32, 但 

  是當源為 16 位時 intel 為了向下兼容, 目標操作數不是存放在 eax 中, 而是分別存放在 DX:AX 中, 結果高位存儲在 DX 中, 地位存儲在 AX 中。 

  對於 32 位的源, 目標操作數存儲在 EDX:EAX 中, 其中 EDX 存儲的是高 32 位, EAX 存儲的是低 32 位 

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

  imul source, destination 也可以執行有符號乘法運算, 但是此時可以把目標放在指定的位置, 使用這種格式的缺陷 

  在與乘法的操作結果被限制為單一目標寄存器的長度. 

  imul multiplier, source, destination 

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

  5, 除法 

  div divisor 執行無符號數除法運算 

  除數的最大值取決與被除數的長度, 對於 16 位被除數 ,除數只能為 8 位, 32 或 64 位同上 

  被除數 被除數長度商 余數 

  AX 16 位 AL AH 

  DX:AX 32 位 AX DX 

  EDX:EAX 64 位 EAX EDX 

  idiv divisor 執行有符號數的除法運算, 方式和 div 一樣 

  6, 移位 

  左移位: 

  sal 向左移位 

  sal destination 把 destination 向左移動 1 位 

  sal %cl, destination 把 destination 的值向左移動 CL 寄存器中指定的位數 

  sal shifter, destination 把 destination 的值向左移動 shifter 值指定的位數 

  向左移位可以對帶符號數和無符號數執行向左移位的操作, 移位造成的空位用零填充, 移位造成的超過數據長度的任何位都被存放在進位標志 

  中, 然后在下一次移位操作中被丟棄 

  右移位: 

  shr 向右移位 

  sar 向右移位 

  SHR 指令清空移位造成的空位, 所以它只能對無符號數進行移位操作 

  SAR 指令根據整數的符號位, 要么清空, 要么設置移位造成的空位, 對於負數, 空位被設置為 1 

  循環移位: 

  和移位指令類似, 只不過溢出的位被存放回值的另一端, 而不是丟棄 

  ROL 向左循環移位 

  ROR 向右循環移位 

  RCL 向左循環移位, 並且包含進位標志 

  RCR 向右循環移位, 並且包含進位標志 

  7, 邏輯運算 

  AND OR XOR 

  這些指令使用相同的格式: 

  and source, destination 

  其中 source 可以是 8 位 16 位或者 32 位的立即值 寄存器或內存中的值, destination 可以是 8 位 16 位或者 32 位寄存器或內存中的值, 

  不能同時使用內存值作為源和目標。 布爾邏輯功能對源和目標執行按位操作。 

  也就是說使用指定的邏輯功能按照順序對數據的元素的每個位進行單獨比較。 

  NOT 指令使用單一操作數, 它即是源值也是目標結果的位置 

  清空寄存器的最高效方式是使用 OR 指令對寄存器和它本身進行異或操作.當和本身進行 XOR 操作時, 每個設置為 1 的位就變為 0, 每個設 

  置為 0 的位也變位 0。 

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

  標志 

  ################################################## ######################### 

  ##################### 

  # 八,字符串處理 

  ################################################## ######################### 

  ##################### 

  1, 傳送字符串 

  movs 有三種格式 

  movsb 傳送單一字節 

  movsw 傳送一個字 

  movsl 傳送雙字 

  movs 指令使用隱含的源和目的操作數, 隱含的源操作數是 ESI, 隱含的目的操作數是 EDI, 有兩種方式加載內存地址到 ESI 和 EDI, 

  第一種是使用標簽間接尋址 movl $output, %ESI, 第二種是使用 lea 指令, lea 指令加載對象的地址到指定的目的操作數如 lea output, 

  %esi, 每次執行 movs 指令后, 數據傳送后 ESI 和 EDI 寄存器會自動改變,為另一次傳送做准備, ESI 和 EDI 可能隨着標志 DF 的不同自動 

  遞增或者自動遞減, 如果 DF 標志為 0 則 movs 指令后 ESI 和 EDI 會遞增, 反之會遞減, 為了設置 DF 標志, 可以使用一下指令: 

  CLD 將 DF 標志清零 

  STD 設置 DF 標志 

  2,rep 前綴 

  REP 指令的特殊之處在與它不執行什么操作, 這條指令用於按照特定次數重復執行字符串指令,有 ECX 寄存器控制,但不需要額外的 loop 指 

  令,如 rep movsl 

  rep 的其他格式: 

  repe 等於時重復 

  repne 不等於時重復 

  repnz 不為零時重復 

  repz 為零時重復 

  3, 存儲和加載字符串 

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

  STOS 使用 lods 把字符串值加載到 EAX 后, 可以使用它把 EAX 中的值存儲到內存中去: 

  stos 使用 EDI 作為目的操作數, 執行 stos 指令后, 會根據 DF 的值自動遞增或者遞減 EDI 中的值 

  4, 比較字符串 

  cmps 和其他的操作字符串的指令一樣, 隱含的源和目標操作數都為 ESI 和 EDI, 每次執行時都會根據 DF 的值把 

  ESI 和 EDI 遞增或者遞減, cmps 指令從目標字符串中減去源字符串, 執行后會設置 EFLAGS 寄存器的狀態. 

  5,掃描字符串 

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

  ################################################## ######################### 

  ##################### 

  # 九,使用函數 

  ################################################## ######################### 

  ##################### 

  GNU 匯編語言定義函數的語法: 

  .type 標簽(也就是函數名), @function 

  ret 返回到調用處 

  ################################################## ######################### 

  ##################### 

  # 十,linux 系統調用 

  ################################################## ######################### 

  ##################### 

  linux 系統調用的中斷向量為 0x80 

  1, 系統調用標識存放在%eax 中 

  2, 系統調用輸入值: 

  EBX 第一個參數 

  ECX 第二個參數 

  EDX 第三個參數 

  ESI 第四個參數 

  EDI 第五個參數 

  需要輸入超過 6 個輸入參數的系統調用, EBX 指針用於保存指向輸入參數內存位置的指針, 輸入參數按照連續的的順序存儲, 系統調用的返回 

  值存放在 EAX 中


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM