2013-05-07 24 views
5

Sto imparando a fare di nuovo il linguaggio assembly, e l'unico problema che ho avuto fino ad ora è stato fare chiamate a C. Il libro che ho è orientato a 32 bit, e sto lavorando a 64 bit. Apparentemente c'è una grande differenza nelle convenzioni di chiamata e il sito http://www.x86-64.org/documentation non funziona. Quindi, dopo alcuni scavi/test, compilando programmi fittizi in C e trascorrendo 3 giorni su questo, ho pensato di pubblicare le mie scoperte se aiuta qualcun altro.Come vengono passati i parametri quando si chiama Printf da 64 bit asm?

RAX deve ricevere il conteggio del float? Lo stack padding è "spazio d'ombra" 16 o 32 bit? Questa macro per allineare lo stack è passabile per i programmi piccoli? So che puoi NOP-pad il codice con l'allineamento, non ero sicuro del frame dello stack.

; pf.asm compiled with 'nasm -o pf.o -f elf64 -g -F stabs' 
; linked with 'gcc -o pf pf.o' 
; 64-bit Bodhi (ubuntu) linux 

%include "amd64_abi.mac" 
[SECTION .data] 
First_string: db "First string.",10,"%s", "%d is an integer. So is %d",10 
       db "Floats XMM0:%5.7f XMM1:%.6le XMM2:%lg",10,0 
Second_String: db "This is the second string... %s's are not interpreted here.",10 
       db " Neither are %d's nor %f's. 'Cause it is a passed value.", 10, 0 
; Just a regular string for insert. 
[SECTION .bss] 
[SECTION .text] 
EXTERN printf 
GLOBAL main 
main: 
_preserve_64AMD_ABI_regs ; Saves RBP, RBX, R12-R15 
mov rdi, First_string ; Start of string to be formatted. Null terminated 
mov rsi, Second_String ; String addy of first %s in main string. Not interpretted 
mov rcx, 0456   ; Second Integer (Register is specific for ordered arguments.) 
mov rdx,; First integer (Order of assignment does not matter.) 
         ; Order of Integer/Pointer Registers: 
         ; $1:RDI $2:RSI $3:RDX $4:RCX $5:R8 $6:R9 

mov rax,0AABBCCh   ; Test value to be stored in xmm0 
cvtsi2sd xmm0, rax  ; Convert quad to scalar double 
mov rax,003333h   ; Test value to be stored in xmm1 
cvtsi2sd xmm1, rax  ; Convert quad to scalar double 
cvtsi2sd xmm2, rax  ; Convert quad to scalar double 
divsd xmm2, xmm0  ; Divide scalar double 

sub rsp, 16    ; Allocates 16 byte shadow memory 
_prealign_stack_to16 ; Move to the lower end 16byte boundry (Seg-Fault otherwise) 
; mov rax, 3    ; Count of xmm registers used for floats. ?!needed?! 
Before_Call: 
call printf    ; Send the formatted string to C-printf 
_return_aligned_stack ; Returns RSP to the previous alignment 
add rsp, 16    ; reallocate shadow memory 

_restore_64AMD_ABI_regs_RET 
; Ends pf.asm 

; amd64_abi.mac 
; Aligns stack (RSP) to 16 byte boundry, padding needed amount in rbx 
%macro _preserve_64AMD_ABI_regs 0 
push rbp 
mov rbp, rsp 
push rbx 
push r12 
push r13 
push r14 
push r15 
%endmacro 

%macro _restore_64AMD_ABI_regs_RET 0 
pop r15 
pop r14 
pop r13 
pop r12 
pop rbx 
mov rsp, rbp 
pop rbp 
ret 
%endmacro 

%macro _prealign_stack_to16 0 
mov rbx, 0Fh   ; Bit mask for low 4-bits 10000b = 16 :: 01111b = 15b 
and rbx, rsp   ; get bits 0-3 into rbx 
sub rsp, rbx   ; remove them from rsp, rounding down to multiple of 16 (10h) 
%endmacro 

; De-aligns stack (RSP)from 16 byte boundry using saved rbx offset 
%macro _return_aligned_stack 0 
add rsp, rbx 
%endmacro 

USCITA: Prima stringa. Questa è la seconda stringa ...% s non sono interpretate qui. Né sono% d's né% f's. Perché è un valore passato. 123 è un numero intero. Così è 456 Galleggianti XMM0: 11.189.196,0 milioni XMM1: 1.310700e + 04 XMM2: 0,0011714

Risorse: System V ABI v0.96: http://www.uclibc.org/docs/psABI-x86_64.pdf (non è disponibile a x86-64.org sito è giù) linguaggio Assembler Passo dopo passo. Jeff Duntemann Capitolo 12 Set di istruzioni Intel a 64 bit. http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html

+2

L'approccio più ovvio è quello di scrivere il codice in C prima e avere un compilatore C generare l'assembly. Non sarà mai sbagliato. –

risposta

6

Sì, RAX (in realtà AL) deve contenere il numero di XMM registri utilizzati.

Il codice di allineamento dello stack è eccessivamente complicato, normalmente è sufficiente eseguire AND rsp, -16. Inoltre, l'allineamento dello stack viene in genere eseguito una sola volta (di solito all'inizio di main) e quindi viene mantenuto regolando sempre rsp in modo appropriato.

SYSV ABI non utilizza lo shadow space (ovvero la convenzione microsoft) invece utilizza una "zona rossa", ma ciò non influisce sulla sequenza chiamante.

Aggiornamento circa l'allineamento dello stack:

nelle funzioni che già ottenere allineati RSP (in genere tutto tranne main), basta assicurarsi che eventuali funzioni chiamate a loro volta ottenere RSP che è cambiata di un multiplo di 16.

Se si utilizza un puntatore a frame standard, le funzioni iniziano con PUSH RBP, quindi è necessario assicurarsi di allocare spazio in multipli di 16 (se necessario), ad esempio:

push rbp 
mov rbp, rsp 
sub rsp, n*16 
... 
mov rsp, rbp 
pop rbp 
ret 

In caso contrario, si dovrà compensare le 8 byte di RIP messa in pila (come lei ha giustamente che nel tuo commento):

sub rsp, n*16+8 
... 
add rsp, n*16+8 
ret 

Sia di quanto sopra si applicano solo se si chiama altre funzioni, cioè nelle funzioni foglia, puoi fare quello che vuoi.Inoltre, la zona rossa ho detto prima è utile nelle funzioni di foglie, perché è possibile utilizzare 128 byte sotto lo stack pointer, senza attribuzione esplicita, il che significa non essere necessario regolare RSP affatto:

; in leaf functions you can use memory under the stack pointer 
; (128 byte red zone) 
mov [rsp-8], rax 
+0

Bello, questo è molto utile. Quindi, ogni volta che spingo dopo l'allineamento iniziale, fallo come; sub rsp, 10h; mov [rsp], $ value ... quindi dopo la chiamata è sufficiente aggiungerlo di nuovo? – DouglasCodes

+0

Tutte le chiamate saranno allineate a 16 prima della chiamata giusta? Quindi la chiamata spinge RIP in pila. Lasciando che debba essere regolato per 8 ... così posso AND 'rsp, -16' all'inizio e' aggiungi rsp, 8' prima del RET. Corretta? – DouglasCodes

+0

Aggiornamento della risposta. – Jester