syscall - ¿Cuál es la diferencia entre equ y db en NASM?
nasm x86 (3)
len: equ 2
len: db 2
¿Son lo mismo, produciendo una etiqueta que puede usarse en lugar de 2
? Si no, ¿cuál es la ventaja o desventaja de cada formulario de declaración? ¿Se pueden usar indistintamente?
La primera es igual, similar a la de C:
#define len 2
en el sentido de que en realidad no asigna ningún espacio en el código final, simplemente establece que el símbolo de len
sea igual a 2. Luego, cuando usa len
más adelante en su código fuente, es lo mismo que si estuviera usando el constante 2
.
El segundo es define byte
, similar a C:
int len = 2;
En realidad, asigna espacio, un byte en la memoria, almacena un 2
allí y establece que len
sea la dirección de ese byte.
Aquí hay un código psuedo-assembler que muestra la distinción:
line addr code label instruction
---- ---- -------- ----- -----------
1 0000 org 1234
2 1234 elen equ 2
3 1234 02 dlen db 2
4 1235 44 02 00 mov ax elen
5 1238 44 34 12 mov ax dlen
La línea 1 simplemente establece la dirección del conjunto en 1234
, para que sea más fácil explicar lo que está sucediendo.
En la línea 2, no se genera ningún código, el ensamblador simplemente carga elen
en la tabla de símbolos con el valor 2
. Como no se ha generado ningún código, la dirección no cambia.
Luego, cuando lo usa en la línea 4, carga ese valor en el registro.
La línea 3 muestra que db
es diferente, en realidad asigna algo de espacio (un byte) y almacena el valor en ese espacio. Luego carga dlen
en la tabla de símbolos, pero le da el valor de esa dirección 1234
lugar del valor constante 2
.
Cuando más tarde use dlen
en la línea 5, obtendrá la dirección, la cual tendría que no tener en cuenta para obtener el valor real 2
.
Resumen
NASM 2.10.09 salida ELF:
db
no tiene ningún efecto mágico: simplemente genera bytes directamente en el archivo de objeto de salida.Si esos bytes están frente a un símbolo, el símbolo apuntará a ese valor cuando se inicie el programa.
Si estás en la sección de texto, tus bytes se ejecutarán.
El tiempo que usa
db
odw
, etc., que no especifica el tamaño del símbolo: el campost_size
de la entrada de la tabla de símbolos no se ve afectado.equ
hace que el símbolo en la línea actual tengast_shndx == SHN_ABS
valor mágico en su entrada de tabla de símbolos.En lugar de enviar un byte a la ubicación del archivo de objeto actual, lo envía al campo
st_value
de la entrada de la tabla de símbolos.
Todo lo demás se sigue de esto.
Para comprender lo que realmente significa, primero debe comprender los conceptos básicos del estándar ELF y la relocation .
Teoría SHN_ABS
SHN_ABS
le dice al vinculador que:
- La reubicación no debe hacerse en este símbolo.
-
st_value
campost_value
de la entrada del símbolo se utilizará directamente como valor
Contraste esto con los símbolos "regulares", en los que el valor del símbolo es una dirección de memoria y, por lo tanto, debe pasar por la reubicación.
Ya que no apunta a la memoria, los símbolos SHN_ABS
pueden eliminarse efectivamente del ejecutable mediante el enlazador insertándolos.
Pero aún son símbolos regulares en los archivos de objetos y ocupan memoria allí, y podrían compartirse entre varios archivos si son globales.
Uso de la muestra
section .data
x: equ 1
y: db 2
section .text
global _start
_start:
mov al, x
; al == 1
mov al, [y]
; al == 2
Tenga en cuenta que dado que el símbolo x
contiene un valor literal, no se debe hacer ninguna referencia []
para él como para y
.
Si quisiéramos usar x
de un programa en C, necesitaríamos algo como:
extern char x;
printf("%d", &x);
y puesta en el asm:
global x
Observación empírica de salida generada.
Podemos observar lo que hemos dicho antes con:
nasm -felf32 -o equ.o equ.asm
ld -melf_i386 -o equ equ.o
Ahora:
readelf -s equ.o
contiene:
Num: Value Size Type Bind Vis Ndx Name
4: 00000001 0 NOTYPE LOCAL DEFAULT ABS x
5: 00000000 0 NOTYPE LOCAL DEFAULT 1 y
Ndx
es st_shndx
, entonces vemos que x
es SHN_ABS
mientras que y
no lo es.
También vea que el Size
es 0
para y
: db
de ninguna manera le dijo que era de un solo byte de ancho. Simplemente podríamos agregar dos directivas db
para asignar 2 bytes allí.
Y entonces:
objdump -dr equ
da:
08048080 <_start>:
8048080: b0 01 mov $0x1,%al
8048082: a0 88 90 04 08 mov 0x8049088,%al
Así que vemos que 0x1
se incluyó en la instrucción, mientras que y
obtuvo el valor de una dirección de reubicación 0x8049088
.
Probado en Ubuntu 14.04 AMD64.
Docs
http://www.nasm.us/doc/nasmdoc3.html#section-3.2.4 :
EQU define un símbolo a un valor constante dado: cuando se usa EQU, la línea de origen debe contener una etiqueta. La acción de EQU es definir el nombre de etiqueta dado al valor de su (solo) operando. Esta definición es absoluta, y no puede cambiar más tarde. Así por ejemplo,
message db ''hello, world''
msglen equ $-message
define msglen como la constante 12. msglen no se puede redefinir más adelante. Esta tampoco es una definición de preprocesador: el valor de msglen se evalúa una vez, usando el valor de $ (consulte la sección 3.5 para obtener una explicación de $) en el punto de definición, en lugar de evaluarlo donde sea que se haga referencia y use el valor de $ en el punto de referencia.
Ver también
Pregunta análoga para GAS: ¿ Diferencia entre .equ y .word en ARM Assembly? .equiv
parece ser el equivalente de GAS cerrado.
equ : tiempo de preprocesador. de forma análoga a #define, pero la mayoría de los ensambladores carecen de un #undef, y no pueden tener nada más que una constante atómica de un número fijo de bytes en el lado derecho, por lo que las listas flotan, se duplican, no son compatibles con la directiva equ de la mayoría de ensambladores.
db : tiempo de compilación. el valor almacenado en db se almacena en la salida binaria por el ensamblador en un desplazamiento específico. equ le permite definir constantes que normalmente tendrían que estar codificadas o requerir una operación mov para obtenerlas. db le permite tener datos disponibles en la memoria incluso antes de que comience el programa.
Aquí hay un nasm demostrando db:
; I am a 16 byte object at offset 0.
db ''----------------''
; I am a 14 byte object at offset 16
; the label foo makes the assembler remember the current ''tell'' of the
; binary being written.
foo:
db ''Hello, World!'', 0
; I am a 2 byte filler at offset 30 to help readability in hex editor.
db '' .''
; I am a 4 byte object at offset 16 that the offset of foo, which is 16(0x10).
dd foo
Un equ solo puede definir una constante hasta la más grande que soporta el ensamblador
Ejemplo de equ, junto con algunas limitaciones comunes de la misma.
; OK
ZERO equ 0
; OK(some assemblers won''t recognize /r and will need to look up the ascii table to get the value of it).
CR equ 0xD
; OK(some assemblers won''t recognize /n and will need to look up the ascii table to get the value of it).
LF equ 0xA
; error: bar.asm:2: warning: numeric constant 102919291299129192919293122 -
; does not fit in 64 bits
; LARGE_INTEGER equ 102919291299129192919293122
; bar.asm:5: error: expression syntax error
; assemblers often don''t support float constants, despite fitting in
; reasonable number of bytes. This is one of the many things
; we take for granted in C, ability to precompile floats at compile time
; without the need to create your own assembly preprocessor/assembler.
; PI equ 3.1415926
; bar.asm:14: error: bad syntax for EQU
; assemblers often don''t support list constants, this is something C
; does support using define, allowing you to define a macro that
; can be passed as a single argument to a function that takes multiple.
; eg
; #define RED 0xff, 0x00, 0x00, 0x00
; glVertex4f(RED);
; #undef RED
;RED equ 0xff, 0x00, 0x00, 0x00
el binario resultante no tiene bytes en absoluto porque equ no contamina la imagen; todas las referencias a un equ se reemplazan por el lado derecho de ese equ.