2012-06-30 10 views

risposta

2

align riempie l'indirizzo con NOPs/0x90 (NASM) fino a quando non è allineato con l'operando (l'operando addr modulo è zero).

Ad esempio:

db 12h 
align 4 
db 32h 

uscite Quando riuniti:

0000 12 90 90 90 
0004 32 

Questo è più veloce di accesso alla memoria e necessario caricare alcune tabelle nel CPU x86 (e probabilmente altre architetture). Non posso nominare alcun caso specifico, ma puoi trovare severalanswers su SO e motori di ricerca.

+0

grazie !! ha effetto solo per i prossimi dati/istruzioni o per tutta la sezione? – user1462787

+0

@ user1462787 non dipende o modifica l'istruzione successiva, scrive solo NOP a seconda dell'offset corrente dall'inizio del file – copy

27

Mi è sempre piaciuta la spiegazione esauriente da Samael in questo thread:
Explanation of the ALIGN MASM directive, How is this directive interpreted by the compiler?

Citazione:

1. Uso

ALIGN X 

La direttiva ALIGN è accompagnato da un numero di (X).
Questo numero (X) deve essere una potenza di 2. Quello è 2, 4, 8, 16 e così via ...

La direttiva consente di applicare l'allineamento delle istruzioni o dei dati immediatamente dopo la direttiva , su un indirizzo di memoria multiplo del valore X.

Lo spazio aggiuntivo, tra le istruzioni/dati precedenti e uno dopo la direttiva ALIGN, è riempito con istruzioni NULL (o equivalenti, come MOV EAX, EAX) nel caso di segmenti di codice e NULL nel caso di segmenti di dati.

Il numero X, non può essere maggiore dell'allineamento predefinito del segmento in cui viene fatto riferimento alla direttiva ALIGN. Deve essere inferiore o uguale all'allineamento predefinito del segmento. Maggiori informazioni su questo da seguire ...

2. OGGETTO

A. Operazioni con il codice

Se la direttiva precede codice, il motivo sarebbe l'ottimizzazione (con riferimento alla velocità di esecuzione) . Alcune istruzioni vengono eseguite più rapidamente se sono allineate su un limite di 4 byte (32 bit). Questo tipo di ottimizzazione può essere normalmente utilizzato o referenziato in funzioni temporalmente critiche, come ad esempio cicli che sono progettati per manipolare una grande quantità di dati, costantemente. Tuttavia, oltre al miglioramento della velocità di esecuzione, non esiste "necessità" di utilizzare la direttiva con il codice.

B.Lavorare con i dati

Lo stesso vale anche per i dati: utilizziamo principalmente la direttiva per migliorare la velocità di esecuzione, come mezzo di ottimizzazione della velocità. Ci sono situazioni in cui il disallineamento dei dati può avere un enorme impatto sulle prestazioni della nostra applicazione.

Ma con i dati, ci sono situazioni in cui l'allineamento corretto è una necessità, non di lusso. Ciò vale soprattutto per la piattaforma Itanium e per il set di istruzioni SSE/SSE2, in cui il disallineamento su un limite di 128 bit (X = 16) può generare un'eccezione di protezione generale.

Un articolo interessante e più informativo sulla allineamento dei dati, anche se orientata sulla MS C/C++, è la seguente:

Windows Data Alignment on IPF, x86, and x64, by Kang Su Gatlin, MSDN

3. Qual è il difetto di allineamento di un segmento?

A. Se si utilizza la direttiva di processore .386, e tu non hai esplicitamente dichiarato il valore di allineamento di default per un segmento, l'allineamento segmento predefinito è di DWORD (4 byte) dimensioni. Sì, in questo caso, X = 4. È quindi possibile utilizzare i seguenti valori con la direttiva ALIGN: (X = 2, X = 4). Ricorda che X deve essere inferiore o uguale all'allineamento del segmento.

B. Se si utilizza la direttiva di processore 0,486 e al di sopra, e tu non hai esplicitamente dichiarato il valore di allineamento di default per un segmento, l'allineamento segmento di default è del paragrafo (16 byte) dimensioni. In questo caso, X = 16. È quindi possibile utilizzare i seguenti valori con la direttiva ALIGN: (X = 2, X = 4, X = 8, X = 16).

C. È possibile dichiarare un segmento con l'allineamento non predefinito nel seguente modo:

;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 

Qui ci sono gli alias per i valori segmento assetto in ...

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 Esempio

Considerare il seguente esempio (leggere i commenti sull'utilizzo del Direttiva 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 

Se si compila il programma, ecco cosa il compilatore ha generato:

.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------------------------------ 

Come si vede, dopo il codice/dati dei nostri fini applicativi, il compilatore genera più istruzioni/dati. Questo perché le sezioni PE, quando caricate in memoria, sono allineate su una dimensione PAGE (512 byte).

Quindi, il compilatore, riempie lo spazio extra al successivo 512 byte boudary con junk bytes (solitamente istruzioni INT 3, NOP o NULL per i segmenti di codice e 0FFh, NULL per i segmenti di dati) per garantire che la memoria l'allineamento per l'immagine PE caricata è corretta ...

+0

La spiegazione più accurata, completa ed educativa che ho trovato online, grazie! – petric

+0

Sulla maggior parte degli ISA a lunghezza fissa come MIPS, le istruzioni * devono * essere allineate a 4 byte o la CPU si guasta. Inoltre, su x86, l'allineamento delle istruzioni è importante (a volte) per i target di salto, non in realtà a seconda dell'istruzione * which * che è. La tua affermazione che * alcune istruzioni sono eseguite più velocemente se sono allineate su un limite di 4 byte (32 bit) * non è molto sensibile su nessuna CPU x86 moderna (anche nel 2012 quando hai scritto questo). I limiti che contano sono i limiti della cache-line (64-byte) o del fetch-block (solitamente 16-byte), o dei limiti del blocco della cache-uop (32-byte su Intel). Vedi http://agner.org/optimize/. –

+0

Correlati: [Perché l'assegnazione di un intero su una variabile atomica allineata naturalmente su x86?] (Https://stackoverflow.com/questions/36624881/why-is-integer-assignment-on-a-natural-aligned-variable-atomic -on-x86). –

14

I ricordi sono a larghezza fissa, oggi a 32 bit o in genere a 64 bit (anche se è un sistema a 32 bit). Supponiamo per ora un bus dati a 32 bit.Ogni volta che si esegue una lettura, che si tratti di 8, 16 o 32 bit, è un bus a 32 bit, quindi quelle linee dati avranno qualcosa su di esse, ha senso inserire solo i 32 bit relativi all'indirizzo allineato.

Quindi se all'indirizzo 0x100 si ha il valore di 32 bit 0x12345678. E dovevi eseguire una lettura a 32 bit, tutti quei bit sarebbero sul bus. Se dovessi eseguire una lettura a 8 bit all'indirizzo 0x101, il controller di memoria farebbe una lettura dell'indirizzo 0x100, otterrebbe 0x12345678. E da quei 32 bit isolerebbe la "corsia dei byte" corretta, gli 8 bit relativi all'indirizzo 0x101. Alcuni processori del controller di memoria potrebbero non vedere mai altro che letture a 32 bit, il processore gestirà l'isolamento della corsia dei byte.

E i processori che consentono accessi non allineati come x86? Se avessi 0x12345678 all'indirizzo 0x100 e 0xAABBCCDD all'indirizzo 0x104. E dovevamo fare una lettura a 32 bit all'indirizzo 0x102 su questo sistema basato su bus dati a 32 bit, quindi sono necessari due cicli di memoria, uno all'indirizzo 0x100 dove vivono 16 bit del valore desiderato e poi un altro a 0x104 dove gli altri due byte sono trovato. Dopo queste due letture, è possibile riunire i 32 bit e fornire quello più profondo nel processore in cui è stato richiesto. La stessa cosa accade se si vuole fare una lettura a 16 bit all'indirizzo 0x103, costa il doppio dei cicli di memoria, impiega il doppio del tempo.

Ciò che la direttiva .align normalmente fa in linguaggio assembly (ovviamente è necessario specificare l'assemblatore e il processore esatti in quanto questa è una direttiva e ogni assemblatore può definire qualsiasi cosa voglia definire per le direttive) è l'output in modo tale che il cosa che segue immediatamente lo .align è, beh, allineato su quel confine. Se avessi avuto questo codice:

b: .db 0 
c: .dw 0 

e si scopre che quando ho assemblare e collego l'indirizzo per C è 0x102, ma so che accederanno che molto spesso come un valore a 32 bit, allora posso allineare esso facendo qualcosa di simile a questo:

b: .db 0 
.align 4 
c: .dw 0 

nient'altro assumendo prima di questo cambia di conseguenza, allora b sarà ancora all'indirizzo 0x101, ma l'assemblatore metterà altri due byte nel binario tra B e C in modo che c cambia all'indirizzo 0x104, allineato su un limite di 4 byte.

"allineato su un limite di 4 byte" significa semplicemente che l'indirizzo modulo 4 è zero. in pratica 0x0, 0x4, 0x8, 0xc, 0x10, 0x14, 0x18, 0x1C e così via. (i due bit inferiori dell'indirizzo sono zero). Allineato su 8 significa che 0x0, 0x8, 0x10, 0x18 o 3 bit inferiori dell'indirizzo sono zero. E così via.

Le scritture sono peggio delle letture in quanto è necessario eseguire operazioni di lettura-modifica-scrittura per i dati più piccoli del bus. Se volessimo cambiare il byte all'indirizzo 0x101, dovremmo leggere il valore a 32 bit all'indirizzo 0x100, cambiare l'un byte, quindi riportare quel valore a 32 bit a 0x100. Quindi quando stai scrivendo un programma e pensi di fare cose più velocemente usando valori più piccoli, non lo sei. Quindi una scrittura che non è allineata e la larghezza della memoria costa la lettura-modifica-scrittura. Una scrittura non allineata ti costa il doppio rispetto a quanto fatto con le letture. Una scrittura non allineata sarebbe composta da due scritture di modifica della lettura. Le scritture hanno comunque una funzione di performance rispetto alle letture. Quando un programma deve leggere qualcosa dalla memoria e usare subito quel valore, l'istruzione successiva deve attendere il completamento del ciclo di memoria (che in questi giorni può essere centinaia di cicli di clock, il dram è rimasto bloccato a 133MHz per circa un decennio, la tua memoria DDR3 a 1333 MHz non è 1333 MHz, il bus è 1333 MHz/2 e puoi inserire le richieste a quella velocità ma la risposta non torna per molto tempo). Fondamentalmente con una lettura hai un indirizzo ma devi aspettare i dati per tutto il tempo necessario. Per una scrittura si hanno entrambi gli elementi, l'indirizzo e i dati, e si può "sparare e dimenticare" di fornire al controller di memoria l'indirizzo e i dati e il programma può continuare a funzionare. Se le istruzioni o le istruzioni successive necessitano di accedere alla memoria, leggere o scrivere, allora tutti devono aspettare che finisca la prima scrittura, quindi passare all'accesso successivo.

Tutto quanto sopra è molto semplicistico, eppure quello che si vedrebbe tra il processore e la cache, sull'altro lato della cache, la memoria a larghezza fissa (la larghezza fissa dell'Sram nella cache e la larghezza fissa di il dram sul lato opposto non deve corrispondere) sull'altro lato della cache si accede in "linee cache" che sono generalmente multipli della dimensione della larghezza del bus. questo aiuta e fa male con l'allineamento. Ad esempio, 0x100 è un limite della linea cache. La parola a 0xFE diciamo è la coda di una riga della cache e 0x100 l'inizio della successiva. Se dovessi eseguire una lettura a 32 bit all'indirizzo 0xFE, non solo due cicli di memoria a 32 bit devono accadere, ma due recuperi di riga della cache. Il peggior caso sarebbe quello di dover eliminare due righe di cache nella memoria per fare spazio alle due nuove linee di cache che si stanno recuperando. Se avessi usato un indirizzo allineato, sarebbe ancora male ma solo la metà come male.

La tua domanda non ha specificato il processore, ma la natura della tua domanda implica x86 che è ben noto per questo problema. Altre famiglie di processori non consentono accessi non allineati, oppure è necessario disabilitare in modo specifico l'errore di eccezione. E a volte l'accesso non allineato non è come x86. Ad esempio su almeno un processore se avessi 0x12345678 all'indirizzo 0x100 e 0xAABBCCDD all'indirizzo 0x104 e hai disabilitato l'errore ed eseguito un 32 bit letto all'indirizzo 0x102 riceverai 0x56781234. Un singolo 32 bit letto con le corsie dei byte ruotate per mettere il byte inferiore nel posto giusto. No, non sto parlando di un sistema x86 ma di un altro processore.