Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

GCC 內嵌匯編完整指南:語法、約束與實戰案例

編輯:Linux 教學

GCC 內嵌匯編允許在 C 代碼中直接編寫匯編指令,並自動處理 C 變量與寄存器/內存操作數的關聯。本文系統梳理語法規則、約束字符、操作數對應關係,並通過實例演示常用模式。

基本語法結構

__asm__ [__volatile__] (assem_template
    : output_operands   /* 輸出部分 */
    : input_operands    /* 輸入部分 */
    : clobbers          /* 破壞描述部分 */
);
  • __volatile__:禁止編譯器優化/重排序,確保指令按序執行
  • 四部分用冒號分隔,可為空但冒號不能省略
  • 模板中指令用 ;\n\t 分隔

操作數占位符與約束

占位符 %0~%9 對應冒號後的 C 表達式(按輸出優先、輸入其次順序編號)。每個操作數前有約束字符串:

約束含義典型用途
=r輸出,通用寄存器結果變量
r輸入,通用寄存器參與運算的變量
m內存操作數內存變量
g寄存器、內存或立即數通用輸入
I立即數(特定範圍)常數
=&r早期佔用寄存器(&)防止輸入輸出共用寄存器

實戰案例

1. 簡單指令:hlt

__asm__ __volatile__("hlt");

2. 變量賦值:movl %1, %0

int result, input = 42;
__asm__ __volatile__("movl %1, %0" : "=r" (result) : "r" (input));
// 等價於 result = input;

3. 內存操作:btsl 設置位

static inline void __set_bit(int nr, volatile void *addr)
{
    __asm__("btsl %1, %0"
            : "=m" (*(volatile long *)addr)  // 輸出:內存地址
            : "Ir" (nr));                     // 輸入:立即數或寄存器
}

btsl 不允許雙操作數均為內存,故 nr 用 Ir 強制為立即數/寄存器。

4. 讀取標志寄存器

unsigned long flags;
__asm__ __volatile__("pushfl; popl %0" : "=g" (flags) : : "memory");
// "memory" 告訴編譯器內存可能被修改,防止優化導致錯誤

5. 字節操作:%b0、%h0

char low_byte, high_byte;
__asm__("movb %b1, %0" : "=r" (low_byte) : "r" (val));   // 低字節
__asm__("movb %h1, %0" : "=r" (high_byte) : "r" (val));  // 高字節

破壞描述

列出指令隱式修改的寄存器/狀態,常見:

  • "memory":指令讀寫內存(除輸出操作數外)
  • "cc":修改條件碼標志位(x86 通常隱含)
  • 具體寄存器:"eax", "ecx" 等

常見陷阱

  1. 約束不匹配:如 movl 要求至少一操作數為寄存器,雙 "m" 會導致非法指令 movl mem, mem
  2. 忽略 early-clobber:輸出操作數在指令早期被寫入,需用 & 防止與輸入共用寄存器
  3. 缺少 "memory" clobber:內存操作未聲明,編譯器可能錯誤緩存值
  4. 輸出操作數未初始化:"=r" 要求變量可寫,無需初始值;"+r"(讀寫)需初始化

總結

GCC 內嵌匯編通過模板+約束實現 C 與匯編的安全交互。掌握約束語義、操作數編號規則、clobber 聲明是編寫正確、高效內嵌匯編的關鍵。優先使用內核提供的內聯函數(如 atomic_*, barrier()),僅在必要時手寫匯編。

Copyright © Windows教程網 All Rights Reserved