2010-09-10 18 views
5

Opis ten dotyczy systemu Linux 32-bitowego: Po uruchomieniu programu Linux wszystkie wskaźniki argumentów wiersza polecenia są przechowywane na stosie. Liczba argumentów jest zapisywana jako 0 (% ebp), nazwa programu jest zapisywana jako 4 (% ebp), a argumenty są przechowywane od 8 (% ebp).Parametry wiersza polecenia dla systemu Linux 64 w zespole

Potrzebuję tych samych informacji dla 64-bitowych.

Edit: mam pracę przykładowy kod, który pokazuje, jak używać argc, argv [0] i 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 ... 
... 
} 

Wygląda na to parametry są na stosie. Ponieważ ten kod nie jest jasny, zadaję to pytanie. Zgaduję, że mogę zachować rsp w rbp, a następnie uzyskać dostęp do tych parametrów za pomocą 0 (% rbp), 8 (% rbp), 16 (% rbp) itd. Czy to prawda?

Odpowiedz

9

Wygląda na to, że sekcja 3.4 Inicjalizacja procesu, aw szczególności rysunek 3.9, we wcześniej wspomnianym numerze System V AMD64 ABI opisuje dokładnie to, co chcesz wiedzieć.

+2

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

1

Jestem przekonany, że należy sprawdzić numer x86-64 ABI. W szczególności uważam, że należy zapoznać się z sekcją 3.2.3 Przekazywanie parametrów.

+2

Opisuje to wywoływanie konwencji wewnątrz programu. Parametry wiersza polecenia procesu mogą mieć własne reguły. Potrzebuję tych konkretnych informacji dla systemu Linux 64. –

8

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.

+1

Możesz zbudować program, który definiuje '_start' [używając' gcc -nostdlib'] (http://stackoverflow.com/questions/36861903/assembling-32-bit-binaries-on-a-64-bit- system-gnu-toolchain/36901649 # 36901649). Jeśli zdefiniujesz tylko 'main', a nie' _start', musisz użyć gcc, ale zdefiniowanie '_start' nie powstrzyma cię od używania gcc. –

+0

Przeglądanie rejestrów/pamięci to dobry sposób, aby dowiedzieć się, jak coś * może * działać, tj. Znaleźć coś do wyszukania oficjalnych dokumentów.Nie powinieneś po prostu zakładać, że wszystko, co znajdziesz, jest gwarantowane. Nie chcesz polegać na czymś, co w rzeczywistości jest tylko resztkową kopią, i może nie być w przyszłej wersji. Twoje wnioski są poprawne, ale byłoby znacznie lepiej przejść do tego, w jaki sposób SysV ABI gwarantuje, że wartości te będą tam obecne. –

+0

Nie wspomnisz również o kodzie CRT glibc, który zapewnia '_start' i nazywa twój' główny'. Nie ma żadnej magicznej różnicy między definiowaniem "main" lub "_start"; nie jest trudno zrozumieć, co robi kod startowy, aby zebrać stan uruchamiania procesu i wywołać 'main' z tymi argumentami funkcji. –

Powiązane problemy