2010-09-10 93 views
5

此說明適用於Linux 32位: 當Linux程序開始時,所有指向命令行參數的指針都存儲在堆棧中。參數的數量存儲在0(%ebp),程序的名稱存儲在4(%ebp),並且參數從8(%ebp)存儲。彙編中的Linux 64命令行參數

我需要64位的相同信息。

編輯: 我已經工作的代碼示例,其顯示瞭如何使用的argc,argv的[0]和argv [1]:http://cubbi.com/fibonacci/asm.html

 
.globl _start 
_start: 
    popq %rcx  # this is argc, must be 2 for one argument 
    cmpq $2,%rcx 
    jne  usage_exit 
    addq $8,%rsp  # skip argv[0] 
    popq %rsi  # get argv[1] 
    call ... 
... 
} 

它看起來像參數是在堆棧上。由於這個代碼不清楚,我問這個問題。我的猜測是我可以在rbp中保存rsp,然後使用0(%rbp),8(%rbp),16(%rbp)等訪問這些參數。

回答

9

看起來像3.4節過程初始化,特別是圖3.9,在已經提到的System V AMD64 ABI中正好描述了你想知道的內容。

+2

鏈接只有答案是脆弱的。該頁面不適合我。 * www.x86-64.org沒有發送任何數據。 ERR_EMPTY_RESPONSE * – doug65536 2017-10-30 10:26:40

1

我相信你需要做的是檢查出x86-64 ABI。具體來說,我認爲你需要看3.2.3參數傳遞。

+2

這描述了程序內部的調用約定。處理命令行參數可能有自己的規則。我需要這個Linux 64的具體信息。 – 2010-09-10 08:37:11

8

儘管接受的答案綽綽有餘,但我想給出一個明確的答案,因爲還有其他一些可能會混淆的答案。

最重要的(更多信息,請參見下面的實施例):在x86-64的命令行參數通過堆棧傳遞:

(%rsp) -> number of arguments 
8(%rsp) -> address of the name of the executable 
16(%rsp) -> address of the first command line argument (if exists) 
... so on ... 

它是從通過在X86-64函數參數不同,它使用%rdi%rsi等。

還有一件事:不應該從C main功能的反向工程中推斷出行爲。 C運行時提供入口點_start,將命令行參數包裝並調用main作爲常用函數。要看到它,我們來考慮下面的例子。

與-nostdlib

沒有C運行時/ GCC讓我們來看看這個簡單的x86-64彙編程序,它什麼也不做,但返回42:

.section .text 
.globl _start 
_start: 
    movq $60, %rax #60 -> exit 
    movq $42, %rdi #return 42 
    syscall #run kernel 

我們與它建立

as --64 exit64.s -o exit64.o 
ld -m elf_x86_64 exit64.o -o exit64 

gcc -nostdlib exit64.s -o exit64 

運行在gdb與

./exit64 first second third 

,並停止在斷點_start。讓我們檢查一下寄存器:

(gdb) info registers 
... 
rsi   0x0 0 
rdi   0x0 0 
... 

沒有。那堆棧呢?

(gdb) x/5g $sp 
0x7fffffffde40: 4 140737488347650 
0x7fffffffde50: 140737488347711 140737488347717 
0x7fffffffde60: 140737488347724 

所以堆棧中的第一個元素是4 - 預期argc。接下來的4個值看起來很像指針。我們來看第二個指針:

(gdb) print (char[5])*(140737488347711) 
$1 = "first" 

正如所料,它是第一個命令行參數。

所以有實驗證據表明命令行參數是通過x86-64中的堆棧傳遞的。但是,只有閱讀ABI(正如接受的答案所暗示的那樣),我們可以肯定的是,情況確實如此。

隨着C運行時

我們必須稍微改變一下程序,重命名_startmain,因爲入口點_start是由C運行時提供。

.section .text 
.globl main 
main: 
    movq $60, %rax #60 -> exit 
    movq $42, %rdi #return 42 
    syscall #run kernel 

我們與(C運行時的缺省設置是使用)構建它:用

./exit64gcc first second third 

在gdb

gcc exit64gcc.s -o exit64gcc 

運行在該斷點處停止main。什麼是堆棧?

(gdb) x/5g $sp 
0x7fffffffdd58: 0x00007ffff7a36f45 0x0000000000000000 
0x7fffffffdd68: 0x00007fffffffde38 0x0000000400000000 
0x7fffffffdd78: 0x00000000004004ed 

它看起來並不熟悉。並註冊?

(gdb) info registers 
... 
rsi   0x7fffffffde38 140737488346680 
rdi   0x4 4 
... 

我們可以看到,rdi包含argc值。但是,如果我們現在rsi奇怪的事情檢查指針發生:

(gdb) print (char[5])*($rsi) 
$1 = "\211\307???" 

別急,在C main函數的第二個參數不是char *,但char **也:

(gdb) print (unsigned long long [4])*($rsi) 
$8 = {140737488347644, 140737488347708, 140737488347714, 140737488347721} 
(gdb) print (char[5])*(140737488347708) 
$9 = "first" 

現在我們找到了我們的參數,這些參數是通過寄存器傳遞的,正如x86-64中的正常函數一樣。

結論: 正如我們所看到的,是用C運行時和代碼不涉及的命令行參數傳遞代碼之間的差。

+1

您可以構建一個定義'_start'的程序[使用'gcc -nostdlib'](http://stackoverflow.com/questions/36861903/assembling-32-bit-binaries-on-a-64-bit-系統-GNU工具鏈/ 36901649#36901649)。如果只定義'main',而不是'_start',則必須使用gcc,但是定義'_start'並不會阻止您使用gcc。 – 2016-07-01 23:10:17

+0

查看寄存器/內存是瞭解* *可能*如何工作的一種好方法,即找到某些東西去搜索官方文檔。儘管如此,你不應該假設你找到的任何東西都保證在那裏。您不希望依賴某些實際上只是剩餘副本的內容,並且可能不在將來的版本中。你的結論是正確的,但是繼續參考SysV ABI如何保證這些值會在那裏會好得多。 – 2016-07-01 23:13:22

+0

你也沒有提到提供'_start'並調用你的'main'的glibc CRT代碼。定義'main'或'_start'沒有什麼神奇的區別;不難理解啓動樣板代碼如何收集流程啓動狀態並用這些函數參數調用'main'。 – 2016-07-01 23:15:19