因為想要在 C 裡寫一些組合語言,所以就研究了一下怎麼寫,發現事情並不是像我想的這麼簡單故筆記之。
GCC 准許我們使用行內組語依照如下格式
asm("asm template" // 組合語言
:output operands //(optional) 輸出運算元
:input operands //(optional) 輸入運算元
:list of clobbered registers //(optional) 被更動的暫存器
組合語言使用雙引號 (") 包住,若為多行組語則須在結尾加上 \n\t 最後一行除外
因為 GCC 將組語一行一行指令當成字串
以下為簡單範例
#include <stdio.h> // simple.c
int main(void) {
int foo=10, bar=5, result;
asm("add ebx, eax":"=b"(result):"a"(foo), "b"(bar));
printf("foo + bar = %d\n", result);
return 0;
}
範例中 a 就代表 eax, b 表示 ebx 其他以此類推
然後編譯一下
gcc -o simple simple.c
然後你就會得到錯誤如下
simple.c Assembler messages :
simple.c:5: Error: too many memory references for `add’
這奇怪的錯誤訊息著實讓人看不懂,其實只是我們使用的是 intel syntax 去寫,可是 GCC 預設的是 AT&T
所以我們只要給個參數就好 gcc -masm=intel -o simple simple.c
這樣就可以執行無誤了
當然我們也可以使用 system call
#include <stdio.h>
int main(void){
asm(
"mov eax, 0x1\n\t"
"mov ebx, 0x2a\n\t"
"int 0x80");
return 0;
}
一樣編譯並執行之,使用 echo $?
可以看到最後一行指令的 exit code 為 42 代表呼叫成功。
所以你其實也可以完完全全在 C 裡寫組語過日子
//hell.c
#include <stdio.h>
char *line = "Hello world!\n";
/*
eax = syscall numer
ebx = unsigned int fd
ecx = const void *buf
edx = size_t count
*/
void sayHi(){
asm(
"mov eax, 0x4\n\t"
"mov ebx, 0x1\n\t"
"mov ecx, %0\n\t"
"mov edx, 0xd\n\t"
"int 0x80"
::"r"(line):"eax", "ebx", "ecx", "edx");
}
//eax = syscall number, ebx = int error_code
void exit(){
asm(
"mov eax, 0x1\n\t"
"mov ebx, 0x2a\n\t"
"int 0x80");
}
notmain(){
echo();
exit();
}
我們全部都使用 inline assembly 來撰寫一個 Hello world
其中 “r” 代表 register, 而 %0 代表第一個參數,也就是 input operands 的第一個,也就是 line
這邊我們編譯的時候略有不同
gcc -c -m32 -fno-builtin -masm=intel hell.c
ld -static -m elf_i386 -e notmain -o hell hell.o
- -fno-builtin 參數表示我們不使用內建的函式
- -c 代表只編譯不連結
- -m32 代表該程式要編譯成 32 位元
- -m elf_i386 同上原因
- -static 使用靜態連結
- -e notmain 可以將程式的入口,由預設的 _start 改為 notmain
大致上簡單的 inline assembly 就是如此了,不過這些都只是冰山一角
還有更多的東西可以挖掘在這裡 跟 這裡