GCC 內聯彙編
阿新 • • 發佈:2020-11-24
[TOC]
GNU C 允許在 C 程式碼中嵌入彙編程式碼,這種特性被稱為內聯彙編。使用內聯彙編可以同時發揮 C 和彙編的強大能力。
本文介紹 GCC 的內聯彙編拓展,Clang 編譯器相容大部分 GCC 語言拓展,因此 GNU C 的內聯彙編特性大部分在 Clang 中工作正常。
本文實驗環境如下:
```
Linux Friday 5.8.17-300.fc33.x86_64 #1 SMP Thu Oct 29 15:55:40 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
gcc (GCC) 10.2.1 20201016 (Red Hat 10.2.1-6)
```
使用 64 位 AT&T 風格 x86 彙編,為了和編譯器自動生成的註釋區分開,我新增的註釋使用`##`風格。
# 基本內聯彙編
基本內聯彙編是 GCC 對內聯彙編最簡陋的支援,它實際上已經沒有任何使用價值了,介紹它只是為了說明使用內聯彙編的基本原理和問題。
基本內聯彙編的語法如下:
```c
asm asm_qualifiers ( AssembleInstructions )
```
`asm_qulifiers`包括以下兩個修飾符:
- volatile: 指示編譯器不要對 asm 程式碼段進行優化
- inline: 指示編譯器儘可能小的假設 asm 指令的大小
這兩個修飾符的意義先不用深究,本文會逐步介紹它們的作用。
`asm`不是 ISO C 中的關鍵字,如果我們開啟了 -std=c99 等啟用 ISO C 的編譯選項,程式碼將無法成功編譯。然而,內聯彙編對於許多 ISO C 程式是必須的,GCC 通過 \__asm__ 給程式設計師開了個後門。使用 \_\_asm__ 替代 asm 可以讓程式作為 ISO C 程式成功編譯。volatile 和 inline 也有加 \_\_ 的版本。
`AssembleInstructions`是我們手寫的彙編指令。基本內聯彙編的例子如下:
```c
__asm__ __valatile__(
"movq %rax, %rdi \n\t"
"movq %rbx, %rsi \n\t"
);
```
編譯器不解析 asm 塊中的指令,直接把它們插入到生成的彙編程式碼中,剩下的任務有彙編器完成。這個過程有些類似於巨集。為了避免我們手寫的彙編程式碼擠在一起,導致指令解析錯誤,通常在每一條指令後面都加上`\n\t`獲得合適的格式。
編譯器不解析 asm 塊中的指令的一個推論是:GCC 對我們插入的指令毫不知情。這相當於我們人為地干涉了 GCC 自動的程式碼生成,如果我們處理不當,很可能導致最終生成的程式碼是錯誤的。考慮以下程式碼段:
```c
#