熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Oracle >> 正文

Linux 中 x86 的內聯匯編

2022-06-13   來源: Oracle 

  作者Bharata B Rao
  
  將各個部分組合起來
  
  如果您是 Linux 內核的開發人員您會發現自己經常要對與體系結構高度相關的功能進行編碼或優化代碼路徑您很可能是通過將匯編語言指令插入到 C 語句的中間(又稱為內聯匯編的一種方法)來執行這些任務的讓我們看一下 Linux 中內聯匯編的特定用法(我們將討論限制在 IA 匯編
  
  GNU 匯編程序簡述
  讓我們首先看一下 Linux 中使用的基本匯編程序語法GCC(用於 Linux 的 GNU C 編譯器)使用 AT&T 匯編語法下面列出了這種語法的一些基本規則(該列表肯定不完整只包括了與內聯匯編相關的那些規則
  
  寄存器命名
  寄存器名稱有 % 前綴如果必須使用 eax它應該用作 %eax
  
  源操作數和目的操作數的順序
  在所有指令中先是源操作數然後才是目的操作數這與將源操作數放在目的操作數之後的 Intel 語法不同
  
  mov %eax %ebx transfers the contents of eax to ebx
  
  操作數大小
  根據操作數是字節 (byte)字 (word) 還是長型 (long)指令的後綴可以是 bw 或 l這並不是強制性的GCC 會嘗試通過讀取操作數來提供相應的後綴但手工指定後綴可以改善代碼的可讀性並可以消除編譯器猜測不正確的可能性
  
  movb %al %bl Byte move
   movw %ax %bx Word move
   movl %eax %ebx Longword move
  
  立即操作數
  通過使用 $ 指定直接操作數
  
  movl $xffff %eax will move the value of xffff into eax register
  
  間接內存引用
  任何對內存的間接引用都是通過使用 ( ) 來完成的
  
  movb (%esi) %al will transfer the byte in the memory
  
  pointed by esi into al
  register
  
  內聯匯編
  GCC 為內聯匯編提供特殊結構它具有以下格式
  
  GCG 的 asm 結構
  
   asm ( assembler template
  
  : output operands (optional)
  
  
  : input operands (optional)
  
  
  : list of clobbered registers
   (optional)
  
  
  );
  
  本例中匯編程序模板由匯編指令組成輸入操作數是充當指令輸入操作數使用的 C 表達式輸出操作數是將對其執行匯編指令輸出的 C 表達式
  
  內聯匯編的重要性體現在它能夠靈活操作而且可以使其輸出通過 C 變量顯示出來因為它具有這種能力所以 asm 可以用作匯編指令和包含它的 C 程序之間的接口
  
  一個非常基本但很重要的區別在於簡單內聯匯編只包括指令而擴展內聯匯編包括操作數要說明這一點考慮以下示例
  
  內聯匯編的基本要素
  
  {
   int a= b;
   asm (movl % %%eax;
   movl %%eax %;
   :=r(b) /* output */
   :r(a) /* input */
   :%eax); /* clobbered register */
  }
  
  在上例中我們使用匯編指令使 b 的值等於 a請注意以下幾點
  
  b 是輸出操作數由 % 引用a 是輸入操作數由 % 引用
  r 是操作數的約束它指定將變量 ab 存儲在寄存器中請注意輸出操作數約束應該帶有一個約束修飾符 =指定它是輸出操作數
  要在 asm 內使用寄存器 %eax%eax 的前面應該再加一個 %換句話說就是 %%eax因為 asm 使用 %% 等來標識變量任何帶有一個 % 的數都看作是輸入/輸出操作數而不認為是寄存器
  第三個冒號後的修飾寄存器 %eax 告訴將在 asm 中修改 GCC %eax 的值這樣 GCC 就不使用該寄存器存儲任何其它的值
  movl % %%eax 將 a 的值移到 %eax 中movl %%eax % 將 %eax 的內容移到 b
  因為 b 被指定成輸出操作數因此當 asm 的執行完成後它將反映出更新的值換句話說asmb 所做的更改將在 asm 外反映出來
  現在讓我們更詳細的了解每一項的含義
  
  匯編程序模板
  匯編程序模板是一組插入到 C 程序中的匯編指令(可以是單個指令也可以是一組指令)每條指令都應該由雙引號括起或者整組指令應該由雙引號括起每條指令還應該用一個定界符結尾有效的定界符為新行 (\n) 和分號 (;) \n 後可以跟一個 tab(\t) 作為格式化符號增加 GCC 在匯編文件中生成的指令的可讀性 指令通過數 %% 等來引用 C 表達式(指定為操作數)
  
  如果希望確保編譯器不會在 asm 內部優化指令可以在 asm 後使用關鍵字 volatile如果程序必須與 ANSI C 兼容則應該使用 __asm__ 和 __volatile__而不是 asm 和 volatile
  
  操作數
  C 表達式用作 asm 內的匯編指令操作數在匯編指令通過對 C 程序的 C 表達式進行操作來執行有意義的作業的情況下操作數是內聯匯編的主要特性
  
  每個操作數都由操作數約束字符串指定後面跟用括弧括起的 C 表達式例如constraint (C expression)操作數約束的主要功能是確定操作數的尋址方式
  
  可以在輸入和輸出部分中同時使用多個操作數每個操作數由逗號分隔開
  
  在匯編程序模板內部操作數由數字引用如果總共有 n 個操作數(包括輸入和輸出)那麼第一個輸出操作數的編號為 逐項遞增最後那個輸入操作數的編號為 n總操作數的數目限制在 如果機器描述中任何指令模式中的最大操作數數目大於 則使用後者作為限制
  
  修飾寄存器列表
  如果 asm 中的指令指的是硬件寄存器可以告訴 GCC 我們將自己使用和修改它們這樣GCC 就不會假設它裝入到這些寄存器中的值是有效值通常不需要將輸入和輸出寄存器列為 clobbered因為 GCC 知道 asm 使用它們(因為它們被明確指定為約束)不過如果指令使用任何其它的寄存器無論是明確的還是隱含的(寄存器不在輸入約束列表中出現也不在輸出約束列表中出現)寄存器都必須被指定為修飾列表修飾寄存器列在第三個冒號之後其名稱被指定為字符串
  
  至於關鍵字如果指令以某些不可預知且不明確的方式修改了內存則可能將 memory 關鍵字添加到修飾寄存器列表中這樣就告訴 GCC 不要在不同指令之間將內存值高速緩存在寄存器中
  
  操作數約束
  前面提到過asm 中的每個操作數都應該由操作數約束字符串描述後面跟用括弧括起的 C 表達式操作數約束主要是確定指令中操作數的尋址方式約束也可以指定
  
  是否允許操作數位於寄存器中以及它可以包括在哪些種類的寄存器中
  操作數是否可以是內存引用以及在這種情況下使用哪些種類的地址
  操作數是否可以是立即數
  約束還要求兩個操作數匹配
  
  常用約束
  在可用的操作數約束中只有一小部分是常用的下面列出了這些約束以及簡要描述有關操作數約束的完整列表請參考 GCC 和 GAS 手冊
  
  寄存器操作數約束 (r)
  使用這種約束指定操作數時它們存儲在通用寄存器中請看下例
  
  asm (movl %%cr %\n :=r(crval));
  
  這裡變量 crval 保存在寄存器中%cr 的值復制到寄存器上crval 的值從該寄存器更新到內存中指定 r 約束時GCC 可以將變量 crval 保存在任何可用的 GPR 中要指定寄存器必須通過使用特定的寄存器約束直接指定寄存器名
  
  a %eax
  
  b %ebx
  
  c %ecx
  
  d %edx
  
  S %esi
  
  D %edi
  
  內存操作數約束 (m)
  當操作數位於內存中時任何對它們執行的操作都將在內存位置中直接發生這與寄存器約束正好相反後者先將值存儲在要修改的寄存器中然後將它寫回內存位置中但寄存器約束通常只在對於指令來說它們是絕對必需的或者它們可以大大提高進程速度時使用當需要在 asm 內部更新 C 變量而您又確實不希望使用寄存器來保存其值時使用內存約束最為有效例如idtr 的值存儲在內存位置 loc 中
  
  (sidt %\n : :m(loc));
  
  匹配(數字)約束
  在某些情況下一個變量既要充當輸入操作數也要充當輸出操作數可以通過使用匹配約束在 asm 中指定這種情況
  
  asm (incl % :=a(var):(var));
  
  在匹配約束的示例中寄存器 %eax 既用作輸入變量也用作輸出變量將 var 輸入讀取到 %eax增加後將更新的 %eax 再次存儲在 var 中這裡的 指定第 個輸出變量相同的約束它指定 var 的輸出實例只應該存儲在 %eax 中該約束可以用於以下情況
  
  輸入從變量中讀取或者變量被修改後修改寫回到同一變量中
  不需要將輸入操作數和輸出操作數的實例分開
  使用匹配約束最重要的意義在於它們可以導致有效地使用可用寄存器
  
  一般內聯匯編用法示例
  以下示例通過各種不同的操作數約束說明了用法有如此多的約束以至於無法將它們一一列出這裡只列出了最經常使用的那些約束類型
  
  asm 和寄存器約束 r
  讓我們先看一下使用寄存器約束 r 的 asm我們的示例顯示了 GCC 如何分配寄存器以及它如何更
From:http://tw.wingwit.com/Article/program/Oracle/201311/17556.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.