Mặc dù câu trả lời được chấp nhận là quá đủ, tôi muốn đưa ra câu trả lời rõ ràng, vì có một số câu trả lời khác có thể gây nhầm lẫn.
Quan trọng nhất (để biết thêm thông tin xem các ví dụ dưới đây): trong x86-64 các đối số dòng lệnh được truyền qua chồng:
(%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 ...
Đó là khác biệt so với các tham số chức năng đi qua trong x86-64, trong đó sử dụng %rdi
, %rsi
v.v.
Một điều nữa: không nên suy ra hành vi từ kỹ thuật đảo ngược của C main
-chức năng. Thời gian chạy C cung cấp điểm nhập _start
, kết thúc các đối số dòng lệnh và gọi main
làm chức năng chung. Để xem nó, hãy xem xét ví dụ sau.
Không C runtime/GCC với -nostdlib
Hãy kiểm tra chương trình x86-64 lắp ráp đơn giản này, mà không phải làm gì nhưng trả 42:
.section .text
.globl _start
_start:
movq $60, %rax #60 -> exit
movq $42, %rdi #return 42
syscall #run kernel
Chúng tôi xây dựng nó với:
as --64 exit64.s -o exit64.o
ld -m elf_x86_64 exit64.o -o exit64
hoặc với
gcc -nostdlib exit64.s -o exit64
chạy trong gdb với
./exit64 first second third
và dừng lại ở breakpoint tại _start
. Hãy kiểm tra sổ đăng ký:
(gdb) info registers
...
rsi 0x0 0
rdi 0x0 0
...
Không có gì ở đó. Điều gì về ngăn xếp?
(gdb) x/5g $sp
0x7fffffffde40: 4 140737488347650
0x7fffffffde50: 140737488347711 140737488347717
0x7fffffffde60: 140737488347724
Vì vậy, phần tử đầu tiên trên ngăn xếp là 4
- mong đợi argc
. 4 giá trị tiếp theo trông rất giống con trỏ. Hãy xem con trỏ thứ hai:
(gdb) print (char[5])*(140737488347711)
$1 = "first"
Như dự kiến, đây là đối số dòng lệnh đầu tiên.
Vì vậy, có bằng chứng thực nghiệm, rằng các đối số dòng lệnh được chuyển qua ngăn xếp trong x86-64. Tuy nhiên chỉ bằng cách đọc các ABI (như câu trả lời được chấp nhận đề nghị) chúng tôi có thể chắc chắn, rằng đây thực sự là trường hợp.
Với C runtime
Chúng ta phải thay đổi chương trình một chút, đổi tên _start
vào main
, bởi vì điểm vào _start
được cung cấp bởi các runtime C.
.section .text
.globl main
main:
movq $60, %rax #60 -> exit
movq $42, %rdi #return 42
syscall #run kernel
Chúng tôi xây dựng nó với (C runtime được sử dụng mỗi mặc định):
gcc exit64gcc.s -o exit64gcc
chạy trong gdb với
./exit64gcc first second third
và dừng lại ở breakpoint tại main
. Có gì ở ngăn xếp?
(gdb) x/5g $sp
0x7fffffffdd58: 0x00007ffff7a36f45 0x0000000000000000
0x7fffffffdd68: 0x00007fffffffde38 0x0000000400000000
0x7fffffffdd78: 0x00000000004004ed
Nó trông không quen thuộc. Và đăng ký?
(gdb) info registers
...
rsi 0x7fffffffde38 140737488346680
rdi 0x4 4
...
Chúng tôi có thể thấy rằng rdi
chứa giá trị argc
. Nhưng nếu bây giờ chúng ta kiểm tra con trỏ trong rsi
những điều kỳ lạ xảy ra:
(gdb) print (char[5])*($rsi)
$1 = "\211\307???"
Nhưng chờ đợi, đối số thứ hai của main
hàm trong C không phải là char *
, nhưng char **
thêm:
(gdb) print (unsigned long long [4])*($rsi)
$8 = {140737488347644, 140737488347708, 140737488347714, 140737488347721}
(gdb) print (char[5])*(140737488347708)
$9 = "first"
Và bây giờ chúng tôi tìm thấy các đối số của chúng tôi, được thông qua qua sổ đăng ký vì nó sẽ là một hàm bình thường trong x86-64.
Kết luận: Như chúng ta có thể thấy, sự khác biệt liên quan đến việc truyền các đối số dòng lệnh giữa mã sử dụng thời gian chạy C và mã không có.
Chỉ liên kết câu trả lời là mong manh. Trang đó không hiệu quả với tôi. * www.x86-64.org không gửi bất kỳ dữ liệu nào. ERR_EMPTY_RESPONSE * – doug65536