2012-06-30 16 views

Odpowiedz

2

align wypełnia adres NOP/0x90 (NASM), dopóki nie zostanie wyrównany do argumentu operacji (operand addr modulo wynosi zero).

na przykład:

db 12h 
align 4 
db 32h 

po zmontowaniu wyjścia:

0000 12 90 90 90 
0004 32 

jest szybsza dla dostępu do pamięci oraz niezbędne do załadowania tabel w procesorach x86 (i prawdopodobnie innych architekturach dołek). Nie mogę podać żadnych konkretnych przypadków, ale można znaleźć severalanswers na SO i wyszukiwarkach.

+0

dzięki! wpływa tylko na następne dane/instrukcje lub na całą sekcję? – user1462787

+0

@ user1462787 to nie zależy ani nie zmodyfikuje następnej instrukcji, po prostu zapisuje NOP w zależności od aktualnego przesunięcia od początku pliku – copy

27

Zawsze lubiłem wszechstronnego wyjaśnienia przez Samaela w wątku:
Explanation of the ALIGN MASM directive, How is this directive interpreted by the compiler?

Cytat:

1. ZASTOSOWANIE

ALIGN X 

Dyrektywa align towarzyszy szereg (X).
Ta liczba (X) musi być potęgą 2. Oznacza to 2, 4, 8, 16 itd.

Dyrektywa umożliwia egzekwowanie dostosowania instrukcji lub danych bezpośrednio po dyrektywie na adres pamięci stanowiący wielokrotność wartości X.

Dodatkowa przestrzeń między poprzednią instrukcją/danymi a jedną po dyrektywie ALIGN jest uzupełniona instrukcjami NULL (lub odpowiednikami, takimi jak MOV EAX, EAX) w przypadku segmentów kodu i NULL w przypadku segmentów danych.

Liczba X nie może być większa niż domyślne wyrównanie segmentu, do którego odwołuje się dyrektywa ALIGN. Musi być mniejszy lub równy domyślnemu wyrównaniu segmentu. Więcej na ten temat podążać ...

2. CEL

A. Praca z kodem

Jeżeli dyrektywa poprzedza kod, powodem będzie optymalizacja (w odniesieniu do szybkości wykonania) . Niektóre instrukcje są wykonywane szybciej, jeśli są wyrównane na granicy 4-bajtowej (32-bitowej). Ten rodzaj optymalizacji może być zwykle używany lub odwoływać się do funkcji o krytycznym czasie, takich jak pętle, które są przeznaczone do manipulowania dużą ilością danych. Oprócz poprawy szybkości wykonania, nie ma "konieczności" użycia dyrektywy z kodem.

B.Praca z danymi

To samo dotyczy również danych - głównie używamy dyrektywy w celu poprawy szybkości realizacji - jako środka optymalizacji prędkości. Są sytuacje, w których niewspółosiowość danych może mieć ogromny wpływ na wydajność aplikacji.

Jednak w przypadku danych istnieją sytuacje, w których prawidłowe wyrównanie jest koniecznością, a nie luksusem. Dotyczy to zwłaszcza platformy Itanium i zestawu instrukcji SSE/SSE2, w których niewspółosiowość na granicy 128-bitowej (X = 16) może spowodować wyjątek ogólnej ochrony.

Ciekawą i najbardziej pouczające artykuł na wyrównanie danych, choć ukierunkowane na MS C/C++, jest następujący:

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

3. Jaki jest domyślny Aligment segmentu?

A. Jeśli użyć dyrektywy 0,386 procesora, a ty miałaś wyraźnie oświadczył domyślną wartość wyrównania segmentu, ustawienie domyślne segmentu jest DWORD (4 bajty) wielkości. Tak, w tym przypadku X = 4. Następnie można użyć następujących wartości z dyrektywą ALIGN: (X = 2, X = 4). Pamiętaj, że X musi być mniejszy lub równy niż wyrównanie segmentu.

B. Jeśli użyć dyrektywy 0,486 procesora i wyżej, i miałaś wyraźnie oświadczył domyślną wartość wyrównania segmentu, ustawienie domyślne segment jest ustępu (16 bajtów) wielkości. W tym przypadku X = 16. Następnie można użyć następujących wartości z dyrektywą ALIGN: (X = 2, X = 4, X = 8, X = 16).

C. Można zadeklarować segment z innych niż domyślne wyrównanie w następujący sposób:

;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 

Oto aliasy dla wartości Aligment Segment ...

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 Przykład

Rozważmy następujący przykład (przeczytaj uwagi na temat użycia Dyrektywa 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 

Jeśli skompilować program, oto co kompilator generowane:

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

Jak widać, po kodzie/dane naszych końcówek aplikacyjnych, kompilator generuje więcej instrukcji/danych. Dzieje się tak dlatego, że sekcje PE, po załadowaniu do pamięci, są wyrównane do rozmiaru PAGE (512 bajtów).

Tak więc kompilator wypełnia dodatkową przestrzeń do następnego 512 bajtów boudary z wiadomościami-śmieciami (zwykle instrukcje INT 3, NOP lub NULL dla segmentów kodu i 0FFh, wartości NULL dla segmentów danych) w celu zapewnienia, że ​​pamięć wyrównanie dla załadowanego obrazu PE jest prawidłowe ...

+0

Najdokładniejsze, kompleksowe i edukacyjne wyjaśnienie znalazłem online, dziękuję! – petric

+0

W przypadku większości programów ISA o stałej długości, takich jak MIPS, instrukcje * muszą * być 4-bajtowe wyrównane lub procesor ulegnie awarii. Ponadto, na x86, wyrównanie instrukcji ma znaczenie (czasami) dla celów skoku, a nie w zależności od tego, * która * instrukcja jest. Twoje twierdzenie, że * niektóre instrukcje są wykonywane szybciej, jeśli są wyrównane na 4-bajtowej (32-bitowej) granicy *, nie jest zbyt sensowne na nowoczesnych procesorach x86 (nawet w 2012 roku, kiedy to napisałeś). Granice, które mają znaczenie, to granice pamięci podręcznej (64-bajtowe) lub bloków pobierania (zwykle 16-bajtowe) lub granice bloków pamięci podręcznej (32-bajtowe na Intel). Zobacz http://agner.org/optimize/. –

+0

Powiązane: [Dlaczego przypisanie liczby całkowitej do naturalnie wyrównanej zmiennej atomowej na x86?] (Https://stackoverflow.com/questions/36624881/why-is-integer-assignment-on-a-naturally-aligned-variable-atomic -on-x86). –

14

Pamięci mają stałą szerokość, obecnie 32-bitową lub zazwyczaj 64-bitową (nawet jeśli jest to system 32-bitowy). Pozwala na razie założyć 32-bitową magistralę danych.Za każdym razem, gdy czytasz, czyli 8, 16 lub 32 bity, jest to 32-bitowa magistrala, więc te linie danych będą miały coś na sobie, sensownym jest po prostu wstawienie 32 bitów związanych z wyrównanym adresem.

Więc jeśli pod adresem 0x100 miałeś 32-bitową wartość 0x12345678. I miałbyś wykonać 32-bitowe czytanie, wszystkie te bity znajdowałyby się w autobusie. Jeśli miałbyś wykonać 8-bitowy adres odczytu 0x101, kontroler pamięci zrobiłby odczyt adresu 0x100, otrzymywałby 0x12345678. A z tych 32 bitów izolowałoby to właściwy "bajtowy pas", 8 bitów związanych z adresem 0x101. Niektóre procesory kontrolera pamięci mogą nigdy nie widzieć niczego oprócz 32-bitowych odczytów, procesor poradziłby sobie z odizolowaniem bajtowego pasa.

A co z procesorami, które umożliwiają dostęp bez adiustacji, np. X86? Jeśli miałeś 0x12345678 pod adresem 0x100 i 0xAABBCCDD pod adresem 0x104. I miały być 32-bitowe odczyty adresu 0x102 w tym 32-bitowym systemie opartym na magistrali danych, a następnie dwa cykle pamięci są wymagane, jeden pod adresem 0x100, gdzie 16 bitów żądanej wartości jest aktywne, a następnie inny przy 0x104, gdzie pozostałe dwa bajty są uznany. Po tych dwóch odczytach można złożyć 32 bity i dostarczyć je głębiej do procesora, w którym zażądano. To samo dzieje się, jeśli chcesz zrobić 16-bitowy odczyt adresu 0x103, kosztuje dwa razy więcej cykli pamięci, trwa dwa razy dłużej.

Co zwykle robi dyrektywa .align w języku asemblerowym (oczywiście musisz podać dokładny asembler i procesor, ponieważ jest to dyrektywa i każdy asembler może zdefiniować to, co chce zdefiniować dla dyrektyw), to dopisz wyjście tak, aby rzecz, która natychmiast podąża za .align jest, dobrze, wyrównana do tej granicy. Gdybym miał ten kod:

b: .db 0 
c: .dw 0 

I okazuje się, że kiedy zebrać i połączyć adres dla C 0x102, ale wiem, że będę dostępu, które bardzo często jako wartość 32 bitową, to mogę wyrównać że robiąc coś takiego:

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

zakładając niczego innego, zanim ten zmienia się w wyniku, a następnie b nadal będzie pod adresem 0x101, ale assembler wprowadzi kolejne dwa bajty w pliku binarnego między b i C tak, że c zmiany adresu 0x104, wyrównane na granicy 4 bajtów.

"wyrównany do granicy 4 bajtów" oznacza po prostu, że adres modulo 4 wynosi zero. zasadniczo 0x0, 0x4, 0x8, 0xc, 0x10, 0x14, 0x18, 0x1C i tak dalej. (dwa dolne bity adresu są zerowe). Wyrównane na 8 oznacza, że ​​0x0, 0x8, 0x10, 0x18 lub niższe 3 bity adresu są zerowe. I tak dalej.

Pisania są gorsze niż odczyty, ponieważ musisz zrobić odczyt-modyfikować-zapis dla danych mniejszych niż autobus. Jeśli chcielibyśmy zmienić bajt pod adresem 0x101, odczytalibyśmy 32-bitową wartość pod adresem 0x100, zmieniliby jeden bajt, a następnie zapiszemy 32-bitową wartość z powrotem na 0x100. Więc kiedy piszesz program i myślisz, że robisz rzeczy szybciej, używając mniejszych wartości, nie jesteś. Tak więc zapis, który nie jest wyrównany, a szerokość pamięci kosztuje odczytanie-modyfikowanie-zapis. Niewyrównany zapis kosztuje Cię dwa razy tyle, co w czytaniu. Niewyrównany zapis to dwa zapisujące-modyfikuj-zapisy. W przypadku pisania napisy mają jednak funkcję wydajności. Kiedy program musi odczytać coś z pamięci i od razu użyć tej wartości, następna instrukcja musi poczekać na zakończenie cyklu pamięci (który może być setkami cykli zegara, dram utknął przy 133 MHz przez około dziesięć lat, Twoja pamięć DDR3 1333 MHz nie jest 1333MHz, magistrala 1333MHz/2 i możesz wysyłać żądania z tą prędkością, ale odpowiedź nie wraca przez dłuższy czas). Zasadniczo przy odczycie masz adres, ale musisz czekać na dane tak długo, jak to konieczne. Aby napisać, że masz zarówno przedmioty, adres i dane, i możesz "odpalić i zapomnieć", przekazujesz kontrolerowi pamięci adres i dane, a twój program może dalej działać. Przyznane, jeśli następna instrukcja lub zestaw instrukcji wymaga dostępu do pamięci, odczytu lub zapisu, wtedy każdy musi poczekać na zakończenie pierwszego zapisu, a następnie przejść do następnego dostępu.

Wszystkie powyższe są bardzo uproszczone, ale to, co można zobaczyć między procesorem i pamięcią podręczną, po drugiej stronie pamięci podręcznej, pamięcią o stałej szerokości (stała szerokość sram w pamięci podręcznej i stałą szerokość the dram po drugiej stronie nie muszą się zgadzać) po drugiej stronie pamięci podręcznej jest dostępny w "liniach pamięci podręcznej", które są na ogół wielokrotnościami rozmiaru szerokości szyny. to pomaga i boli z wyrównaniem. Powiedz na przykład 0x100 jest granicą linii pamięci podręcznej. Słowo na 0xFE powiedzmy, że jest to koniec jednej linii pamięci podręcznej, a 0x100 to początek następnej. Jeśli miałbyś wykonać 32-bitowy adres odczytu na adres 0xFE, nie tylko musisz wykonać dwa 32-bitowe cykle pamięci, ale dwa pobrania linii pamięci podręcznej. Najgorszym przypadkiem byłoby wyrzucenie dwóch pamięci podręcznych do pamięci, aby zrobić miejsce dla dwóch nowych linii pamięci podręcznej, które pobierasz. Gdybyś użył wyrównanego adresu, nadal byłby zły, ale tylko w połowie tak zły.

Twoje pytanie nie określa procesora, ale natura twojego pytania implikuje x86, który jest dobrze znany z tego problemu. Inne rodziny procesorów nie zezwalają na nieprzypisany dostęp lub musisz wyraźnie wyłączyć błąd wyjątku. Czasami dostęp do niewyrównanego nie jest podobny do x86. Na przykład na co najmniej jednym procesorze, jeśli miałeś 0x12345678 pod adresem 0x100, i 0xAABBCCDD pod adresem 0x104, a wyłączyłeś błąd i wykonałeś 32-bitowy adres odczytu 0x102, otrzymasz 0x56781234. Pojedynczy 32-bitowy odczyt z pasami bajtów obrócony, aby umieścić niższy bajt we właściwym miejscu. Nie, nie mówię o systemie x86, ale o innym procesorze.