2016-03-27 270 views
0

我最近進入了gcc的內聯彙編,並具有基本的彙編知識,我理解如何使系統調用相當好,直到我試圖用一個或多個參數執行一個簡單的sys_execve。 如果我沒有傳遞任何附加參數,系統調用execve可以正常工作,並且在嘗試傳遞任何參數時僅運行不帶參數的可執行文件。執行內聯彙編

#include <stdio.h> 

char *argv[]={"/bin/echo","parameter test", NULL}; 

int main(){ 
    __asm__ volatile ("int $0x80" 
      : 
      :"a"(11), // syscall number (execve) 
      "b"(argv[0]), // filename 
      "c"(argv), // arguments 
      "d"(0)); // env 
    return 0; 
} 

我不知道有什麼可以去錯了,因爲我已經測試這與

execve(argv[0], argv, NULL); 

和它的工作如預期。

+2

查看[sys_execve](http://docs.cs.up.ac.za/programming/asm/derick_tut/syscalls.html)的api,我不確定你的參數是否正確。另外,你正在編譯x64?如果是這樣,你不應該使用int 0x80。 –

+0

我正在編譯x64,並且根據我當前的進度,文件名部分是正確的,因爲我嘗試執行的任何可執行文件都可以正常工作。我堅持的部分是給它任何參數。我用戶[this](http://syscalls.kernelgrok.com/)來確定我必須通過。 – LazyShpee

回答

4

這是32位代碼,使用32位約定。編譯使用gcc -m32它會工作。可替換地,切換到合適的64位的版本,例如:

#include <stdio.h> 

char *argv[]={"/bin/echo","parameter test", NULL}; 

int main(){ 
    int ret; 
    __asm__ volatile ("syscall" 
      :"=a" (ret) 
      :"a"(59), // syscall number (execve) 
      "D"(argv[0]), // filename 
      "S"(argv), // arguments 
      "d"(0) // env 
      :"rcx","r11","cc"); 
    return 0; 
} 

的實際問題是,你必須在陣列中的64位的指針,但32位兼容中斷你使用當然預計,32個poiters。

+2

'= a'值得明確指出:系統調用在'eax'(或'rax')寄存器中返回一個值,因此編譯器需要知道發生了這種情況。否則,它會假設asm代碼沒有修改'eax',並且如果代碼後面需要值59,它可能會嘗試從'eax'加載它。這會導致問題,因爲實際上'eax'已被系統調用的返回值覆蓋。這個代碼可能不太可能導致問題,但一般來說要注意。 –

+2

順便說一下,從我讀到的amd64 abi中,系統調用可能會破壞'r11'以及'rcx'。 –

+1

@NateEldredge:這是正確的,'syscall' /'sysret'的設計保證'syscall'總是會打破rcx和r11(使用RIP和RFLAGS)。讓內核甚至可以看到這些註冊表中的內容將需要用戶空間的合作(例如將它們放在堆棧上)。 Linux系統調用不會查看用戶空間堆棧,所以ABI只是簡單地保留它,並且系統調用在amd64上打開這兩個regs,不像32bit'int 0x80',只有'eax'被修改(返回值)。 –