O código da máquina pode ser traduzido para uma arquitetura diferente?

11

Portanto, isso está relacionado a uma pergunta sobre executando um servidor Windows no ARM . Assim, a premissa da minha pergunta é: o código da máquina pode ser traduzido de uma arquitetura para outra para executar um binário em uma arquitetura diferente da que foi compilada para ser executada.

O QEMU e outros emuladores podem traduzir as instruções em tempo real e, portanto, executar um executável em um computador para o qual não foi compilado. Por que não fazer essa tradução antes do tempo, em vez de em tempo real para acelerar o processo? Do meu conhecimento relativamente limitado de montagem, a maioria das instruções como MOV , ADD e outras deve ser portátil entre as arquiteturas.

Qualquer coisa que não tenha um mapeamento direto pode ser mapeado para algum outro conjunto de instruções, já que todas as máquinas são Turing Complete. Isso seria complicado demais? Não funcionaria de forma alguma por algum motivo que eu não estou familiarizado? Funcionaria, mas não produziria melhores resultados do que usar um emulador?

    
por Kibbee 26.08.2011 / 22:52

7 respostas

6

A resposta curta : você não pode traduzir um executável vinculado compilado. Embora seja tecnicamente possível, é altamente improvável de realizar (veja abaixo). No entanto , se você tiver o arquivo de origem do assembly (contendo as instruções e rótulos), é muito possível fazer (embora se você de alguma forma obtiver a fonte de montagem, a menos que o programa é escrito em assembly, você deve ter o código-fonte do programa original também, então seria melhor compilar para a arquitetura diferente para começar).

A resposta longa :

QEMU and other emulators can translate the instructions on the fly, and therefore run an executable on a computer it wasn't compiled for. Why not do this translation ahead of time, instead of on the fly in order to speed up the process?

Eu sei que pode parecer fácil em princípio, mas na prática, é quase impossível por algumas razões principais. Para começar, diferentes conjuntos de instruções usam modos de endereçamento amplamente diferentes, estruturas opcode diferentes, tamanhos de palavras diferentes, e alguns nem têm as instruções que você precisa.

Digamos que você precise substituir a instrução XYZ por mais duas instruções, ABC e DEF . Agora você deslocou efetivamente todos os endereços relativos / deslocados de todo o programa a partir de então, então você precisaria analisar e percorrer todo o programa e atualizar os deslocamentos (antes e depois da mudança). Agora, digamos que um dos deslocamentos muda significativamente - agora você precisa alterar os modos de endereçamento, o que pode alterar o tamanho do endereço. Isso irá forçá-lo novamente a varrer novamente todo o arquivo e recalcular todos os endereços, e assim por diante e em quarto lugar.

Quando você escreve programas de montagem, pode usar rótulos, mas a CPU não - quando o arquivo é montado, todos os rótulos são calculados para serem relativos, absolutos ou deslocados. Você pode ver porque isso rapidamente se torna uma tarefa não trivial e quase impossível. Substituir uma instrução single pode exigir que você passe por todo o programa centenas de vezes antes de prosseguir.

From my somewhat limited knowledge of assembly, most of the instructions like MOV, ADD and others should be portable across architectures.

Sim, mas observe os problemas que descrevi acima. E quanto ao tamanho da palavra da máquina? Comprimento do endereço? Tem mesmo os mesmos modos de endereçamento? Novamente, você não pode simplesmente "encontrar e substituir" instruções. Cada segmento de um programa tem um endereço especificamente definido. Saltos para outras etiquetas são substituídos por endereços literais ou de memória offset quando um programa é montado.

Anything that's doesn't have a direct mapping could be mapped to some other set of instructions, since all machines are Turing Complete. Would doing this be too complicated? Would it not work at all for some reason I'm unfamiliar with? Would it work, but yield no better results than using an emulator?

Você está 100% correto de que é possível e seria muito mais rápido . No entanto, escrever um programa para realizar isso é incrivelmente difícil e altamente improvável, se não for para qualquer coisa, exceto os problemas que descrevi acima.

Se você tivesse o código-fonte real do assembly, seria trivial traduzir o código da máquina para outra arquitetura de conjunto de instruções. O próprio código da máquina, no entanto, é montado , portanto, sem a fonte de montagem (que contém vários rótulos usados para calcular os endereços de memória), torna-se incrivelmente difícil. Novamente, a alteração de uma única instrução pode alterar os deslocamentos de memória em todo o programa e exigir centenas de passes para recalcular os endereços.

Fazer isso para um programa com alguns milhares de instruções exigiria dezenas, se não centenas, de milhares de passes. Para programas relativamente pequenos, isso pode ser possível, mas lembre-se de que o número de passes aumentará exponencialmente com o número de instruções de máquina no programa. Para qualquer programa de tamanho decente, é quase impossível.

    
por 27.08.2011 / 00:48
2

Sim, o que você sugere pode e foi feito. Não é muito comum, e eu não conheço nenhum sistema atual que use a técnica, mas está definitivamente dentro do campo de viabilidade técnica.

Costumava ser feito muito para permitir a transferência de código de um sistema para outro, antes que alguém conseguisse a "portabilidade" bruta que temos agora. Exigiu uma análise complexa da "fonte" e poderia ser bloqueada pela modificação de código e outras práticas excêntricas, mas ainda era feito.

Mais recentemente, sistemas como o IBM System / 38 - iSeries - System i tiraram proveito da portabilidade do código intermediário (semelhante aos bytecodes Java) armazenados com programas compilados para permitir a portabilidade entre arquiteturas de conjuntos de instruções incompatíveis.

    
por 26.08.2011 / 23:17
1

O próprio código da máquina é específico da arquitetura.

As linguagens que permitem fácil portabilidade em várias arquiteturas (Java é provavelmente o mais conhecido) tendem a ser de nível muito alto, exigindo a instalação de intérpretes ou frameworks em uma máquina para que funcionem.

Esses frameworks ou intérpretes são escritos para cada arquitetura de sistema específica na qual eles serão executados e, portanto, não são, por si só, mais portáteis do que um programa "normal".

    
por 26.08.2011 / 23:03
1

Absolutamente, é possível. O que é código de máquina? É apenas a linguagem que um computador em particular entende. Pense em você como o computador e você está tentando entender um livro escrito em alemão. Você não pode fazer isso, porque você não entende a língua. Agora, se você pegasse um dicionário alemão e procurasse a palavra "Kopf", veria traduzindo para a palavra inglesa "head". O dicionário que você usou é chamado de camada de emulação no mundo da computação. Fácil certo? Bem, fica mais difícil. Pegue a palavra alemã "Schadenfruede" e traduza para o inglês. Você verá que não há palavra no idioma inglês, mas há uma definição. O mesmo problema existe no mundo dos computadores, traduzindo coisas que não possuem uma palavra equivalente. Isso torna as portas diretas difíceis, já que os desenvolvedores da camada de emulação precisam interpretar o significado dessa palavra e fazer com que o computador host a entenda. Às vezes, simplesmente não funciona do jeito que se esperaria. Todos nós já vimos traduções engraçadas de livros, frases, etc. na internet, certo?

    
por 27.08.2011 / 01:36
1

O processo que você descreve é chamado de Recompilação Estática, e tem sido feito, mas não de uma maneira geralmente aplicável. O que significa que é impossível, foi feito muitas vezes, mas exigiu trabalho manual.

Existem muitos exemplos históricos que vale a pena pesquisar, mas são menos capazes de demonstrar as preocupações modernas. Eu encontrei dois exemplos que deveriam essencialmente fazer qualquer céptico completo questionar as pessoas que afirmam que tudo é impossível.

Primeiro, esse cara fez uma arquitetura e plataforma estática completa para uma ROM NES. link

Ele faz alguns pontos muito bons, mas conclui que o JIT ainda é mais prático. Na verdade, não sei por que ele ainda não sabia que, para essa situação, esse poderia ser o tipo de situação que a maioria das pessoas considera. Não tendo atalhos, exigindo precisão total do ciclo e essencialmente não usando nenhum ABI. Se fosse tudo o que havia, poderíamos jogar o conceito no lixo e chamá-lo um dia, mas não é tudo e nunca foi ... Como sabemos isso? Porque todos os projetos de sucesso não usaram essa abordagem.

Agora, para as possibilidades menos óbvias, aproveite a plataforma que você já tem ... Starcraft em um handheld Linux ARM? Sim, a abordagem funciona quando você não restringe a tarefa exatamente ao que você faria dinamicamente. Ao usar o Winlib, as chamadas da plataforma do Windows são todas nativas, tudo o que precisamos nos preocupar é com a arquitetura.

link

Eu jogaria dólares para donuts que a desaceleração é quase insignificante, considerando que a Pandora portátil ARM é apenas um pouco mais strong que o Pi. As ferramentas que ele usou estão neste repositório.

link

Esse cara decompiled muito manualmente, acredito que o processo poderia ser automatizado significativamente com menos trabalho ... mas ainda um trabalho de amor no momento. Não deixe ninguém lhe dizer que algo não é possível, nem me deixe dizer que não é prático ... Poderia ser prático, assim que você inovar uma nova maneira de fazê-lo.

    
por 06.07.2014 / 10:34
0

Teoricamente, sim, isso pode ser feito. O maior problema que entra em jogo é traduzir um aplicativo para um sistema operacional (ou kernel) para outro. Existem diferenças significativas entre as operações de baixo nível dos kernels Windows, Linux, OSX e iOS, que todos os aplicativos para esses dispositivos precisam usar.

Mais uma vez, teoricamente, poder-se-ia escrever um aplicativo que pudesse decompor um aplicativo, bem como todo o código de máquina associado ao sistema operacional em que ele foi compilado, e recompilá-lo para outro dispositivo. No entanto, isso seria altamente ilegal em quase todos os casos e seria extremamente difícil de escrever. É verdade, as engrenagens na minha cabeça estão começando a se apoderar apenas de pensar nisso.

UPDATE

Alguns comentários abaixo parecem não concordar com a minha resposta, no entanto, acho que eles estão errando o meu ponto. Que eu saiba, não há nenhum aplicativo que possa obter uma seqüência de bytes executáveis para uma arquitetura, decomponha-os no nível de bytecode, incluindo todas as chamadas necessárias para bibliotecas externas incluindo chamadas para o kernel do sistema operacional subjacente e remonte para outro sistema e > salve o bytecode executável resultante . Em outras palavras, não há aplicativos que possam usar algo tão simples como o Notepad.exe, decompor o pequeno arquivo 190k que ele é e 100% remontá-lo em um aplicativo que possa ser executado no Linux ou OSX.

Entendo que a pessoa que fez a pergunta queria saber se podemos virtualizar software ou executar aplicativos por meio de programas como o Wine ou o Parallels, por que não podemos simplesmente re-traduzir o código de bytes para diferentes sistemas. O motivo é que, se você quiser remontar completamente um aplicativo para outra arquitetura, deverá decompor todo o código de bytes necessário para executá-lo antes de montá-lo novamente. Há mais para cada aplicativo do que apenas o arquivo exe, digamos, para uma máquina Windows. Todos os aplicativos do Windows usam os objetos e funções do kernel do Windows de baixo nível para criar menus, áreas de texto, métodos para redimensionar janelas, desenhar na tela, enviar / receber mensagens do sistema operacional e assim por diante, etc.

Todo esse código de bytes deve ser desmontado se você quiser remontá-lo ao aplicativo e executá-lo em uma arquitetura diferente.

Aplicativos como Wine interpretam binários do Windows no nível de byte. Eles reconhecem chamadas para o kernel e traduzem essas chamadas para funções relacionadas ao Linux ou emulam o ambiente do Windows. Mas, isso não é uma retradução byte-by-byte (ou opcode para opcode). É mais uma tradução função-por-função e isso é um pouco diferente.

    
por 26.08.2011 / 23:14
0

Parece que todos os especialistas estão perdendo esse ponto: A "tradução" é complexa, mas muito adequada para o computador (não inteligente, apenas trabalhoso). Mas após a tradução, os programas precisam de suporte ao SO, ex: GetWindowVersion não existe no Linux. Isso normalmente é fornecido pelo emulador (muito grande). Então você poderia 'pré-traduzir' um programa simples, mas você tem que se conectar a uma enorme biblioteca para rodar de forma independente. Imaging programas de cada windows vem com o seu próprio kernel.dll + user.dll + shell.dll ...

    
por 27.08.2011 / 18:09