2011-09-23 7 views
5

Czytałem, że jeśli bit eflags 18 (AC-wyrównanie wyboru) może być modyfikowany, wiesz CPU jest 486 lub nowszy. W 386 bit jest odporny na modyfikacje.Jak działa bit eflags bit 18 (kontrola wyrównania) x86? (Odnośnie sprawdzania dla 386 vs. 486 i późniejszych).

ja podnoszone następujące kod montażowe z this site i dodano uwagi wyczerpujące (pozostawiając nienaruszone składni nieparzyste):

asm 
    mov bx,sx   ; Save the stack pointer to bx (is sx a typo or a real register?). 
    and sp,$fffc   ; Truncate the stack pointer to a 4-byte boundary. 
    pushfl    ; Push the eflags register to the stack. 
    pop eax    ; Pop it into eax. 
    mov ecx,eax   ; Save the original eflags value into ecx. 
    xor eax,$40000  ; Flip bit 18 in eax. 
    push eax    ; Push eax to the stack. 
    popfl     ; Pop modified value into eflags. 
    pushfl    ; Push eflags back onto the stack. 
    pop eax    ; Pop it into eax. 
    xor eax,ecx   ; Get changed bits from the original value. 
    setz al    ; Set al register to 1 if no bits changed (0 otherwise). 
    and sp,$fffc   ; Truncate the stack pointer to a 4-byte boundary. 
    push ecx    ; Push ecx (original eflags) to stack. 
    popfl     ; Pop it into eflags to restore the original value. 
    mov sp,bx   ; Restore the original stack pointer. 
end ['eax','ebx','ecx']; 

CPU 386 to czy rejestr wsp jest ustawiony na 1, na końcu (zakładając od samego początku, że nie jest starsza), a jest 486 lub nowsza w przeciwnym razie. Rozumiem tę część.

Czego nie rozumiem, dlaczego wskaźnik stosu musi zostać obcięty do granicy 4-bajtowej przed wykonaniem testu modyfikacji flagi? Zakładam, że ma on ustawić bit 18, ponieważ jest to bit wyrównania, mimo wszystko ... ale xor z 0x40000 odwróci bit niezależnie od jego wartości. Innymi słowy, test modyfikacji powinien mieć taki sam wynik, niezależnie od wartości początkowej, prawda?

Jeśli odpowiedź brzmi "nie", moim najlepszym [niewykształconym] domysłem "dlaczego" jest: "Może następujące instrukcje push/pop mogłyby wymusić wyrównanie?" Wyrównano by niewyrównany wskaźnik stosu i spowodowało odwrócenie bitu wyrównania od 0 do 1. W takim przypadku udana modyfikacja wydaje się nieskuteczna, i na odwrót. " (EDYCJA: Jest to zdecydowanie niepoprawne, ponieważ bit dopasowania ma na celu raczej wymuszenie niż śledzenie, a ponadto wątpię, aby pop/push wymusił wyrównanie na wcześniej niewyrównanym stosie.)

Nawet jeśli tak jest, jaki jest cel wyrównania wskaźnika stosu ponownie po teście (tuż przed przywróceniem oryginalnych eflagów i wskaźnika stosu)? Czy nie powinno to już być na 4-bajtowej granicy z wcześniej? Jeśli nie, to jak to się mogło zmienić z wartości 4-bajtowych?

Krótko mówiąc, niektóre instrukcje wydają mi się zbędne i wydaje mi się, że brakuje mi czegoś ważnego. Czy ktoś może to wyjaśnić?

(Pytanie boczne: Pierwsza linia kopiuje wartość z "sx" do bx. Nigdy nie widziałem odniesienia do rejestru sx w dowolnym miejscu.To rzeczywiście istnieje, czy jest to literówka? klucz jest dość daleko od klawisza "p", przynajmniej na klawiaturach amerykańskich.)

EDYCJA: Teraz, kiedy to pytanie zostało odebrane, postanowiłem usunąć niepoprawny komentarz z dwóch linii linii w kodzie. Początkowo założyłem, że wyrównanie stosu ustawi bit dopasowania, i napisałem to do mojego komentarza (reszta pytania jest kontynuowana z tą niepoprawną logiką). Zamiast tego bit sprawdzania wyrównania naprawdę polega na wymuszaniu wyrównania (a nie na śledzeniu go), jak wskazuje flolo na odpowiedź dotyczącą sigbusa. Postanowiłem poprawić komentarze, aby uniknąć zamieszania osób z podobnymi pytaniami.

+0

Jakie muzeum włamałeś się, aby znaleźć 386? Lepiej odłóż to z powrotem. –

+0

LOL! Nigdy nie dotknąłem pulpitu 386, o ile wiem ... Znowu zszedłem ze starym 286, ale od razu przeskoczyłem do Pentium 75. Postanowiłem po prostu zakryć moje bazy przed wywołaniem instrukcji CPUID (najpierw upewnij się, że nie masz 386, a następnie przetestuj 21 bitów eflags na obecność CPUID), w sytuacji, gdy jakiś szalony kiedykolwiek zdecydował się użyć mojego kodu na antycznym sprzęcie. Chyba jestem po prostu analna, ale przynajmniej nie sprawdzam 8-bitowych i 16-bitowych procesorów, ponieważ nowoczesne kompilatory najwyraźniej nawet nie generują kodu. ;) –

Odpowiedz

4

Moje przypuszczenie jest bardzo proste: Kod nie chce sigbus. Jeśli wyrównanie zaznaczenia nie jest ustawione, a użytkownik je ustawił, faktycznie włącza sprawdzanie wyrównania (gdy ustawienie to działa). A kiedy wskaźnik stosu nie jest wyrównany do granicy 4-bajtowej, zgadnij, co się dzieje? Masz niewyrównany dostęp do pamięci, w wyniku czego powstaje sigbus. A jeśli nie chcesz, aby dostęp do nieprawidłowej pamięci miał miejsce (ponieważ chcesz tylko zmienić bit do celów testowych), musisz uważać, aby wszystkie próby dostępu podczas testowania zakładały najgorszy przypadek (czyli: , a twój stos był przed wyrównaniem, ponieważ nie musi, jak do tej pory kontrole zostały wyłączone).

+0

Dziękuję. To doskonale wyjaśnia pierwszą instrukcję wyrównania; Nie rozpoznałem znaczenia ustawionego bitu (potencjał dla sigbusa). Czy wiesz, dlaczego wyrównanie jest ustawione ponownie później, gdy wydaje się jasne, że pamięć powinna być już wyrównana? –

+1

+1 To jest powód (wygląda na to, że pochodzi z fragmentu pascala i dlatego ucieka z 16-bitowego stosu, co nie gwarantuje, że jest wyrównany do 32-bitów). Drugie wyrównanie sp jest rzeczywiście zbędne, prawdopodobnie tylko po to, aby podkreślić, że sprawdzanie wyrównania może być potencjalnie ponownie włączone przez instrukcję popfl. – user786653

+0

Jeszcze raz dziękuję, chłopaki! Dopóki nie pojawi się ktoś inny i nie wyjaśni, dlaczego drugie wyrównanie jest rzeczywiście konieczne, zaznaczę to jako zaakceptowane i założę się, że drugie wyrównanie jest zbędne. –

2

Brak rejestru SX. Albo literówka, albo odnosi się do lokalizacji w pamięci.

+0

Dzięki. Miałem to na uwadze, kiedy napisałem komentarz o zapisywaniu wskaźnika stosu w pierwszej linii, ale dobrze jest mieć potwierdzenie, że nie jest to jakiś niejasny sub-rejestr. (Myślę, że nie ma sensu i tak: 16-bitowy sp zostaje przywrócony z 16-bitowym bx, więc nie mniej niż 16 bitów zrobi do zapisywania oryginalną wartość bx w pierwszej kolejności). –