Параметры командной строки Linux 64 в сборке - PullRequest
6 голосов
/ 10 сентября 2010

Это описание действительно для 32-разрядной ОС Linux: при запуске программы 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 ...
...
}

Это похоже на параметрынаходятся в стеке.Поскольку этот код не понятен, я задаю этот вопрос.Мое предположение, что я могу сохранить rsp в rbp, а затем получить доступ к этим параметрам, используя 0 (% rbp), 8 (% rbp), 16 (% rbp) и т. Д. Это правильно?

Ответы [ 3 ]

9 голосов
/ 10 сентября 2010

Это похоже на раздел 3.4 Инициализация процесса , и, в частности, рисунок 3.9, в уже упомянутой System V AMD64 ABI точно описывает то, что вы хотите знать.

8 голосов
/ 02 июля 2016

Несмотря на то, что принятого ответа более чем достаточно, я хотел бы дать четкий ответ, поскольку есть и другие ответы, которые могут запутать.

Самое важное (дополнительную информацию см. В примерах ниже): в 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, и кодом, который этого не делает.

1 голос
/ 10 сентября 2010

Я верю, что вам нужно проверить x86-64 ABI .В частности, я думаю, вам нужно взглянуть на раздел 3.2.3 Передача параметров.

...