Jest to więc związane z pytaniem o uruchomienie serwera Windows na ARM . Tak więc przesłanka mojego pytania brzmi: czy kod maszynowy może być tłumaczony z jednej architektury na drugą w celu wykonania pliku binarnego na architekturze innej niż ta, na której został skompilowany.
QEMU i inne emulatory mogą tłumaczyć instrukcje w locie, a zatem uruchomić plik wykonywalny na komputerze, dla którego nie został skompilowany. Dlaczego nie zrobić tego tłumaczenia z wyprzedzeniem, zamiast w locie, aby przyspieszyć proces? Z mojego dość ograniczoną wiedzę na montaż, większość instrukcji podoba MOV
, ADD
a inne powinny być przenośne na wszystkich architekturach.
Wszystko, co nie ma bezpośredniego mapowania, można zmapować na inny zestaw instrukcji, ponieważ wszystkie maszyny są Turing Complete. Czy zrobienie tego byłoby zbyt skomplikowane? Czy to nie zadziałałoby z jakiegoś powodu, którego nie znam? Czy to zadziała, ale nie przyniesie lepszych rezultatów niż użycie emulatora?
źródło
Odpowiedzi:
Krótka odpowiedź : nie można przetłumaczyć skompilowanego, połączonego pliku wykonywalnego. Chociaż jest to technicznie możliwe, jest wysoce nieprawdopodobne do osiągnięcia (patrz poniżej). Jeśli jednak masz plik źródłowy zestawu (zawierający instrukcje i etykiety), jest to bardzo możliwe (chociaż jeśli w jakiś sposób uzyskasz źródło zestawu, chyba że program jest napisany w zestawie, powinieneś mieć oryginalny kod źródłowy programu jako cóż, lepiej więc skompiluj to dla innej architektury na początek).
Długa odpowiedź :
Wiem, że może to wydawać się łatwe, ale w praktyce jest to prawie niemożliwe z kilku głównych powodów. Na początek różne zestawy instrukcji używają zasadniczo różnych trybów adresowania, różnych struktur kodu operacyjnego, różnych rozmiarów słów, a niektóre nawet nie mają potrzebnych instrukcji.
Powiedzmy, że musisz zastąpić instrukcję
XYZ
dwiema kolejnymi instrukcjamiABC
orazDEF
. Teraz skutecznie przesunąłeś wszystkie adresy względne / przesunięcia w całym programie od tego momentu, więc musisz przeanalizować i przejść przez cały program i zaktualizować przesunięcia (zarówno przed zmianą, jak i po niej). Powiedzmy, że jedna z przesunięć znacznie się zmienia - teraz musisz zmienić tryby adresowania, które mogą zmienić rozmiar adresu. To znowu zmusi cię do ponownego przeskanowania całego pliku i ponownego obliczenia wszystkich adresów, i tak dalej, i tak po czwarte.Podczas pisania programów asemblerowych możesz używać etykiet, ale procesor tego nie robi - po skompletowaniu pliku wszystkie etykiety są obliczane jako lokalizacje względne, bezwzględne lub przesunięte. Możesz zobaczyć, dlaczego to szybko staje się nietrywialnym zadaniem, a prawie niemożliwe. Zastąpienie pojedynczej instrukcji może wymagać przejścia całego programu setki razy przed przejściem dalej.
Tak, ale spójrz na problemy, które przedstawiłem powyżej. Co z rozmiarem słowa maszyny? Długość adresu? Czy ma nawet te same tryby adresowania? Ponownie nie możesz po prostu „znaleźć i zamienić” instrukcje. Każdy segment programu ma konkretnie określony adres. Przeskoki do innych etykiet są zastępowane literalnymi lub przesuniętymi adresami pamięci podczas składania programu.
Masz 100% rację, że jest to możliwe i byłoby o wiele szybsze . Jednak napisanie programu do osiągnięcia tego celu jest niewiarygodnie trudne i wysoce nieprawdopodobne, jeśli nie z wyjątkiem problemów, które przedstawiłem powyżej.
Jeśli posiadasz aktualny kod źródłowy zestawu, przetłumaczenie kodu maszynowego na inną architekturę zestawu instrukcji byłoby banalne. Sam kod maszynowy jest jednak składany , więc bez źródła asemblera (które zawiera różne etykiety używane do obliczania adresów pamięci) staje się niezwykle trudne. Ponownie zmiana pojedynczej instrukcji może zmienić przesunięcia pamięci w całym programie i wymagać setek przejść w celu ponownego obliczenia adresów.
Wykonanie tego dla programu z kilkoma tysiącami instrukcji wymagałoby dziesiątek, jeśli nie setek tysięcy przejść. W przypadku stosunkowo małych programów może to być możliwe, ale pamiętaj, że liczba przejść wzrośnie wykładniczo wraz z liczbą instrukcji maszynowych w programie. Dla każdego programu o wystarczającej wielkości jest to prawie niemożliwe.
źródło
Tak, to, co sugerujesz, może być i zostało zrobione. Nie jest to zbyt powszechne i nie znam żadnych obecnych systemów, które wykorzystują tę technikę, ale zdecydowanie jest w zakresie technicznej wykonalności.
Kiedyś dużo się działo, aby umożliwić przenoszenie kodu z jednego systemu do drugiego, zanim ktokolwiek osiągnął nawet prymitywną „przenośność”, którą mamy teraz. Wymagało to złożonej analizy „źródła” i mogło być utrudnione przez modyfikację kodu i inne dziwne praktyki, ale nadal tak było.
Ostatnio systemy takie jak IBM System / 38 - iSeries - System i skorzystały z możliwości przenoszenia kodu pośredniego (podobnego do kodów bajtowych Java) przechowywanego w skompilowanych programach, aby umożliwić przenoszenie między niekompatybilnymi architekturami zestawów instrukcji.
źródło
Sam kod maszynowy jest specyficzny dla architektury.
Języki, które pozwalają na łatwą przenośność na wielu architekturach (Java jest prawdopodobnie najbardziej znana), mają zwykle bardzo wysoki poziom, wymagając zainstalowania interpreterów lub platform na komputerze, aby mogły działać.
Te frameworki lub interpretatory są napisane dla każdej konkretnej architektury systemu, na której będą uruchamiane, a zatem same w sobie nie są bardziej przenośne niż „normalny” program.
źródło
Oczywiście, jest to możliwe. Co to jest kod maszynowy? To tylko językktóre rozumie dany komputer. Pomyśl o sobie jak o komputerze i starasz się zrozumieć książkę napisaną po niemiecku. Nie możesz tego zrobić, ponieważ nie rozumiesz języka. Teraz, jeśli weźmiesz niemiecki słownik i odszukasz słowo „Kopf”, zobaczysz, że przekłada się ono na angielskie słowo „głowa”. Używany słownik to tak zwana warstwa emulacji w świecie komputerów. Łatwe, prawda? Cóż, staje się trudniejsze. Weź niemieckie słowo „Schadenfruede” i przetłumacz je na angielski. Zobaczysz, że nie ma słowa w języku angielskim, ale istnieje definicja. Ten sam problem istnieje w świecie komputerów, tłumacząc rzeczy, które nie mają odpowiednika. Utrudnia to bezpośrednie porty, ponieważ twórcy warstwy emulacji muszą dokonać interpretacji tego słowa i sprawić, aby komputer-host zrozumiał. Czasami po prostu nie działa tak, jak można by się spodziewać. Wszyscy widzieliśmy śmieszne tłumaczenia książek, zwrotów itp. W Internecie, prawda?
źródło
Opisany proces nazywa się rekompilacją statyczną i został wykonany, ale nie w ogólnie obowiązujący sposób. Oznacza to, że jest to niemożliwe, zostało to zrobione wiele razy, ale wymagało to pracy ręcznej.
Istnieje wiele przykładów historycznych wartych zbadania, ale są one mniej zdolne do wykazania współczesnych obaw. Znalazłem dwa przykłady, które zasadniczo powinny sprawić, że jakikolwiek sceptyczny sceptyk zostanie zakwestionowany przez ludzi, którzy twierdzą, że wszystko jest trudne, jest niemożliwe.
Najpierw ten facet wykonał pełną statyczną architekturę ORAZ platformę dla ROM NES. http://andrewkelley.me/post/jamulator.html
Robi bardzo dobre uwagi, ale konkluduje, że JIT jest jeszcze bardziej praktyczny. Właściwie nie jestem pewien, dlaczego nie wiedział, że w tej sytuacji może to być sytuacja, którą większość ludzi bierze pod uwagę. Bez skrótów, wymagający pełnej dokładności cyklu i zasadniczo bez użycia ABI. Gdyby to było wszystko, moglibyśmy wrzucić ten pomysł do kosza i nazwać go dniem, ale to nie wszystko i nigdy nie było… Skąd to wiemy? Ponieważ wszystkie udane projekty nie stosowały tego podejścia.
Teraz, gdy możliwości są mniej oczywiste, wykorzystaj platformę, którą już masz ... Starcraft na podręcznym systemie ARM? Tak, podejście działa, gdy nie ograniczysz zadania dokładnie do tego, co zrobiłbyś dynamicznie. Korzystając z Winlib, wszystkie wywołania platformy Windows są rodzime, musimy się tylko martwić architekturą.
http://www.geek.com/games/starcraft-has-been-reverse-engineered-to-run-on-arm-1587277/
Rzucałbym pączkom dolary, że spowolnienie jest prawie znikome, biorąc pod uwagę, że ręczna pandora ARM jest tylko trochę silniejsza niż Pi. Narzędzia, których użył, znajdują się w tym repozytorium.
https://github.com/notaz/ia32rtools
Ten facet zdekompilował się bardzo ręcznie, uważam, że proces ten można znacznie zautomatyzować przy mniejszym nakładzie pracy ... ale w tej chwili wciąż wysiłku miłości. Nie pozwól nikomu powiedzieć, że coś jest niemożliwe, nawet nie powiem, że to nie jest praktyczne ... To może być praktyczne, gdy tylko wprowadzisz nowy sposób, aby to zrobić.
źródło
Teoretycznie tak, można to zrobić. Największym problemem, który pojawia się w grze, jest tłumaczenie aplikacji dla jednego systemu operacyjnego (lub jądra) na inny. Istnieją znaczne różnice między operacjami niskiego poziomu jądra systemu Windows, Linux, OSX i iOS, z których muszą korzystać wszystkie aplikacje dla tych urządzeń.
Po raz kolejny teoretycznie można napisać aplikację, która mogłaby rozpakować aplikację, a także cały kod maszynowy powiązany z systemem operacyjnym, na którym została skompilowana, a następnie ponownie skompilować cały ten kod maszynowy dla innego urządzenia. Byłoby to jednak wysoce nielegalne w prawie każdym przypadku i byłoby niezwykle trudne do napisania. Faktem jest, że koła zębate w mojej głowie zaczynają łapać się na samą myśl o tym.
AKTUALIZACJA
Kilka komentarzy poniżej wydaje się nie zgadzać z moją odpowiedzią, jednak myślę, że nie rozumiem tego. Według mojej wiedzy, nie ma aplikacji, która mogłaby pobrać sekwencję bajtów wykonywalnych dla jednej architektury, rozłożyć ją na poziomie kodu bajtowego, w tym wszystkie niezbędne wywołania bibliotek zewnętrznych, w tym wywołania do jądra systemu operacyjnego, i złożyć je dla innego systemu i zapisać wynikowy wykonywalny kod bajtowy . Innymi słowy, nie ma aplikacji, która mogłaby zająć się czymś tak prostym, jak Notepad.exe, rozłożyć mały 190k plik, który jest, i w 100% złożyć go ponownie w aplikację, która mogłaby działać w systemie Linux lub OSX.
Rozumiem, że osoba zadająca pytanie chciała wiedzieć, że jeśli możemy wirtualizować oprogramowanie lub uruchamiać aplikacje za pomocą programów takich jak Wine lub Parallels, dlaczego nie możemy po prostu ponownie przetłumaczyć kodu bajtowego dla różnych systemów. Powodem jest to, że jeśli chcesz w pełni złożyć aplikację dla innej architektury, musisz zdekomponować cały bajt-kod potrzebny do uruchomienia go przed ponownym złożeniem. Każda aplikacja zawiera coś więcej niż tylko plik exe dla komputera z systemem Windows. Wszystkie aplikacje systemu Windows używają obiektów jądra systemu Windows i funkcji niskiego poziomu do tworzenia menu, obszarów tekstowych, metod zmiany rozmiaru okna, rysowania na wyświetlaczu, wysyłania / odbierania komunikatów systemu operacyjnego itd. Itd.
Cały ten bajt-kod musi zostać zdemontowany, jeśli chcesz ponownie złożyć w aplikacji i uruchomić go na innej architekturze.
Aplikacje takie jak Wine interpretują pliki binarne Windows na poziomie bajtów. Rozpoznają połączenia z jądrem i tłumaczą je na powiązane funkcje Linuksa lub emulują środowisko Windows. Ale to nie jest retranslacja bajt po bajcie (lub opcode dla opcode). Jest to bardziej tłumaczenie funkcji dla funkcji i jest to nieco inne.
źródło
Wydaje się, że wszystkim ekspertom brakuje tego punktu: „tłumaczenie” jest złożone, ale bardzo odpowiednie dla komputera (brak inteligentnego, po prostu pracowity). Ale po tłumaczeniu programy wymagają wsparcia systemu operacyjnego, np .: GetWindowVersion nie istnieje w systemie Linux. Zwykle jest to dostarczane przez emulator (bardzo duży). Możesz więc „wstępnie przetłumaczyć” proste programy, ale musisz połączyć się z ogromną biblioteką, aby działać niezależnie. Obrazowanie programów każdego systemu Windows ma własny kernel.dll + user.dll + shell.dll ...
źródło