pelicula - assembly significado
¿Cuál es el significado de alinear y comenzar una sección? (3)
¿Cuál es el significado de alinear y comenzar una sección?
Por ejemplo:
align 4
a: dw 0
¿Cómo se guarda el acceso a la memoria?
Las memorias tienen un ancho fijo, hoy en día de 32 bits o típicamente de 64 bits de ancho (incluso si se trata de un sistema de 32 bits). Asumamos un bus de datos de 32 bits por ahora. Cada vez que realice una lectura, ya sea de 8, 16 o 32 bits, es un bus de 32 bits, por lo que esas líneas de datos tendrán algo en ellos, tiene sentido colocar los 32 bits relacionados con la dirección alineada.
Entonces, si en la dirección 0x100 tenías el valor de 32 bits 0x12345678. Y tenías que realizar una lectura de 32 bits, todos esos bits estarían en el bus. Si tuviera que realizar una lectura de 8 bits en la dirección 0x101, el controlador de memoria haría una lectura de la dirección 0x100, obtendría 0x12345678. Y a partir de esos 32 bits aislaría el "carril de bytes" adecuado, los 8 bits relacionados con la dirección 0x101. Es posible que algunos procesadores del controlador de memoria nunca vean nada más que lecturas de 32 bits, el procesador manejaría el aislamiento del carril de bytes.
¿Qué pasa con los procesadores que permiten accesos no alineados como el x86? Si tenía 0x12345678 en la dirección 0x100 y 0xAABBCCDD en la dirección 0x104. Y si hiciéramos una lectura de 32 bits en la dirección 0x102 en este sistema de bus de datos de 32 bits, entonces se requieren dos ciclos de memoria, uno en la dirección 0x100 donde viven 16 bits del valor deseado y luego otro en 0x104 donde están los otros dos bytes encontró. Después de que esas dos lecturas ocurran, puede juntar los 32 bits y proporcionarlos más profundamente en el procesador donde se solicitó. Lo mismo sucede si desea realizar una lectura de 16 bits en la dirección de decir 0x103, le cuesta el doble de ciclos de memoria, toma el doble de tiempo.
Lo que la directiva .align
normalmente hace en lenguaje ensamblador (por supuesto, tiene que especificar el ensamblador y el procesador exactos, ya que esta es una directiva y cada ensamblador puede definir lo que quiera definir para las directivas) es rellenar la salida de tal manera que lo que inmediatamente sigue el .align
está, bueno, alineado en ese límite. Si tuviera este código:
b: .db 0
c: .dw 0
Y resulta que cuando armo y vinculo la dirección de C es 0x102, pero sé que accederé a eso muy a menudo como un valor de 32 bits, entonces puedo alinearlo haciendo algo como esto:
b: .db 0
.align 4
c: .dw 0
asumiendo que no hay nada más antes de que esto cambie como resultado, entonces b seguirá en la dirección 0x101, pero el ensamblador colocará dos bytes más en el binario entre byc, de modo que c cambie a la dirección 0x104, alineado en un límite de 4 bytes.
"alineado en un límite de 4 bytes" simplemente significa que el módulo de dirección 4 es cero. Básicamente 0x0, 0x4, 0x8, 0xc, 0x10, 0x14, 0x18, 0x1C y así sucesivamente. (los dos bits más bajos de la dirección son cero). Alineado en 8 significa que 0x0, 0x8, 0x10, 0x18 o menos 3 bits de la dirección son cero. Y así.
Las escrituras son peores que las lecturas, ya que tiene que hacer lecturas-modificar-escrituras para datos más pequeños que el bus. Si quisiéramos cambiar el byte en la dirección 0x101, leemos el valor de 32 bits en la dirección 0x100, cambiamos el byte y luego escribimos ese valor de 32 bits de nuevo a 0x100. Entonces, cuando estás escribiendo un programa y crees que estás haciendo las cosas más rápido usando valores más pequeños, no lo estás haciendo. Así que una escritura que no está alineada y el ancho de la memoria le cuesta la lectura-modificación-escritura. Una escritura sin alinear le cuesta el doble, igual que con las lecturas. Una escritura no alineada sería dos escrituras de modificación de lectura. Sin embargo, las escrituras tienen una característica de rendimiento sobre las lecturas. Cuando un programa necesita leer algo de la memoria y usar ese valor de inmediato, la siguiente instrucción tiene que esperar a que se complete el ciclo de la memoria (que en estos días pueden ser cientos de ciclos de reloj, dram se ha atascado a 133MHz durante aproximadamente una década, su memoria DDR3 de 1333MHz no es 1333MHz, el bus es de 1333MHz / 2 y puede ingresar solicitudes a esa velocidad pero la respuesta no regresa por mucho tiempo). Básicamente, con una lectura tiene una dirección, pero tiene que esperar los datos todo el tiempo que sea necesario. Para una escritura, tiene ambos elementos, la dirección y los datos, y puede "disparar y olvidar" le da al controlador de memoria la dirección y los datos, y su programa puede seguir funcionando. Concedido si la siguiente instrucción o conjunto de instrucciones necesita acceder a la memoria, leer o escribir, entonces todos tienen que esperar a que termine la primera escritura y luego pasar al siguiente acceso.
Todo lo anterior es muy simplista, pero lo que vería entre el procesador y el caché, en el otro lado del caché, la memoria de ancho fijo (el ancho fijo del sram en el caché y el ancho fijo del dram en la El lado lejano no tiene que coincidir) en el otro lado del caché se accede en "líneas de caché" que generalmente son múltiplos del tamaño del ancho del bus. esto ayuda y duele con la alineación. Digamos, por ejemplo, 0x100 es un límite de línea de caché. La palabra en 0xFE digamos que es el final de la cola de una línea de caché y 0x100 el comienzo de la siguiente. Si tuviera que realizar una lectura de 32 bits en la dirección 0xFE, no solo tienen que suceder dos ciclos de memoria de 32 bits, sino también dos búsquedas de línea de caché. El peor de los casos sería tener que desalojar dos líneas de caché a la memoria para dejar espacio para las dos nuevas líneas de caché que está buscando. Si hubiera usado una dirección alineada, aún sería malo pero solo la mitad de malo.
Su pregunta no especificó el procesador, pero la naturaleza de su pregunta implica x86, que es bien conocido para este problema. Otras familias de procesadores no permiten accesos no alineados, o tiene que deshabilitar específicamente el error de excepción. Y a veces el acceso no alineado no es como x86. Por ejemplo, al menos en un procesador, si tenía 0x12345678 en la dirección 0x100, y 0xAABBCCDD en la dirección 0x104 y deshabilitó el error y realizó una lectura de 32 bits en la dirección 0x102, obtendrá 0x56781234. Una sola lectura de 32 bits con los carriles de bytes girados para colocar el byte inferior en el lugar correcto. No, no estoy hablando de un sistema x86 sino de algún otro procesador.
Siempre me gustó la explicación completa de Samael en el siguiente hilo:
Explicación de la directiva ALIGN MASM, ¿cómo interpreta esta directiva el compilador?
Citar:
1. USO
ALIGN X
La directiva ALIGN va acompañada de un número (X).
Este número (X) debe ser una potencia de 2. Eso es 2, 4, 8, 16, y así sucesivamente ...
La directiva le permite imponer la alineación de la instrucción o los datos inmediatamente después de la directiva, en una dirección de memoria que es un múltiplo del valor X.
El espacio adicional, entre la instrucción / datos anterior y el que está después de la directiva ALIGN, se rellena con instrucciones NULL (o equivalentes, como MOV EAX, EAX) en el caso de segmentos de código y NULL en el caso de segmentos de datos.
El número X no puede ser mayor que la alineación predeterminada del segmento en el que se hace referencia a la directiva ALIGN. Debe ser menor o igual a la alineación predeterminada del segmento. Más sobre esto para seguir ...
2.PROPOSITO
A. Trabajar con código
Si la directiva precede al código, la razón sería la optimización (con referencia a la velocidad de ejecución). Algunas instrucciones se ejecutan más rápido si se alinean en un límite de 4 bytes (32 bits). Este tipo de optimización generalmente se puede usar o referenciar en funciones de tiempo crítico, como los bucles que están diseñados para manipular una gran cantidad de datos, constantemente. Sin embargo, además de la mejora en la velocidad de ejecución, no hay "necesidad" de usar la directiva con código.
B. Trabajar con datos
Lo mismo ocurre con los datos, principalmente utilizamos la directiva para mejorar la velocidad de ejecución, como un medio de optimización de la velocidad. Hay situaciones en las que la desalineación de los datos puede tener un gran impacto en el rendimiento de nuestra aplicación.
Pero con los datos, hay situaciones donde la alineación correcta es una necesidad, no un lujo. Esto es especialmente cierto en la plataforma Itanium y el conjunto de instrucciones SSE / SSE2, donde la desalineación en un límite de 128 bits (X = 16), puede activar una excepción de protección general.
Un artículo interesante e informativo sobre la alineación de datos, aunque orientado en el compilador MS C / C ++, es el siguiente:
Alineación de datos de Windows en IPF, x86 y x64, por Kang Su Gatlin, MSDN
3. ¿Cuál es la alineación predeterminada de un segmento?
A. Si usa la directiva de procesador .386, y no ha declarado explícitamente el valor de alineación predeterminado para un segmento, la alineación de segmento predeterminada es de tamaño DWORD (4 bytes). Sí, en este caso, X = 4. Luego puedes usar los siguientes valores con la directiva ALIGN: (X = 2, X = 4). Recuerde, X debe ser menor o igual que la alineación del segmento.
B. Si usa la directiva del procesador .486 y superior, y no ha declarado explícitamente el valor de alineación predeterminado para un segmento, la alineación predeterminada del segmento es de tamaño PARAGRAPH (16 bytes). En este caso, X = 16. Luego puede usar los siguientes valores con la directiva ALIGN: (X = 2, X = 4, X = 8, X = 16).
C. Puede declarar un segmento con alineación no predeterminada de la siguiente manera:
;Here, we create a code segment named "JUNK", which starts aligned on a 256 bytes boundary
JUNK SEGMENT PAGE PUBLIC FLAT ''CODE''
;Your code starts aligned on a PAGE boundary (X=256)
; Possible values that can be used with the ALIGN directive
; within this segment, are all the powers of 2, up to 256.
JUNK ENDS
Aquí están los alias para los valores de segmentación de segmentos ...
Align Type Starting Address
BYTE Next available byte address.
WORD Next available word address (2 bytes per word).
DWORD Next available double word address (4 bytes per double word).
PARA Next available paragraph address (16 bytes per paragraph).
PAGE Next available page address (256 bytes per page).
4. Ejemplo
Considere el siguiente ejemplo (lea los comentarios sobre el uso de la directiva ALIGN).
.486
.MODEL FLAT,STDCALL
OPTION CASEMAP:NONE
INCLUDE /MASM32/INCLUDE/WINDOWS.INC
.DATA
var1 BYTE 01; This variable is of 1 byte size.
ALIGN 4
; We enforce the next variable to be alingned in the next memory
;address that is multiple of 4.
;This means that the extra space between the first variable
;and this one will be padded with nulls. ( 3 bytes in total)
var2 BYTE 02; This variable is of 1 byte size.
ALIGN 2
; We enforce the next variable to be alingned in the next memory
;address that is multiple of 2.
;This means that the extra space between the second variable
;and this one will be padded with nulls. ( 1 byte in total)
var3 BYTE 03; This variable is of 1 byte size.
.CODE
; Enforce the first instruction to be aligned on a memory address multiple of 4
ALIGN 4
EntryPoint:
; The following 3 instructions have 7 byte - opcodes
; of the form 0F B6 05 XX XX XX XX
; In the following block, we do not enforce opcode
; alignment in memory...
MOVZX EAX, var1
MOVZX EAX, var2
MOVZX EAX, var3
; The following 3 instructions have 7 byte - opcodes
; of the form 0F B6 05 XX XX XX XX
; In the following block, we enforce opcode alignment
; for the third instruction, on a memory address multiple of 4.
; Since the second instruction opcodes end on a memory address
; that is not a multiple of 4, some nops would be injected before
; the first opcode of the next instruction, so that the first opcode of it
; will start on a menory address that is a multiple of 4.
MOVZX EAX, var1
MOVZX EAX, var2
ALIGN 4
MOVZX EAX, var3
; The following 3 instructions have 7 byte - opcodes
; of the form 0F B6 05 XX XX XX XX
; In the following block, we enforce opcode alignment
; for all instructions, on a memory address multiple of 4.
;The extra space between each instruction will be padded with NOPs
ALIGN 4
MOVZX EAX, var1
ALIGN 4
MOVZX EAX, var2
ALIGN 4
MOVZX EAX, var3
ALIGN 2
; The following instruction has 1 byte - opcode (CC).
; In the following block, we enforce opcode alignment
; for the instruction, on a memory address multiple of 2.
;The extra space between this instruction ,
;and the previous one, will be padded with NOPs
INT 3
END EntryPoint
Si compilamos el programa, esto es lo que generó el compilador:
.DATA
;------------SNIP-SNIP------------------------------
.data:00402000 var1 db 1
.data:00402001 db 0; This NULL was generated to enforce the alignment of the next instruction on an address that is a multiple of 4
.data:00402002 db 0; This NULL was generated to enforce the alignment of the next instruction on an address that is a multiple of 4
.data:00402003 db 0; This NULL was generated to enforce the alignment of the next instruction on an address that is a multiple of 4
.data:00402004 var2 db 2
.data:00402005 db 0; This NULL was generated to enforce the alignment of the next instruction oon an address that is a multiple of 2
.data:00402006 var3 db 3
.data:00402007 db 0; The rest of the NULLs are to fill the memory page in which the segment will be loaded
;------------SNIP-SNIP------------------------------
.CODE
;------------SNIP-SNIP------------------------------
.text:00401000 start:
.text:00401000 movzx eax, var1
.text:00401007 movzx eax, var2
.text:0040100E movzx eax, var3
.text:00401015 movzx eax, var1
.text:0040101C movzx eax, var2
.text:00401023 nop; This NOP was generated to enforce the alignment...
.text:00401024 movzx eax, var3
.text:0040102B nop; This NOP was generated to enforce the alignment...
.text:0040102C movzx eax, var1
.text:00401033 nop; This NOP was generated to enforce the alignment...
.text:00401034 movzx eax, var2
.text:0040103B nop; This NOP was generated to enforce the alignment...
.text:0040103C movzx eax, var3
.text:00401043 nop; This NOP was generated to enforce the alignment...
.text:00401044 int 3 ; Trap to Debugger
.text:00401044; ---------------------------------------------------------------------------
.text:00401045 db 0
.text:00401046 db 0
.text:00401047 db 0
.text:00401048 db 0
;------------SNIP-SNIP------------------------------
Como puede ver, una vez que finaliza el código / datos de nuestra aplicación, el compilador genera más instrucciones / datos. Esto se debe a que las secciones de PE, cuando están cargadas en la memoria, están alineadas en un tamaño de PÁGINA (512 bytes).
Por lo tanto, el compilador, llena el espacio adicional al siguiente boudary de 512 bytes con bytes basura (generalmente instrucciones INT 3, NOP o NULL para segmentos de código, y 0FFh, NULL para segmentos de datos) para asegurar que la alineación de memoria para el cargado La imagen de PE es correcta ...
align
llena la dirección con NOPs / 0x90 (NASM) hasta que se alinee con el operando (addr modulo operand is zero).
Por ejemplo:
db 12h
align 4
db 32h
Cuando se ensamblan las salidas:
0000 12 90 90 90
0004 32
Esto es más rápido para el acceso a la memoria y es necesario para cargar algunas tablas en las CPU x86 (y probablemente también en otras arquitecturas). No puedo nombrar ningún caso específico, pero puede encontrar several answers en SO y motores de búsqueda.