GCC 內聯彙編
前面寫了三篇,是自我摸索三篇,摸著石頭過河,有些或許是錯誤的細節,不必在意! 今天我們直接用GCC編譯C語言程式碼,且在C語言裡面內嵌AT&T風格的彙編! 前三篇大家瞭解即可,我們重點放在內嵌彙編裡,簡單快捷舒服!
GCC支援在C/C++程式碼中嵌入彙編程式碼,這些彙編程式碼被稱作GCC Inline ASM——GCC內聯彙編。
這是一個非常有用的功能,有利於我們將一些C/C++語法無法表達的指令直接潛入C/C++程式碼中,另外也允許我們直接寫C/C++程式碼中使用匯編編寫簡潔高效的程式碼。其實為了裝逼和護城河!
gcc 編譯器支援 2 種形式的內聯 asm 程式碼:
基本 asm 格式:不支援運算元;
擴充套件 asm 格式:支援運算元;
1. 語法規則
asm [volatile] ("彙編指令")
所有指令,必須用雙引號包裹起來;
超過一條指令,必須用\n分隔符進行分割,為了排版,一般會加上\t;
多條彙編指令,可以寫在一行,也可以寫在多行;
關鍵字 asm 可以使用 asm 來替換;
volatile 是可選的,編譯器有可能對彙編程式碼進行最佳化,使用 volatile 關鍵字之後,告訴編譯器不要最佳化手寫的內聯彙編程式碼。
基本內聯彙編的格式是
__asm__ __volatile__("Instruction List");
可選風格 有比較多種,不過測試了下 就下面程式碼的風格支援得好,
每行彙編命令 要加冒號和\N\T 確實麻煩. 不過可以使用軟體前後追加特定符號就行了.
下面是基本格式 不支援運算元,它必須使用全域性變數,這限制太麻煩了
#include <stdio.h>
int a =
1;
int b =
2;
int c;
int
main
()
{
asm
volatile
(
"movl a, %eax\n\t"
"addl b, %eax\n\t"
"movl %eax, c"
);
printf(
"c = %d \n", c);
return
0;
}
其實彙編 內嵌 相當於呼叫個 C函式 而已, 不過這C函式是彙編函式而已
#
include
<stdio.h>
int
main
()
{
int data1 =
1;
int data2 =
2;
int data3;
asm
volatile
(
"movl %%ebx, %%eax\n\t"
"addl %%ecx, %%eax"
:
"=a"(data3) :
"b"(data1),
"c"(data2)
);
/*asm [volatile] ("彙編指令\n\t" : "輸出運算元列表" : "輸入運算元列表" : "改動的暫存器")*/
printf(
"data3 = %d \n", data3);
return
0;
}
這裡必須使用擴充套件ASM格式,才能不使用全域性變數當作引數傳入彙編函式裡
所以彙編命令 暫存器要多個%號 "movl %%ebx,%%eax" 把EBX的值覆蓋進
EAX
裡.
所有彙編命令結束後 最後個小掛號前 開始定義我們的函式引數
第一個冒開始是 輸出引數 :"=a"(data3)
第二個冒號開始是 輸入引數 有多個引數用逗號分隔
第三個冒號是不要最佳化暫存器列表
其中
:
"=a
"
(data3
) 的 data3 是C的變數 用小掛號保護起來, 前面的=A
叫做
"修飾符"
對輸出暫存器或記憶體地址提供 額外的說明,包括下面4個修飾符:
+:被修飾的運算元可以讀取,可以寫入;
=:被修飾的運算元只能寫入;
%:被修飾的運算元可以和下一個運算元互換;
&:在行內函式完成之前,可以刪除或者重新使用被修飾的運算元;
其中A 使用暫存器的別名 "=a" 表示只能寫A的寄出器(EAX)
通俗講下面的
叫約束
a: 使用 eax/ax/al 暫存器;
b: 使用 ebx /bx/bl 暫存器;
c: 使用 ecx/cx/cl 暫存器;
d: 使用 edx/dx/dl 暫存器;
r: 使用任何可用的通用暫存器;
m: 使用變數的記憶體位置;
b(data1)表示 data1變數的值複製到B寄出器裡
在內聯彙編程式碼中,沒有宣告“改動的暫存器”列表,也就是說可以省略掉(前面的冒號也不需要);
使用佔位符來代替暫存器名稱
如果運算元有
很多,那麼在內聯彙編程式碼中去寫每個暫存器的名稱,就顯得
很不方便。佔位符有點類似於批處理指令碼中,利用
2...來引用輸入引數一樣,內聯彙編程式碼中的佔位符,從
輸出運算元列表中的暫存器開始從
0 編號,一直編號到
輸入運算元列表中的所有暫存器。
#
include
<stdio.h>
int
main
()
{
int data1 =
1;
int data2 =
2;
int data3;
asm(
"movl %1, %%eax\n\t"
"addl %2, %0"
:
"=r"
(data3) :
"r"
(data1),
"r"
(data2) );
printf(
"data3 = %d \n", data3);
return
0;
}
%0 是輸入引數 依次是 兩個輸入引數 %1 %2
內聯彙編的C語言 正常編譯就好了
[root@dsmart=>LINUX_ASM]
$gcc main_add.c -o main.add.exe
[root@dsmart=>LINUX_ASM]$./main.add.exe
data3 = 3
我們可以檢視下GCC 彙編的程式碼
[root@dsmart=>LINUX_ASM]$gcc -S main_add.c -o main_add.asm[root@dsmart=>LINUX_ASM]$vim main_add.asm
下面是我們GCC把MAIN_ADD.C全部翻譯成了彙編程式碼,而我們重點的內嵌彙編用藍色註解#APP----#NOAPP 範圍內
.file "main_add.c"
.section .rodata
.LC0:
.string "data3 = %d \n"
.text
.globl main
. type main, @ function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16 , %rsp
movl $1 , -4(%rbp)
movl $2 , -8(%rbp)
movl -4(%rbp), %eax
movl -8(%rbp), %edx
#APP
# 9 "main_add.c" 1
movl %eax, %eax
addl %edx, %eax
# 0 "" 2
#NO_APP
movl %eax, -12(%rbp)
movl -12(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0 , %eax
call printf
movl $0 , %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 5.5.0" .section .note.GNU-stack,"",@progbits裡面這段程式碼不是%1了,被具體替換成了寄出器名. 這段是C語言呼叫匯編函式進行引數壓棧操作,分別把引數1,2壓入棧底
movl $1, -4(%rbp) movl $2, -8(%rbp) movl -4(%rbp), %eax movl -8(%rbp), %edxrsp : 棧指標 暫存器,指向棧頂
rbp : 棧基址暫存器,指向棧底返回引數:
movl %eax, -12(%rbp) movl -12(%rbp), %eax把結果 壓入棧底 -12位置,然後出棧 把RBP棧的值 返回給EAX 好像這有點多餘
下面準備呼叫PRINTF函式 edi : 函式引數
rsi/esi : 函式引數下面我們進行O3最佳化下看
[root@dsmart=>LINUX_ASM]$gcc main_add.c -S -O3 -o main_add.asm程式碼確實少了些
.file "main_add.c"
.section .rodata.str1. 1, "aMS",@progbits, 1
.LC 0:
.string "data3 = %d \n"
.section .text.unlikely, "ax",@progbits
.LCOLDB1:
.section .text.startup, "ax",@progbits
.LHOTB1:
.p2align 4,, 15
.globl main
.type main, @function
main:
.LFB11:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $2, %esi
movl $1, %eax
#APP
# 9 "main_add.c" 1
movl %eax, %eax
addl %esi, %esi
# 0 "" 2
#NO_APP
movl $.LC 0, %edi
xorl %eax, %eax
call printf
xorl %eax, %eax
addq $8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE11:
.size main, .-main
.section .text.unlikely
.LCOLDE1:
.section .text.startup
.LHOTE1:
.ident "GCC: (GNU) 5.5.0"
.section .note.GNU-stack, "",@progbits
結果最佳化的不像人樣了, volatile 也無法禁止最佳化內嵌彙編!
來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/680758/viewspace-3004498/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 64位內聯彙編
- Solidity之旅(十八)內聯彙編 [inline assembly]Solidinline
- GCC編譯過程(預處理->編譯->彙編->連結)GC編譯
- gcc 和 g++ 的聯絡和區別,使用 gcc 編譯 c++GC編譯C++
- C 語言宏 + 內聯彙編實現 MIPS 系統呼叫
- 內聯彙編很可怕嗎?看完這篇文章,終結它!
- gcc編譯階段列印巨集定義的內容GC編譯
- C++內嵌彙編 教程1C++
- Linux中gcc編譯工具LinuxGC編譯
- C指標原理(7)-C內嵌彙編指標
- C指標原理(8)-C內嵌彙編指標
- C指標原理(6)-C內嵌彙編指標
- 探索gcc編譯最佳化細節 編譯器最佳化gcc -o3GC編譯
- 09. C語言內嵌彙編程式碼C語言
- 彙編
- 編譯器優化:方法內聯編譯優化
- GCC編譯和連結過程GC編譯
- GCC編譯器背後的故事GC編譯
- nasm彙編ASM
- 彙編命令A
- 記錄一次gcc的編譯GC編譯
- 32位支援:使用 GCC 交叉編譯GC編譯
- 記一次編譯GCC的經歷編譯GC
- 彙編基礎
- 初識彙編
- linux彙編指令Linux
- 手撕彙編。。。
- ARM彙編指令集彙總
- iOS逆向之旅(基礎篇) — 彙編(一)— 彙編基礎iOS
- iOS逆向學習筆記 - 彙編(一) - 初識彙編iOS筆記
- CentOS7編譯和安裝GCC7.5CentOS編譯GC
- Notepad++編譯和執行C語言 (GCC)編譯C語言GC
- Mingw GCC 編譯OpenCV報錯: Project files may be invalidGC編譯OpenCVProject
- iOS逆向之旅(基礎篇) — 彙編(五) — 彙編下的BlockiOSBloC
- iOS彙編入門教程(一)ARM64彙編基礎iOS
- 重新整理彙編—————彙編的基礎理論前置篇
- 彙編指令(待完善)
- 彙編快速入門