Несмотря на то, что принятого ответа более чем достаточно, я хотел бы дать четкий ответ, поскольку есть и другие ответы, которые могут запутать.
Самое важное (дополнительную информацию см. В примерах ниже): в 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 runtime
Мы должны немного изменить программу, переименовав _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???"
Но подождите, второй аргумент функции main
в C - это не char *
, но char **
также:
(gdb) print (unsigned long long [4])*($rsi)
$8 = {140737488347644, 140737488347708, 140737488347714, 140737488347721}
(gdb) print (char[5])*(140737488347708)
$9 = "first"
И теперь мы нашли наши аргументы, которые передаются через регистры, как это было бы для нормальной функции в x86-64.
Вывод:
Как мы видим, есть разница в передаче аргументов командной строки между кодом, использующим среду выполнения C, и кодом, который этого не делает.