assembly - ¿Cómo funciona $ funciona exactamente en NASM?
x86 (1)
message db "Enter a digit ", 0xA,0xD
Length equ $- message
¿Se usa para obtener la longitud de una cuerda?
¿Cómo funciona internamente?
$
es la dirección de la posición actual
antes de
emitir los bytes (si los hay) para la línea en la que aparece.
La Sección 3.5 del manual
no entra en muchos detalles.
$ - msg
es como hacer
here - msg
, es decir, la distancia en bytes entre la posición actual (al final de la cadena) y el inicio de la cadena.
(
Consulte también este tutorial
sobre etiquetas y directivas NASM como
resb
)
(Relacionado: la mayoría de los otros ensambladores x86 también usan
$
la misma manera, excepto para GAS que usa
.
(
Punto
). El
ensamblador MMIX
usa
@
, que tiene el significado semántico correcto).
Para comprenderlo mejor, puede ser útil ver qué sucede cuando se equivoca: .com/questions/26897633/… . Esta persona usó
HELLO_MSG db ''Hello, World!'',0
GOODBYE_MSG db ''Goodbye!'',0
hlen equ $ - HELLO_MSG
glen equ $ - GOODBYE_MSG
resultando en
hlen
incluyendo la longitud de ambas cuerdas.
EQU
evalúa el lado derecho de inmediato, a un valor constante.
(En algunos ensambladores como FASM,
equ
es una sustitución de texto y debe usar
glen = $ - GOODBYE_MSG
para evaluar con
$
en esta posición, en lugar de evaluar
$
en una
mov ecx, glen
posterior
mov ecx, glen
instrucción
mov ecx, glen
o algo así. Pero el NASM evalúa en el lugar; use
%define
para sustituciones de texto)
Usar
$
es exactamente equivalente a poner una etiqueta al comienzo de la línea y usarla en lugar de
$
.
El ejemplo del tamaño del objeto también se puede hacer usando etiquetas regulares:
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
Puede poner
Length equ msgend - msg
cualquier lugar, o
mov ecx, msgend - msg
directamente.
(A veces es útil tener una etiqueta al final de algo, por ejemplo,
cmp rsi, msgend
/
jb .loop
en la parte inferior de un bucle.
Por cierto, generalmente es CR LF, no LF CR.
Ejemplos menos obvios:
times 4 dd $
se ensambla de la misma manera (pero sin crear una entrada en la tabla de símbolos o entrar en conflicto con un nombre existente):
here: times 4 dd here
En
times 4 dd $
,
$
no se actualiza a su propia dirección para cada dword, sigue siendo la dirección del inicio de la línea.
(Pruébelo en un archivo por sí mismo y reduzca el binario plano: es todo ceros).
Pero un bloque
%rep
se expande antes de
$
, entonces
%rep 4
dd $
%endrep
produce 0, 4, 8, 12 (a partir de una posición de salida de
0
en un binario plano para este ejemplo).
$ nasm -o foo rep.asm && hd foo
00000000 00 00 00 00 04 00 00 00 08 00 00 00 0c 00 00 00
Codificación manual de desplazamientos de salto:
Una
call
directa normal
es
E8 rel32
, con el desplazamiento calculado en relación con el
final
de la instrucción.
(es decir, en relación con EIP / RIP mientras la instrucción se está ejecutando, porque RIP contiene la dirección de la siguiente instrucción. Los modos de direccionamiento relativos a RIP también funcionan de esta manera). operando, la dirección del final es
$+4
.
Por supuesto, podría poner una etiqueta en la
siguiente
línea y 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
Salida de desmontaje (desde
objdump -drwC -Mintel
):
0000000000400080 <earlyfunc>:
400080: e8 34 00 00 00 call 4000b9 <func> # encoded by NASM
400085: e8 2f 00 00 00 call 4000b9 <func> # encoded manually
40008a: e8 f1 ff ff ff call 400080 <earlyfunc> # and backwards works too.
Si obtiene el desplazamiento incorrecto, objdump colocará la parte simbólica como
func+8
, por ejemplo.
El desplazamiento relativo en las primeras 2 instrucciones de llamada difiere en 5 porque la
call rel32
tiene una longitud de 5 bytes y tienen el mismo destino real,
no
el mismo desplazamiento relativo.
Tenga en cuenta que el desensamblador se encarga de agregar el rel32 a la dirección de las instrucciones de llamada para mostrarle las direcciones de destino absolutas.
Puede usar
db target - ($+1)
para codificar el desplazamiento para un corto
jmp
o
jmp
.
(Pero tenga cuidado:
db 0xEB, target - ($+1)
no está bien, porque el final de la instrucción es en realidad
$+2
cuando coloca tanto el código de operación como el desplazamiento como múltiples argumentos para la misma pseudoinstrucción de
db
).
Relacionado:
$$
es el comienzo de la
sección
actual
, por lo que
$ - $$
es qué tan lejos de la sección actual se encuentra.
Pero esto solo está dentro del archivo actual, por lo que vincular dos archivos que ponen cosas en
.rodata
es diferente de tener bloques de dos
section .rodata
en el mismo archivo fuente.
Vea
¿Cuál es el verdadero significado de $$ en nasm
?
Con mucho, el uso más común es
times 510-($-$$) db 0
/
dw 0xAA55
para rellenar (con
db 0
) un sector de arranque a 510 bytes, y luego agregar la firma del sector de arranque para obtener 512 bytes.
(
El manual NASM explica cómo funciona esto
)