Rekompilacja statyczna to obiecujący sposób tłumaczenia plików binarnych z obcej architektury na inną docelową architekturę. Byłby szybszy niż Just-In-Time (JIT), ponieważ nie musi kompilować kodu tuż przed uruchomieniem, a ponieważ dodatkowy czas kompilacji może zająć użyteczne jest zoptymalizowanie kodu generującego.
Jednak kompilacja JIT wykorzystuje dynamiczną analizę programu, podczas gdy rekompilacja statyczna opiera się na statycznej analizie programu (stąd nazwa).
W analizie statycznej nie ma informacji o czasie wykonywania.
Głównym problemem jest pośrednie skoki. Termin ten obejmuje kod, który może być generowany z pewnych instrukcji switch
, z użycia wskaźników funkcji lub z polimorfizmu środowiska wykonawczego (think virtual table). To wszystko sprowadza się do instrukcji w postaci:
JMP reg_A
Powiedzmy znasz adres początkowy programu, i postanowił zacząć skompilować instrukcji od tego momentu. Gdy natrafisz na bezpośredni skok, przejdziesz do jego adresu docelowego i będziesz kontynuował jego rekompilację. Kiedy jednak dojdziesz do pośredniego skoku, utkniesz. W niniejszej instrukcji montażu treść reg_A
nie jest znana statycznie. Dlatego nie znamy adresu następnej instrukcji. Zauważ, że w rekompilacji dynamicznej nie mamy tego problemu, ponieważ emulujemy stan wirtualny rejestrów i znamy aktualną zawartość reg_A
. Poza tym, w rekompilacji statycznej, jesteś zainteresowany znalezieniem w tym miejscu możliwych wartości dla , ponieważ chcesz skompilować wszystkie możliwe ścieżki. W analizie dynamicznej potrzebna jest tylko bieżąca wartość do wygenerowania ścieżki, którą aktualnie wykonujesz, jeśli zmienimy jej wartość na reg_A
, nadal będziesz mógł wygenerować inne ścieżki. W niektórych przypadkach analiza statyczna może znaleźć listę kandydatów (jeśli jest to switch
, gdzieś musi być tabela możliwego przesunięcia), ale w ogólnym przypadku po prostu nie wiemy.
Dobrze, mówisz, Ponownie skompilujmy wszystkie instrukcje w pliku binarnym!
Problem polega na tym, że w większości plików binarnych znajdują się zarówno kod, jak i dane. W zależności od architektury, możesz nie być w stanie stwierdzić, która jest która.
Co gorsza, w niektórych architekturach nie ma ograniczeń wyrównania i instrukcji o zmiennej szerokości, i możesz zacząć rozbierać się w pewnym momencie, tylko po to, aby odkryć, że uruchomiłeś swoją rekompilację z przesunięciem.
Weźmy uproszczony zestaw instrukcji zawierającej dwie instrukcje i jeden rejestr A
:
41 xx (size 2): Add xx to `A`.
42 (size 1): Increment `A` by one.
Weźmy następujący binarny programu:
41 42
Załóżmy, że punkt startowy to pierwszy bajt 41
. zrobić:
41 42 (size 2): Add 42 to `A`.
Ale co, jeśli 41 to fragment danych? Następnie program będzie:
42 (size 1): Increment `A` by one.
Problem ten jest powiększany w stare gry, które często były zoptymalizowane bezpośrednio w montażu, a gdzie programista może celowo expect some byte to be interpreted as both code and data, depending on the context!
Nawet gorzej The zrekompilowane program może generować sam kod! Wyobraź sobie rekompilację kompilatora JIT. Wynik nadal generuje kod dla architektury źródłowej i próbuje przeskoczyć do niego, najprawdopodobniej powodując, że program wkrótce umrze. Statyczna rekompilacja kodu, który jest dostępny tylko w środowisku wykonawczym, wymaga nieskończonej sztuczki!
Statyczna analiza binarna to bardzo żywa dziedzina badań (głównie w dziedzinie bezpieczeństwa, szukania luk w systemach, których źródła nie są dostępne), a tak naprawdę znam próbę wyprodukowania NES emulator that tries to statically recompile programs. Artykuł jest bardzo interesujący.
Kompromisem pomiędzy JIT a rekompilacją statyczną byłoby statyczna rekompilacja jak największej ilości kodu, zachowując tylko bity, których nie można przetłumaczyć statycznie.
Zrobiono to kilka razy. Na przykład DEC FX! 32 zrekompilował binaria x86, aby działać na DEC Alpha, często szybciej niż jakikolwiek x86 tego czasu. Nie wystarczyło to jednak do nadrobienia zaległości w zarządzaniu DEC, a Compaq/HP nie przejmował się tym zbytnio. –
Dlaczego emulatory istnieją, jeśli tak jest? Czy to NAPRAWDĘ * to * znacznie trudniejsze niż pisanie emulatora? – user1483857