Mimo że zaakceptowana odpowiedź jest więcej niż wystarczająca, chciałbym udzielić jednoznacznej odpowiedzi, ponieważ istnieją inne odpowiedzi, które mogą wprowadzać w błąd.
Najważniejszym (więcej informacji patrz przykłady poniżej): w x86-64 argumenty wiersza polecenia są przekazywane poprzez stos:
(%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 ...
To różni się od parametru funkcji przechodzącą w x86-64, która wykorzystuje %rdi
, %rsi
i tak dalej.
Jeszcze jedno: nie należy wnioskować o zachowaniu z inżynierii wstecznej funkcji C main
. Środowisko wykonawcze C zapewnia punkt wejścia _start
, opakowuje argumenty wiersza poleceń i wywołuje main
jako wspólną funkcję. Aby to zobaczyć, rozważmy następujący przykład.
C Runtime/GCC z -nostdlib
Sprawdźmy to prosty program, który asemblera x86-64 zrobić nic, ale zwraca 42:
.section .text
.globl _start
_start:
movq $60, %rax #60 -> exit
movq $42, %rdi #return 42
syscall #run kernel
Budujemy go z:
as --64 exit64.s -o exit64.o
ld -m elf_x86_64 exit64.o -o exit64
lub z
gcc -nostdlib exit64.s -o exit64
prowadzony w gdb z
./exit64 first second third
i zatrzymać przy zerwaniu w _start
. Sprawdźmy rejestry:
(gdb) info registers
...
rsi 0x0 0
rdi 0x0 0
...
Nic tam nie ma. A co ze stosem?
(gdb) x/5g $sp
0x7fffffffde40: 4 140737488347650
0x7fffffffde50: 140737488347711 140737488347717
0x7fffffffde60: 140737488347724
więc pierwszy element na stosie jest 4
- przewidywany argc
. Następne 4 wartości wyglądają bardzo podobnie do wskaźników. Spójrzmy na drugi wskaźnik:
(gdb) print (char[5])*(140737488347711)
$1 = "first"
Zgodnie z oczekiwaniami jest to pierwszy argument linii poleceń.
Mamy więc dowody eksperymentalne, że argumenty wiersza poleceń przekazywane są za pomocą stosu w x86-64. Jednak tylko przez przeczytanie ABI (jak sugeruje przyjęta odpowiedź) możemy być pewni, że tak jest naprawdę.
zc wykonywania
Musimy zmienić program nieznacznie, zmienianie nazw _start
do main
, ponieważ punkt wejścia _start
jest przez C starcie.
.section .text
.globl main
main:
movq $60, %rax #60 -> exit
movq $42, %rdi #return 42
syscall #run kernel
Budujemy go (C Runtime jest używany domyślnie):
gcc exit64gcc.s -o exit64gcc
bieg w gdb z
./exit64gcc first second third
i zatrzymać przy zerwaniu w main
. Co jest na stosie?
(gdb) x/5g $sp
0x7fffffffdd58: 0x00007ffff7a36f45 0x0000000000000000
0x7fffffffdd68: 0x00007fffffffde38 0x0000000400000000
0x7fffffffdd78: 0x00000000004004ed
To nie wygląda znajomo. I rejestry?
(gdb) info registers
...
rsi 0x7fffffffde38 140737488346680
rdi 0x4 4
...
Widzimy, że rdi
zawiera wartość argc
. Ale jeśli teraz skontrolować wskaźnik w rsi
dzieją się dziwne rzeczy:
(gdb) print (char[5])*($rsi)
$1 = "\211\307???"
Ale poczekaj, drugi argument funkcji w C main
nie jest char *
, ale char **
również:
(gdb) print (unsigned long long [4])*($rsi)
$8 = {140737488347644, 140737488347708, 140737488347714, 140737488347721}
(gdb) print (char[5])*(140737488347708)
$9 = "first"
A teraz my znaleźliśmy nasze argumenty, które są przekazywane za pośrednictwem rejestrów, tak jak byłoby to w przypadku normalnej funkcji w x86-64.
Wniosek: Jak widać, różnica dotycząca przekazywanie argumentów wiersza poleceń między kodu przy użyciu C czas pracy i kod, który nie.
Odpowiedzi dotyczące wyłącznie linków są delikatne. Ta strona nie działa dla mnie. * www.x86-64.org nie wysłał żadnych danych. ERR_EMPTY_RESPONSE * – doug65536