2011-02-27 156 views
8

我試圖使用內聯彙編... 我讀了此頁http://www.codeproject.com/KB/cpp/edujini_inline_asm.aspx但我無法理解傳遞給我函數的參數。在C/C++中使用內聯彙編

我正在寫一個C寫的例子..這是我的函數頭:

write2(char *str, int len){ 
} 

這是我的彙編代碼:

global write2 
write2: 
    push ebp 
    mov ebp, esp 
    mov eax, 4  ;sys_write 
    mov ebx, 1  ;stdout 
    mov ecx, [ebp+8] ;string pointer 
    mov edx, [ebp+12] ;string size 
    int 0x80  ;syscall 
    leave 
    ret 

什麼我必須做的傳遞代碼到C函數...我正在做這樣的事情:

write2(char *str, int len){ 
    asm ("movl 4, %%eax;" 
      "movl 1, %%ebx;" 
      "mov %1, %%ecx;" 
      //"mov %2, %%edx;" 
      "int 0x80;" 
      : 
      : "a" (str), "b" (len) 
    ); 
} 

這是因爲我沒有輸出變量,所以我怎麼做處理? 此外,使用此代碼:

global main 
main: 
    mov ebx, 5866  ;PID 
    mov ecx, 9  ;SIGKILL 
    mov eax, 37  ;sys_kill 
    int 0x80  ;interruption 
    ret 

我怎樣才能把這些代碼嵌入在我的代碼..所以我可以要求PID給用戶..這樣的.. 這是我的預編碼

void killp(int pid){ 
    asm ("mov %1, %%ebx;" 
      "mov 9, %%ecx;" 
      "mov 37, %%eax;" 
      : 
      : "a" (pid)   /* optional */ 
    ); 
} 
+0

你想傳遞一個輸出參數,以及str,len?,或者你的意思是你想發送一個文件描述符,你想寫str? – Zimbabao 2011-02-27 05:55:36

+0

我只是想傳遞我的字符串指針和我的字符串長度......就像它在程序集代碼中看起來一樣......所以,只使用系統調用,我可以將我的字符串打印到標準輸出。 – RodrigoCR 2011-02-27 06:07:22

回答

10

嗯,你沒有具體說,但通過你的文章,它看起來像你使用gcc和它的內聯asm與約束語法(其他C編譯器具有非常不同的內聯語法)。也就是說,您可能需要使用AT & T彙編語法,而不是Intel,因爲這是gcc所使用的。

所以和上面說的一樣,讓我們​​來看看你的write2函數。首先,你不想創建一個堆棧幀,因爲gcc會創建一個,所以如果你在asm代碼中創建一個,你最終會得到兩個幀,事情可能會非常困惑。其次,由於gcc正在佈置堆棧框架,因此您不能使用「[ebp + offset]」廣告訪問變量,您不知道它是如何佈局的。這就是約束條件 - 你說什麼樣的地方你希望gcc把值(任何寄存器,內存,特定的寄存器)和在代碼中使用「%X」。最後,如果你在asm代碼中使用顯式寄存器,你需要在第三部分中列出它們(在輸入約束之後),以便gcc知道你正在使用它們。否則,它可能會在其中一個寄存器中放置一些重要的值,並且會打破該值。

因此,所有的是,你寫2函數看起來像:

void write2(char *str, int len) { 
    __asm__ volatile (
     "movl $4, %%eax;" 
     "movl $1, %%ebx;" 
     "movl %0, %%ecx;" 
     "movl %1, %%edx;" 
     "int $0x80" 
     :: "g" (str), "g" (len) 
     : "eax", "ebx", "ecx", "edx"); 
} 

注意AT & T語法 - SRC,DEST而非DEST,寄存器名前src和%

現在,這將工作,但效率低下,因爲它將包含大量額外的mov。一般來說,你絕不應該在asm代碼中使用mov指令或顯式寄存器,因爲使用約束條件來說明你想要的東西的位置並讓編譯器確保它們在那裏更好。這樣,優化器可能會擺脫大部分movs,特別是如果它內聯函數(如果指定-O3,它將執行此操作)。方便的是,i386的機型有特定的寄存器約束,這樣你就可以代替做:

void write2(char *str, int len) { 
    __asm__ volatile (
     "movl $4, %%eax;" 
     "movl $1, %%ebx;" 
     "int $0x80" 
     :: "c" (str), /* c constraint tells the compiler to put str in ecx */ 
      "d" (len) /* d constraint tells the compiler to put len in edx */ 
     : "eax", "ebx"); 
} 

甚至更​​好

void write2(char *str, int len) { 
    __asm__ volatile ("int $0x80" 
     :: "a" (4), "b" (1), "c" (str), "d" (len)); 
} 

還請注意這是需要使用volatile告訴編譯器,這即使沒有使用它的輸出(其中沒有),也不能被淘汰。

編輯

最後一個音符 - 這個功能是做一個寫系統調用,這並在EAX返回值 - 寫入的字節數或錯誤代碼。所以,你可以得到與輸出的制約條件:

int write2(char *str, int len) { 
    __asm__ ("int $0x80" : "=a" (len) : "a" (4), "b" (1), "c" (str), "d" (len)); 
    return len; 
} 

與真正的輸出,你可能會或可能不希望揮發 - 沒有它可以讓編譯器死代碼消除寫如果返回值未使用。但是你總是檢查返回值的錯誤,對不對?

+1

轉換爲AT&T時,你錯過了一件事:常量需要一個$在他們面前。否則,它們是內存引用,我敢肯定你不想執行地址爲0x80的任何中斷。 – ughoavgfhw 2011-02-27 06:44:44

+0

非常感謝那個很有幫助的答案。我意識到AT&T的sintax,所以我修改了我的代碼......但是你看不到:P,altought現在我理解優化了...所以,我需要把int $ 0x80正確嗎? – RodrigoCR 2011-02-27 06:47:36

+0

void write2(char * str,int len){ __asm__ volatile(「int $ 0x80」 ::「a」(4),「b」(1),「c」(str),「d」(len )); } 注意:這是正確的答案!交換數值並使用$。謝謝你們兩位! – RodrigoCR 2011-02-27 07:19:04