簡體   English   中英

程序集中的Linux 64命令行參數

[英]Linux 64 command line parameters in Assembly

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

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 ...
...
}

看起來參數在堆棧上。 由於此代碼不清楚,我問這個問題。 我猜我可以將rp保留在rbp中,然后使用0(%rbp),8(%rbp),16(%rbp)等來訪問這些參數。這是正確的嗎?

盡管接受的答案綽綽有余,但我想給出一個明確的答案,因為還有一些其他答案可能會引起混淆。

最重要的(有關更多信息,請參見下面的示例):在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作為公共函數調用。 為了看到它,讓我們考慮以下示例。

沒有C運行時/ GCC與-nostdlib

讓我們檢查一下這個簡單的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運行時

我們必須稍微更改程序,將_start重命名為main ,因為入口點_start由C運行時提供。

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

我們用它構建它(默認情況下使用C運行時):

gcc exit64gcc.s -o exit64gcc

用gdb運行

./exit64gcc first second third

並停在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運行時的代碼和不執行C代碼的代碼之間傳遞命令行參數是有區別的。

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

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM