Come funziona $ in NASM, esattamente?

message db "Enter a digit ", 0xA,0xD Length equ $- message 

È usato per ottenere la lunghezza di una stringa?
Come funziona internamente?

$ è l’indirizzo della posizione corrente prima di emettere i byte (se ce ne sono) per la riga su cui appare. La sezione 3.5 del manuale non fornisce molti dettagli.

$ - msg è come fare here - msg , cioè la distanza in byte tra la posizione corrente (alla fine della stringa) e l’inizio della stringa. ( Vedi anche questo tutorial su etichette e direttive NASM come resb )

(Correlato: la maggior parte degli altri assemblatori x86 usa anche $ allo stesso modo, eccetto per GAS che usa . ( Punto ). L’assemblatore MMIX usa @ , che ha il giusto significato semantico).


Per capirlo meglio, può aiutare a vedere cosa succede quando si sbaglia: nelle etichette NASM l’una accanto all’altra in memoria causano problemi di stampa . Questa persona ha usato

 HELLO_MSG db 'Hello, World!',0 GOODBYE_MSG db 'Goodbye!',0 hlen equ $ - HELLO_MSG glen equ $ - GOODBYE_MSG 

risultando in hlen inclusa la lunghezza di entrambe le stringhe.

EQU valuta subito il lato destro, a un valore costante. (In alcuni assemblatori come FASM, equ è una sostituzione di testo e devi usare glen = $ - GOODBYE_MSG per valutare con $ in questa posizione, invece di valutare $ in un mov ecx, glen successivo mov ecx, glen istruzione mov ecx, glen o qualcosa del genere. lo spot; usa %define per le sostituzioni testuali)


Usare $ equivale esattamente a mettere un’etichetta all’inizio della riga e usarla invece di $ .

L’esempio della dimensione dell’object può anche essere eseguito utilizzando etichette regolari:

 msg: db "Enter a digit " msgend: Length equ msgend - msg Length2 equ $ - msg ; Length2 = Length newline: db 0xA,0xD Length3 equ $ - msg ; Length3 includes the \n\r LF CR sequence as well. ; sometimes that *is* what you want 

Puoi inserire Length equ msgend - msg ovunque, o mov ecx, msgend - msg direttamente. (A volte è utile avere un’etichetta alla fine di qualcosa, ad esempio cmp rsi, msgend / jb .loop nella parte inferiore di un ciclo.

A proposito, è di solito CR LF, non LF CR.


Esempi meno ovvi:

 times 4 dd $ 

assembla lo stesso (ma senza creare una voce nella tabella dei simboli o scontrandosi con un nome esistente):

 here: times 4 dd here 

A times 4 dd $ , $ non si aggiorna al proprio indirizzo per ogni dword, è sempre l’indirizzo dell’inizio della linea. (Provalo in un file da solo e hexdump del binario piatto: sono tutti zeri).


Ma un blocco %rep viene espanso prima di $ , quindi

 %rep 4 dd $ %endrep 

produce 0, 4, 8, 12 (partendo da una posizione di uscita di 0 in un binario piatto per questo esempio).

 $ nasm -o foo rep.asm && hd foo 00000000 00 00 00 00 04 00 00 00 08 00 00 00 0c 00 00 00 

Codifica manuale degli spostamenti di salto:

Una normale call diretta è E8 rel32 , con lo spostamento calcolato rispetto alla fine dell’istruzione. (cioè relativo a EIP / RIP mentre l’istruzione è in esecuzione, poiché RIP contiene l’indirizzo dell’istruzione successiva. Anche le modalità di indirizzamento relative al RIP funzionano in questo modo.) Una parola è 4 byte, quindi in una pseudo-istruzione dd con una operando, l’indirizzo della fine è $+4 . Ovviamente potresti semplicemente mettere un’etichetta sulla riga successiva e usarla.

 earlyfunc: ; before the call call func ; let NASM calculate the offset db 0xE8 dd func - ($ + 4) ; or do it ourselves db 0xE8 dd earlyfunc - ($ + 4) ; and it still works for negative offsets ... func: ; after the call 

output di disassemblaggio (da objdump -drwC -Mintel ):

 0000000000400080 : 400080: e8 34 00 00 00 call 4000b9  # encoded by NASM 400085: e8 2f 00 00 00 call 4000b9  # encoded manually 40008a: e8 f1 ff ff ff call 400080  # and backwards works too. 

Se ottieni l’offset sbagliato, objdump inserirà la parte simbolica come func+8 , ad esempio. Lo spostamento relativo nelle prime 2 istruzioni di chiamata differisce di 5 perché la call rel32 è lunga 5 byte e hanno la stessa destinazione effettiva, non lo stesso spostamento relativo. Si noti che il disassemblatore si occupa di aggiungere il rel32 all’indirizzo delle istruzioni di chiamata per mostrare gli indirizzi di destinazione assoluti.

È ansible utilizzare db target - ($+1) per codificare l’offset per un breve jmp o jcc . (Ma attenzione: db 0xEB, target - ($+1) non è corretto, perché la fine dell’istruzione è in realtà $+2 quando si mettono sia l’opcode sia lo spostamento come più argomenti per la stessa pseudo-istruzione db .)


Correlato: $$ è l’inizio della sezione corrente , quindi $ - $$ indica la distanza nella sezione corrente. Ma questo è solo all’interno del file corrente, quindi il collegamento di due file che inseriscono elementi in .rodata è diverso dall’avere due blocchi section .rodata nello stesso file sorgente. Vedi Qual è il vero significato di $$ in nasm .

Di gran lunga l’uso più comune è il times 510-($-$$) db 0 / dw 0xAA55 per il pad (con db 0 ) un settore di avvio fino a 510 byte, quindi aggiungere la firma del settore di avvio per creare 512 byte. ( Il manuale NASM spiega come funziona )