Il codice macchina può essere tradotto in un'architettura diversa?


11

Quindi questo è in qualche modo correlato a una domanda sull'esecuzione di un server Windows su ARM . Quindi la premessa della mia domanda è: il codice macchina può essere tradotto da un'architettura all'altra per eseguire un binario su un'architettura diversa da quella su cui è stato compilato.

QEMU e altri emulatori possono tradurre le istruzioni al volo e quindi eseguire un eseguibile su un computer per il quale non è stato compilato. Perché non fare questa traduzione in anticipo, anziché al volo per accelerare il processo? Dalla mia conoscenza un po 'limitato di assemblaggio, la maggior parte delle istruzioni piace MOV, ADDe altri dovrebbero essere portabile su architetture.

Tutto ciò che non ha una mappatura diretta può essere mappato su un altro set di istruzioni, poiché tutte le macchine sono Turing complete. Fare questo sarebbe troppo complicato? Non funzionerebbe affatto per qualche motivo con cui non ho familiarità? Funzionerebbe, ma non produrrebbe risultati migliori rispetto all'utilizzo di un emulatore?


La tecnica è probabilmente caduta in disgrazia perché (oltre alla sua sfaldatezza) non è necessaria molto. Oggigiorno la portabilità / standardizzazione è (leggermente) migliore (se non altro perché Wintel ha conquistato il mondo) e, dove l'emulazione cross-machine è davvero necessaria (ad esempio, per un emulatore di telefono in un ambiente di sviluppo di app), l'emulazione diretta fornisce un risultato più affidabile e preciso. Inoltre, i processori sono abbastanza veloci che il costo dell'emulazione non è un problema così grave come in passato.
Daniel R Hicks,

Risposte:


6

La risposta breve : non è possibile tradurre un eseguibile compilato e collegato. Sebbene tecnicamente possibile, è altamente improbabile da realizzare (vedi sotto). Tuttavia , se si dispone del file sorgente dell'assembly (contenente le istruzioni e le etichette), è molto possibile farlo (anche se in qualche modo si ottiene l'origine dell'assembly, a meno che il programma non sia scritto in assembly, è necessario disporre del codice sorgente originale del programma come bene, quindi faresti meglio a compilarlo per la diversa architettura per cominciare).


La lunga risposta :

QEMU e altri emulatori possono tradurre le istruzioni al volo e quindi eseguire un eseguibile su un computer per il quale non è stato compilato. Perché non fare questa traduzione in anticipo, anziché al volo per accelerare il processo?

So che in linea di principio potrebbe sembrare facile, ma in pratica è quasi impossibile per alcuni motivi principali. Per iniziare, diversi set di istruzioni utilizzano modalità di indirizzamento in gran parte diverse, strutture di codici operativi diverse, dimensioni delle parole diverse e alcune non hanno nemmeno le istruzioni necessarie.

Diciamo che dovevi sostituire le istruzioni XYZcon altre due istruzioni ABCe DEF. Ora hai effettivamente spostato tutti gli indirizzi relativi / offset nell'intero programma da quel punto in poi, quindi dovrai analizzare e passare attraverso l'intero programma e aggiornare gli offset (sia prima che dopo la modifica). Ora, supponiamo che uno degli offset cambi in modo significativo - ora è necessario modificare le modalità di indirizzamento, che potrebbero cambiare la dimensione dell'indirizzo. Questo ti costringerà di nuovo a scansionare nuovamente l'intero file e ricalcolare tutti gli indirizzi, e così via e così quarto.

Quando si scrivono programmi di assemblaggio, è possibile utilizzare le etichette, ma la CPU no: quando il file viene assemblato, tutte le etichette vengono calcolate come posizioni relative, assolute o offset. Puoi capire perché questo diventa rapidamente un compito non banale e quasi impossibile. La sostituzione di una singola istruzione potrebbe richiedere di passare l'intero programma centinaia di volte prima di procedere.

Dalla mia conoscenza piuttosto limitata dell'assemblaggio, la maggior parte delle istruzioni come MOV, ADD e altre dovrebbero essere trasportabili attraverso architetture.

Sì, ma guarda ai problemi che ho descritto sopra. E le dimensioni della parola della macchina? Indirizzo lunghezza? Ha anche le stesse modalità di indirizzamento? Ancora una volta, non puoi semplicemente "trovare e sostituire" le istruzioni. Ogni segmento di un programma ha un indirizzo specificamente definito. I salti su altre etichette vengono sostituiti con indirizzi di memoria letterali o offset quando viene assemblato un programma.

Tutto ciò che non ha una mappatura diretta può essere mappato su un altro set di istruzioni, poiché tutte le macchine sono Turing complete. Fare questo sarebbe troppo complicato? Non funzionerebbe affatto per qualche motivo con cui non ho familiarità? Funzionerebbe, ma non produrrebbe risultati migliori rispetto all'utilizzo di un emulatore?

Hai ragione al 100% sul fatto che sia possibile e sarebbe molto più veloce . Tuttavia, scrivere un programma per raggiungere questo obiettivo è incredibilmente difficile e altamente improbabile, se non per altro, tranne i problemi che ho descritto sopra.

Se si disponesse dell'attuale codice sorgente dell'assembly, sarebbe banale tradurre il codice macchina in un'altra architettura di set di istruzioni. Il codice macchina stesso, tuttavia, è assemblato , quindi senza la sorgente assembly (che contiene varie etichette utilizzate per calcolare gli indirizzi di memoria), diventa incredibilmente difficile. Ancora una volta, la modifica di una singola istruzione potrebbe modificare gli offset di memoria nell'intero programma e richiedere centinaia di passaggi per ricalcolare gli indirizzi.

Fare questo per un programma con poche migliaia di istruzioni richiederebbe decine se non centinaia di migliaia di passaggi. Per programmi relativamente piccoli, questo può essere possibile, ma ricorda che il numero di passaggi aumenterà esponenzialmente con il numero di istruzioni della macchina nel programma. Per qualsiasi programma di dimensioni abbastanza decenti, è quasi impossibile.


In sostanza ciò che si deve fare è "decompilare" o "disassemblare" il codice dell'oggetto sorgente. Per un codice relativamente semplice (in particolare il codice generato da alcuni compilatori o pacchetti di generazione di codice in cui esiste uno "stile" noto) il reinserimento di etichette e simili è abbastanza semplice. Certamente, i nuovi compilatori altamente ottimizzanti genererebbero codice che era molto più difficile "radunare" in questo modo.
Daniel R Hicks,

@DanH se hai il codice oggetto sorgente, hai praticamente il sorgente assembly ( non il codice macchina). Il file oggetto contiene sequenze denominate (leggi: etichettate) di codice macchina da collegare insieme. Il problema si presenta quando si collegano i file di codice oggetto in un eseguibile. Questi segmenti più piccoli possono essere gestiti (o decodificati) molto più facilmente di un intero eseguibile collegato.
Breakthrough

Certamente, alcuni formati di file oggetto rendono il lavoro un po 'più semplice. Alcuni possono anche contenere informazioni di debug, consentendo di ripristinare la maggior parte delle etichette. Altri sono meno utili. In alcuni casi molte di queste informazioni vengono conservate anche nel formato di file collegato, in altri casi no. Esistono moltissimi formati di file diversi.
Daniel R Hicks,

2

Sì, ciò che suggerisci può essere ed è stato fatto. Non è troppo comune e non conosco alcun sistema attuale che utilizza la tecnica, ma è decisamente ben compreso nel campo della fattibilità tecnica.

Una volta si faceva molto per consentire il porting del codice da un sistema all'altro, prima che qualcuno avesse raggiunto la "portabilità" anche grezza che abbiamo ora. Richiedeva un'analisi complessa della "fonte" e poteva essere ostacolato dalla modifica del codice e da altre pratiche strane, ma era ancora fatto.

Più di recente, sistemi come IBM System / 38 - iSeries - System i hanno sfruttato la portabilità del codice intermedio (simile ai bytecode Java) memorizzati con programmi compilati per consentire la portabilità tra architetture di set di istruzioni incompatibili.


Concordo sul fatto che ciò sia stato fatto, di solito con set di istruzioni molto più vecchi (più semplici). C'è stato un progetto IBM negli anni '70 per convertire i vecchi programmi binari 7xx in System / 360.
segatura,

1

Il codice macchina stesso è specifico dell'architettura.

I linguaggi che consentono una facile portabilità su più architetture (Java è probabilmente il più noto) tendono ad essere di altissimo livello, richiedendo l'installazione di interpreti o framework su una macchina affinché funzionino.

Questi framework o interpreti sono scritti per ogni specifica architettura di sistema su cui verranno eseguiti e quindi non sono, di per sé, più portabili di un programma "normale".


2
Anche i linguaggi compilati sono portatili, non solo linguaggi interpretati, è il compilatore che è specifico dell'architettura in quanto è ciò che alla fine traduce il codice in ciò che la piattaforma su cui è in grado di riconoscere. L'unica differenza è che le lingue compilate vengono tradotte al momento della compilazione e le lingue interpretate vengono tradotte riga per riga secondo necessità.
MaQleod,

1

Assolutamente possibile. Cos'è il codice macchina? È solo la linguache un determinato computer capisce. Pensa a te stesso come al computer e stai cercando di capire un libro scritto in tedesco. Non puoi farlo, perché non capisci la lingua. Ora, se dovessi prendere un dizionario tedesco e cercare la parola "Kopf", la vedresti tradurre nella parola inglese "testa". Il dizionario che hai usato si chiama strato di emulazione nel mondo dei computer. Facile vero? Bene, diventa più difficile. Prendi la parola tedesca "Schadenfruede" e traducila in inglese. Vedrai che non c'è una parola in inglese, ma c'è una definizione. Lo stesso problema esiste nel mondo dei computer, traducendo cose che non hanno una parola equivalente. Ciò rende difficili le porte dirette poiché gli sviluppatori del livello di emulazione devono interpretare cosa significa quella parola e far capire al computer host. A volte semplicemente non funziona come ci si aspetterebbe. Abbiamo visto tutti traduzioni divertenti di libri, frasi, ecc. Su Internet, giusto?


1

Il processo che descrivi si chiama Ricompilazione statica ed è stato fatto, ma non in un modo generalmente applicabile. Significa che è impossibile, è stato fatto molte volte, ma ha richiesto un lavoro manuale.

Ci sono molti esempi storici che vale la pena ricercare, ma sono meno in grado di dimostrare le preoccupazioni moderne. Ho trovato due esempi che dovrebbero essenzialmente mettere in discussione qualsiasi completo scettico sulle persone che sostengono che tutto sia difficile.

Prima questo ragazzo ha realizzato un'archetettura statica completa e una piattaforma per una ROM NES. http://andrewkelley.me/post/jamulator.html

Fa alcuni punti molto positivi, ma conclude che JIT è ancora più pratico. In realtà non sono sicuro del perché non sapesse già che per questa situazione, questo potrebbe essere il tipo di situazione che la maggior parte delle persone considera. Non prendere scorciatoie, richiedere l'accuratezza del ciclo completo e essenzialmente non usare affatto ABI. Se fosse tutto ciò che potevamo, potremmo buttare il concetto nella spazzatura e chiamarlo un giorno, ma non è tutto e non lo è mai stato ... Come facciamo a saperlo? Perché tutti i progetti di successo non hanno utilizzato questo approccio.

Ora, per le possibilità meno ovvie, sfrutta la piattaforma che hai già ... Starcraft su un palmare ARM Linux? Sì, l'approccio funziona quando non si vincola l'attività esattamente a ciò che si farebbe in modo dinamico. Usando Winlib le chiamate della piattaforma Windows sono tutte native, tutto ciò di cui dobbiamo preoccuparci è l'architettura.

http://www.geek.com/games/starcraft-has-been-reverse-engineered-to-run-on-arm-1587277/

Darei dollari alle ciambelle che il rallentamento è quasi trascurabile, considerando che la pandora portatile ARM è solo un po 'più forte del Pi. Gli strumenti che ha usato sono in questo repository.

https://github.com/notaz/ia32rtools

Quel ragazzo si è decompilato molto manualmente, credo che il processo potrebbe essere automatizzato in modo significativo con meno lavoro ... ma al momento è ancora un lavoro d'amore. Non lasciare che nessuno ti dica che qualcosa non è possibile, non lasciarmi nemmeno dire che non è pratico ... Potrebbe essere pratico, non appena innoverai un nuovo modo di farlo.


0

Teoricamente, sì, questo può essere fatto. Il problema più grande che entra in gioco è la traduzione di un'applicazione per un sistema operativo (o kernel) in un altro. Esistono differenze significative tra le operazioni di basso livello dei kernel Windows, Linux, OSX e iOS, che tutte le applicazioni per tali dispositivi devono utilizzare.

Ancora una volta, in teoria, si potrebbe scrivere un'applicazione in grado di scomporre un'applicazione e tutto il codice macchina associato al sistema operativo su cui è stata compilata l'esecuzione e quindi ricompilare tutto quel codice macchina per un altro dispositivo. Tuttavia, ciò sarebbe altamente illegale in quasi tutti i casi e sarebbe estremamente difficile da scrivere. In effetti, gli ingranaggi nella mia testa stanno iniziando a bloccarsi solo a pensarci.

AGGIORNARE

Un paio di commenti qui sotto sembrano non essere d'accordo con la mia risposta, tuttavia, penso che manchino il punto. Per quanto ne sappia, non esiste un'applicazione in grado di prendere una sequenza di byte eseguibili per un'architettura, scomporla a livello di bytecode, comprese tutte le chiamate necessarie a librerie esterne, comprese le chiamate al kernel del sistema operativo sottostante, e rimontarlo per un altro sistema e salvare il codice eseguibile risultante . In altre parole, non esiste un'applicazione che possa richiedere qualcosa di semplice come Notepad.exe, scomporre il piccolo file 190k che è e ricomporlo al 100% in un'applicazione che può essere eseguita su Linux o OSX.

Comprendo che chi si pone la domanda voleva sapere che se possiamo virtualizzare il software o eseguire applicazioni attraverso programmi come Wine o Parallels, perché non possiamo semplicemente tradurre nuovamente il codice byte per sistemi diversi. Il motivo è che se si desidera riassemblare completamente un'applicazione per un'altra architettura, è necessario scomporre tutto il codice byte necessario per eseguirlo prima di riassemblarlo. C'è di più in ogni applicazione oltre al file exe, diciamo, per un computer Windows. Tutte le applicazioni Windows utilizzano oggetti e funzioni del kernel di Windows di basso livello per creare menu, aree di testo, metodi per ridimensionare le finestre, disegnare sul display, inviare / ricevere messaggi del sistema operativo e così via ...

Tutto quel codice byte deve essere smontato se si desidera riassemblare l'applicazione e farlo funzionare su un'architettura diversa.

Applicazioni come Wine interpretano i binari di Windows a livello di byte. Riconoscono le chiamate al kernel e traducono tali chiamate in funzioni Linux correlate o emulano l'ambiente Windows. Ma questa non è una ritrasmissione byte per byte (o opcode per opcode). È più una traduzione funzione per funzione e questo è un po 'diverso.


Non è affatto teorico. E ci sono molte applicazioni che eseguono altri binari su diversi sistemi operativi. Hai sentito parlare del vino? Esegue binari di Windows su diversi sistemi operativi, come Linux, Solaris, Mac OSX, BSD e altri.
Keltari,

La differenza nei sistemi operativi può essere facilmente affinata sulla maggior parte dei sistemi utilizzando un hypervisor per eseguire più sistemi operativi (o per eseguire un "livello" come Wine su un sistema che emula un altro). AFAIK, tutti i processori "moderni" non incorporati sono "virtualizzabili", quindi ciò non richiede emulazione / traduzione del set di istruzioni.
Daniel R Hicks,

0

Sembra che a tutti gli esperti manchi questo punto: la "traduzione" è complessa ma molto adatta al computer (non intelligente, solo laboriosa). Ma dopo la traduzione, i programmi hanno bisogno del supporto del sistema operativo, ad esempio: GetWindowVersion non esiste in Linux. Questo è normalmente fornito dall'emulatore (molto grande). Quindi potresti "pre-tradurre" un semplice programma ma devi collegarti a un'enorme libreria per eseguire in modo indipendente. I programmi di imaging di ogni Windows vengono forniti con kernel.dll + user.dll + shell.dll ...


Non è solo laborioso, richiede intelligenza. Ad esempio, supponi di vedere alcuni calcoli il cui risultato determina l'indirizzo a cui vai, che potrebbe trovarsi nel mezzo di qualcosa che sembra essere una singola istruzione.
David Schwartz,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.