segunda-feira, 5 de agosto de 2019

Recompiladores

Recompiladores

Software Portável em Vários sistemas
Desde que comecei a estudar informática ainda durante o 2º Grau venho ouvindo falar que o objetivo da informática é que se obtenha um sistema que permita que o programa rode em vários sistemas independente da arquitetura do sistema operacional ou do computador. Mas devido as grandes diferenças estruturais ou e arquiteturais isso tem se revelado muito impossível de ser criado durantes os nãos.
Durante a história foram criados muitos mecanismos para tornar os programas dos usuários portáteis.

Linguagens de programação: A ideia de linguagem de programação foi idealizada por Alan Turing. A ideia era que os programas focem escritos em uma forma de alto nível ou seja um texto e depois esse era traduzido para a linguagem binária do computador. Inicialmente esses sistemas são incrivelmente portáteis. Acontece que depois de compilado para um computador não pode mais ser usado em outro. Mas permite que o mesmo programa tenha versões para vários sistemas diferentes. Acontece que se a cópia que o usuário tiver não é compatível com o seu sistema ele precisara obter uma versão nova para seu computador.
Sistemas Operacionais: Alan Turing também idealizou o sistema operacional. O sistema operacional é uma camada de software que fica entre o Hardware e o Software de aplicativos. Ele funciona como uma camada de software que permite que os programas de usuários passam serem usados mesmo com um grande número de hardware diferente. Nesse caso o programa é escrito para o sistema operacional e esse cuida em tornar o software compatível com o computador. O sistema operacional possui um grande número de ferramentas para tornar os softwares portáteis. Driver de hardware, macro linguagens, interpretadores compiladores e o Kernel que é uma central que distribuir serviços. Isso pode acabar ocupando uma grande área da memória principal do computador. Sistemas operacionais em geral conseguem tornar um ambiente compatível para que o mesmo software possa rodar em milhões de dispositivos de Hardware diferentes. 
Por se tratar de um software tão complexo. Para tornar seu desenvolvimento economicamente viável existem centenas de produtores de sistemas operacionais. Isso gerou um grande número de arquiteturas de sistemas operacionais diferentes. Sendo assim existem sistemas operacionais bastante diferentes um dos outros. Além de existirem sistemas operacionais antigos que por motivos técnicos e de segurança não são mais compatíveis como os sistemas novos.
Isso gera um outro tipo de desafio para os programadores criarem software que sejam compatíveis com o maior número de sistemas operacionais possíveis.

Programa Interpretado: Uma forma de se tornar um programa fácil de ser portável é criar ele na forma interpretada. Nesse formato o arquivo fonte do programa é mantido em formato texto e o programa é lido linha a linha. Isso significa que cada comando do programa é lido por um programa interpretador e ele vai executar um comando por vez. Evidente que essa deve ser a forma de portabilidade maior que existe. Acontece que esse tipo de programa interpretado é muito mais lento que um programa compilado que são os programas traduzidos para linguagem de máquina. Outro problema é que o código do programa fica facilmente ou seja não existe segurança para quem cria o programa que seu algoritmo não seja lido.

Meta Compilação: Nesse método de criar programas o programa não é compilado para a linguagem de máquina mas para uma peceudo linguagem. Essa formato ocorre todas as etapas da compilação do programa mas na etapa final de codificação invés de ser traduzido para linguagem de máquina e para os serviços do sistema operacional o programa é compilado para um processador modelo e para um sistema padrão. O JAVA funciona dessa forma e garante a segurança que os desenvolvedores precisam para seu código não seja copiado. Em alguns casos além da criptografia. No caso do Java ele tem até as funções do sistema operacional podendo criar o sistema mais portátil que existe. Mas no entanto este tipo de portabilidade causa um grande problema de baixa performance. Programas em Meta compilação costumam terem problemas de performance pois precisam serem traduzidos durante a execução.

O Grande problema moderno
O Grande problema moderno é que durante a evolução da computação que foi muito rápida em um período de tempo muito curto deixou um grande número de arquiteturas de computadores e de sistemas operacionais que foram descontinuados. Cada vez que isso ocorre milhares de softwares acabam ficando sem mais suporte. Evidente que uma parte desses foram recompilados para outros sistemas operacionais mais modernos. Mas existe um número grande desses que não se tem mais o código fonte ou a ferramenta em que forram criados não existem mais deixando assim pelo caminho milhares de softwares que não podem serem mais usados. Alguns deles que fazem trabalhos essenciais.
Existem dois casos na informática que são peculiarmente preocupantes.
Quando a Appel lançou os Macintosh passou a ter processador x86 a empresa resolveu que o suporte a software para processadores com Motorola 68000 e PowerPC. Essa mudança foi feita para que o sistema tivesse mais estabilidade. Manter a compatibilidade com sistemas antigos Se faz uso de máquinas virtuais.
Outro caso ainda mais marcante. Foi quando o A Microsoft em 2003 resolveu abandonar o suporte ao MS-DOS e o Windows 3.11. Nesse caso a Microsoft queria eliminar o suporte ao acesso direto ao Hardware que era uma porta a falhas de segurança. Nesse caso milhares de softwares passaram a não serem mais compatíveis com o Windows. Esses sistemas atualmente são suportados por várias máquinas virtuais como DOS-BOX, Virtual PC, Virtual-BOX e VMWare e outros.

Máquina Virtual
Máquina virtual é um software que permite que se usem programas de outros sistemas operacionais e arquiteturas de computadores no computador que você estives usando.
A primeira máquina virtual comercial surgiu na década de 1970 quando a IBM resolveu unificar os vários do sistemas de mainframes em um único sistema operacional. O VM permita que os softwares mainframes criados entre 1952 e 1970 pudessem rodarem em computadores mais novos. E também que sistema de outras plataformas como permitir que no mesmo computador rode o Windows desktops o Windows server, Linux e o Mac OS além de algumas versões do Unix.
A segunda máquina virtual surgiu na década de 1980 era o DosBOX esse fora criado originalmente para ser usado no Amiga e permitir que essa máquinas pudessem rodar programas do PC. Depois que o AmigaOS foi descontinuado o DosBOX foi usado sem muito sucesso por vários anos para sistemas Unix e Linux tivessem capacidade de rodar programas dos. Mas tornou-se realmente importante depois do lançamento da Windows Vista quando a Microsoft resolveu abandonar o suporte Nativo ao DOS.

Uma máquina virtual funciona basicamente da seguinte forma.
1°: A Máquina virtual funciona dentro de uma área de memória especifica para ela. O programa que gerencia a máquina virtual reserva uma área dentro da memória do computador. Os sistema operacional e os softwares rodarão dentro dessa área de memória chamada de partição de memória. Não podendo ser afetada pelo sistema hospedeiro. Na verdade o modo protegido do i286 fora criado com esse propósito. Os processadores modernos possuem o mecanismo de particiona mento da memória os processadores x86-64 possuem o modo virtual 8086.
2°. Alguns computadores podem executarem um sistema operacional por vez. Como ocorria no VM da IBM o sistema mudava para partição o computador passava executar o sistema operacional que estivesse na outra partição e enquanto isso o sistema operacional principal ficava parado esperando que se voltasse a esse sistema. Evidente que os sistemas operacionais modernos possuem uma multitarefa mais moderna e permitem que dois sistemas operacionais rodem ao mesmo tempo.
 3º Existem dois tipos de máquinas virtuais. Em um tipo de modelo usa o hardware do próprio computador. Ou seja ele usa o próprio hardware do computador para que seja executado o programa como eram o caso do DOSShell do Windows 95 e NTVDM que era usado no Windows NT. Mas existem máquinas virtuais que possuem um hardware completo computador esse hardware simula todos os recursos necessários para se ter um computador funciona como um sistema interpretado dentro do sistema operacional ele codifica todas as operações da máquina virtual para o hardware do computador hospedeiro. A primeira forma é mais rápida e segunda evita o conflito com o sistema operacional hospedeiro.
Evidente que existe uma abordagem intermediária a essa. Uma máquina virtual pode acessar diretamente o hardware da máquina real de forma progressiva. Ou seja quando se usa um sistema operacional atualizado que pode ser usado em um grande número de hardware atual compatível. Mas a medida que se vai usando um sistema operacional mais antigo que precisa de hardware mais antigo a máquina virtual vai usando mais dispositivos de hardware simulados. Dessa forma se tem uma máquina virtual tem uma certa quantidade de hardware virtual e um certa quantidade de acesso direto ao hardware próprio da máquina.
4º as máquinas virtuais também podem rodar em modo nativo. Nesse caso o programa roda como se fosse um programa do sistema hospedeiro. Evidente que o programa roda dentro de uma máquina virtual com sua própria memória mas pode acessar os recursos do sistema hospedeiro. Evidente que é a melhor forma de se ter uma máquina de realidade virtual. Acontece que esse tipo de maneira de funcionamento é complexa de mais de ser desenvolvida e pode afetar outros programas do sistema até mesmo o desempenho do próprio sistema operacional.

Recompilação
Minha proposta é de longo prazo. Se continuarmos nesse ritmos logo existiram máquinas virtuais funcionando dentro de máquinas virtuais e ai não poderemos saber como será o desempenho ou a estabilidade do sistema nessa condição.
O que proponho é fazer algo definitivo sobre a portabilidade. 
As pessoas acham que um programa de computador está compilado é definitivo. Ele foi compilado para ser usado em um determinado sistema operacional não há mais o que fazer é definitivo. Terá que ser usado nesse tipo de sistema para sempre.
Mas do meu ponto de vista quando um programa é compilado ele ainda está escrito em uma linguagem de programação. Na verdade seu código foi apenas transformado de uma linguagem de alto nível para uma linguagem de máquina. Na verdade o programa nesse formato atende as características de uma linguagem formal. Possui um número finito de comandos que se conhece bem o que fazem além disso o código na linguagem de máquina segue a regas de formatação bastante rígidas determinadas pelas regras do sistema operacional.
Portanto se o programa compilado está na forma de uma linguagem formal ele pode ser recompilado para ser usado em outro computador e sistema operacional. Simplesmente compilando do forma que ele está que servira de fonte para o mesmo programa uma nova linguagem de máquina para um novo computador e sistema operacional. Direto sem intermediários.
Nesse tipo de sistema o compilador lê o programa compilado em um determinado sistema operacional e computador como se fosse um arquivo fonte e o compila novamente para o computador e sistema operacional do usuário ou ainda para qualquer outra plataforma que se tenha interesse isso é caso a pessoa esteja usando um computador pessoal para criar software para outros sistemas operacionais.
Em um sistema operacional mais avançado esses compiladores de linguagem de máquina para linguagem de máquina poderão estar dentro do sistema operacional e farão a recompilação do programa de forma automática dando ao usuário uma invisibilidade ao processo que está ocorrendo.
Apesar de o ideal seja que o sistema operacional tenha todos os compiladores necessários. Mas isso não será obrigatório já que os próprios recompiladores poderão serem recompilados e adicionados ao sistema operacional.
O grande efeito disso é que não importa para qual computador e sistema operacional o programa tenha sido escrito ele sempre será compatível com o sistema em uso e sempre funcionara de forma compilada ou seja da forma mais eficiente possível. E funcionando de forma nativa.
Você está pensando que codificar a linguagem de máquina de um sistema para outro é difícil? Como eu falei os programas em linguagem de máquina são uma linguagem formal. Acontece que o mecanismo de programação para computadores é muito bem documentado empresas de hardware e desenvolvedoras de software oferecem manuais e guias sobre seus computadores e seu sistema operacional justamente devido o interesse dessas empresas que se desenvolvam software para eles. Até a Appel e sua arquitetura fechada tem uma vasta documentação sobre o assunto. Além de uma vasta quantidade de livros escritos por autores independentes
Por exemplo para criar um compilador para programas em DOS bastaria ter os livros programação Assembly e Bíblia do Programador de Peter Norton e o guias do assembla de vários processadores Intel. Bom isso tenho em casa.

Evidente que originalmente o foco mais interessa é os softwares antigos que não possuem mais suporte como CP/M, MP/P, MS-DOS e o OS/2, MacOS Classic ou Unix para que esses programas possam voltarem a serem usados em sistemas modernos. Mas logo se tem interesses em recompilação de sistemas ainda em uso como Windows, MacOS, AIX, Linux ou consoles de vídeo games como xBox, PS e o NitendoWii. Os usuários poderão comprar programas ou jogos sem se preocupar com o equipamento e usar na máquina que quiser sem precisar se preocupar com a máquina ou sistema que esteja usando.

Acontece que muitos desses sistemas antigos foram tirado de linha devido a falhas de segurança principalmente a presença de vírus. Você agora deve estar pensando que recodificar programas antigos pode ser como abrir a caixa de pandora e trazer de volta esses antigos problemas. Acontece que os recompiladores terão que terem sistemas ante Malware. Esses deverão verificar tudo que pode causar problemas ao sistema não somente vírus e poderem evitar que eles causem problemas em sistemas modernos. Alguns serviços dos programas considerados mas práticas poderão ser retiradas da versão que será compilada. Ou ainda colocados no código destino vários sistemas de segurança.
Acontece que compilar programas antigos para sistemas operacionais modernos deve ser mais fácil que traduzir programas modernos para sistemas mais antigos ou de estrutura mais simples pois vários recursos novos devem serem criados para funcionarem em sistemas antigos ou mais simples.

Etapas de decodificação
Etapas desnecessárias
Evidente que um programa que foi compilado alguma vez não precisa mais passar por algumas etapas como a Analise Léxica e a analise sintática. Pois o programa já passou por isso quando foi compilado na primeira vez. Ou seja o programa já foi escrito corretamente uma vez.

Recompilação direta: 
Nesse tipo de compilação existe um único caminho de compilação ou seja o programa que estiver compilado para um determinado sistema operacional será compilado para um determinado sistema destino.
Esse é o tipo de recompilação mais simples se tem um sistema origem que serve de fonte e tem um sistema destino para qual o programa será compilado. 
Recompilação em série:
A recompilação em série ocorre quando não se tem um recompilador que faça uma compilação direta. Ou sejas não se tem um compilador do sistema origem para o sistema destino. Nesse caso se pode fazer uma recompilação do sistema origem para um sistema intermediário esse programa por sua vez é recompilado para outro sistema e assim por diante até que se cheque a um programa para ser usado pelo sistema que o usuário estiver usando. Assim temos vários programas como uma série de programação até se chegar de um sistema até que o programa se torne compatível com o sistema em uso.
Recompilação Combinada:
Em caso se pode criar em um único compilador que possa compilar programas originários de vários sistemas diferentes para o sistema que o usuário esteja usando.  Na verdade esse tipo de recompilador funciona como se fosse uma linguagem de programação hibrida que tem nela várias linguagens de programação podendo compilar programas em várias linguagens e até mesmo misturar código.
Um recompilação combinada o recompilador não tem as definições de um programa de um sistema mas de vários podendo assim recompilar um programas que foram criados em vários sistemas diferentes para os sistema que o usuário está usando.

Etapa 1: Falha de compatibilidade.
Quando um programa roda em um sistema operacional que não é compatível ele simplesmente dá uma mensagem de erro e o sistema paralisa sua execução.
Mas como estamos falando na possibilidade de recompilar um programa automaticamente podemos reprogramar esse tratamento de erro do Sistema para que ele procure entre os recompiladores um recompilador ou uma sequência de compiladores que leve a traduzir o programa que está sendo chamado para ser executado no sistema em uso.

Etapa 2: Identificação do sistema original.
O sistema deve buscar o sistema em que o programa foi originalmente compilado. Isso é ele deve ler o programa e descobrir o tipo de computador e o sistema operacional a partir do qual o programa foi escrito.
Isso é possível pois para um sistema operacional poder funcionar em um sistema operacional ele tem uma determinadas características. Essas características podem ser identificadas lendo uma parte do programa.
Se isso não for possível o sistema original do computador pode ser revelado tentado fazer recompilações com os compiladores existentes o primeiro que der resultado possível é válido.
Etapa 3: Virtualização de Hardware.
Temos que ver que o sistema operacional é escrito para um tipo de hardware muitos diferentes. Arquiteturas antigas de sistema operacionais tem várias versões diferentes. As versões mais antigas costumavam usarem determinado tipo de hardware que não existe mais. Bem como sistemas operacionais modernos tem características de hardware diferentes como CPU, GPU, Sistema de vídeo e sistema de som, placas de rede diferentes. Como por exemplo o Mac tem um firmware muito maior que o do PC.
Em máquinas virtuais esse hardware é simulado por um software que faz a ligação com o hardware verdadeiro. Por exemplo a Sound Blaster simulada traduz a saída de som para a placa de som do computador real.
Em caso de uma recompilação invés de apenas traduzir para o hardware verdadeiro o recompilador pode criar uma novo código para o hardware. Por exemplo se estamos falando de um programa escrito para usar uma impressora paralela. O recompilador pode criar um código para usar a USB no lugar ou ainda usar o driver de impressão do sistema.

Etapa 4: Acesso direto ao Hardware.
Em muitos sistemas operacionais é permitido que o hardware. No início os computadores tinham pouca capacidade de memória e vários sistemas tinham problemas para desenvolver driver de dispositivos. Para isso era permitido que os sistemas acessassem direto o hardware do computador.
Evidente que por motivos de segurança isso não é mais possível então para tender sistemas como UNIX, BSD, TOS, CP/M e DOS pode se criar um hardware simulado dentro dos sistemas de drivers modernos. Evidente que isso não gera uma saída direta mas produzira que os sistemas antigos possam terem uma saída de dados via sistemas de driver dentro do código recompilado.

Etapa 5: Hardware descontinuado.
Muitos programas foram criados para rodarem em tipos de hardware que não existem mais ou foram descontinuados ou não existem nos computadores atuais. Alguns programas por motivos de evitarem pirataria. Vários programas antigos fazem verificação que estão instalados no disque correto. O mesmo ocorre com computadores que vem sem uma unidade de disco Ótico e programas que vem com teste pare verificar se o disco é original Acontece que o computador ou equipamento pede vir sem uma unidade de disco ótico o programa nesse caso o computador ou dispositivo caso os sistema alvo seja um tablete não possuir esse tipo de dispositivo. O recompilador poderá remover o teste desde que ser respeite os direitos autorais. Evidente que esses softwares poderão depois de recompilados serem usados através de outros dispositivos como como o HD ou SSD de um computador ou a parte flash da memória de um tablete
Etapa 6: Processador
O processador é a parte mais importante em um programa. Em geral um programa compilado é escrito para ter o código de máquina escrito para esse processador.
Então é uma das partes mais importantes da recompilação já que isso pode definir o funcionamento coreto e mais eficiente do programa.
Em processadores da mesma família em geral código de um processador mais antigo funciona na versão mais moderna. Evidente que se recompilador for para fazer o contrário. Passar de um processador mais moderno para um mais antigo isso precisara ter algumas funções reescritas.
Acontece que apesar de ser uma parte crítica do processo é aparente fácil. A programação em baixo nível é feito com Macro compiladores. Nesse caso cada comando de um processador origem passa a ser considerado uma macro para um conjunto de comandos no processador do sistema destino.
Um dos exemplos seria a codificação de um programa de Motorola 68040 para um processador Ultra Sparc T2. Alguns instruções do processador bastariam serem trocadas pela instruções equivalentes ou seja uma instrução por uma instrução. Mas a maior parte delas deve ter uma macro com várias instruções para poder ter um comando equivalente.
Evidentemente que essa macros devem respeitarem as boas práticas de programação para que o programa destino fique o mais eficiente e rápido que for possível depois de codificado.

Velocidade do programa: Uma das questões do processador que alguns programas antigos podem ficarem muito rápidos para rodarem em processadores ficam rápidos demais para rodarem em processadores mais rápidos. Os processadores tem capacidade de reduzirem com sua velocidade quando for necessário quando isso não adiantar ou não vaze efeito se pode-se colocar até um timer entre as instruções do processador para fazer o programa ficar mais lento e poder ser visualizado de forma correta.

Etapa 7: Modo de memória.
Acontece que um mesmo tipo de processador possui vários tipos de memória a famílias 68000 tem o 16 bits real usado no Motorola 6800 de 8 bits real 24 bits usado no Motorola 68000 e o real de 32 bits usado a partir do 68020 e o PowerPC 960 incluiu a memória de 64 bits. 
O Intel x86 possui vários tipos de memória 12 bits e 16 bits dos processadores de 8 bits depois o 20 bits de 8086 e o protegido 24 bits i286 e depois o paginado de 32 bits e 64 bits e o modo 36 bits do Pentium P5 e o modo de memória real de 32 bits e 64 bits.
Quando se escolhe um modo de memória deve se levar em conta o caso do tipo de memória que o sistema operacional é esse o tipo de memória que o programa deve ser usado depois que o programa for recompilado. 
O sistema de recompilação deve ser capas de converter a memória do programa destino para a melhor tipo usada pelo sistema destino. Ou seja os endereços do sistema origem devem ser convertidos no formato do sistema destino.

Etapa 8: Analise de estruturas
Nos sistemas mais simples as estruturas do programa. Essas estruturas de dados são.
Área de dados: Onde ficam as variáveis do sistema ou área de memória onde ficam os dados.
Pilha: a pilha salva o valor de registradores do sistema para que possam ser restaurados depois da chamada de uma sub-rotina ou de uma interrupção.
Área de extensão: é uma área de memória onde ficam dados temporários do sistema como variáveis temporárias ponteiros ou usado para acrescentar um segundo segmento de programa.
Área de código: Onde fica o código do programa em si. Ou seja os comandos que dizem o que o programa faz.

Quem já conhece programação assembla do x86 vê que esse tipo de processador essas áreas ficam muito bem definidas no sistema ou seja tem registradores que indicam onde cada uma fica. Mas isso não é regra para todos os processadores muitos deles essas áreas de memória não são exatamente bem definidas ou ainda todos os componentes de um programa ficam em uma única área de memória continua.
Ema alguns processadores as variáveis são definidas dentro do código do programa usando um comando Goto para evitar que essa área seja lida como comandos do programa ou possui um comando em linguagem de máquina como o VAR que reserva aquela parte do programa para não ser executado mas apenas conter dados.
A análise de estrutura deve identificar essas estruturas de dados do programa e marcar elas para o recompilador possa criar essas estruturas para criar elas no código do sistema destino.

Outra coisa era que a maioria dos sistema operacionais trabalham com endereços do tipo indefinido. Ou seja o programa inicialmente não tem o endereço dessas áreas de dados definidas apenas o tamanho e é o sistema operacional que aloca o endereço real dessas áreas quando o programa é carregado na memória. 
Mas existem alguns programas que trabalham com endereços de dados fixos. Ou seja o endereços da memória são definidos na hora que o programa é compilado. Então programas que tem endereço fixo devem ser mudados para programas com endereços relativo quando for necessário e vise versa. Converter endereços relativos em endereços fixos quando necessários.

Mesmo assim existem dentro dos programas alguns casos que eles acessam um endereço fixo na memória mesmo quando o programa tem endereços relativos. É o caso do sistema fazer acesso a um buffer do sistema operacional ou uma variável do sistemas. O programa de recompilação deve ter que verificar esses endereços fixos para endereços similares no sistema destino. Em alguns casos como se um programa DOS que acesse o buffer de vídeo deve ser trocado por uma função de vídeo que seja semelhante.

Etapa 9: Sistema operacional.
O programa destino tem que ser recodificado. Um sistema operacional tem características próprias e o programa que roda nele tem um formato especifico. Os programas para serem entendidos tem um forma definido pelo sistema operacional de como declarar as áreas de dados programas pilha e extensão.
O recompilador trabalha como ser fosse um compilador então ela deve traduzir essas partes para o formato do sistema objetivo.

Etapa 10: Cabeçalho do programa.
Em muitos casos o programa vem um único bloco com dados e código em um único segmento de memória. Mas alguns programas vem divididos em área de dados, setor de pilha e área do programa programas x86 tem uma área de extensão para dados temporários. Essas áreas são definidas pelo cabeçalho do programa. O cabeçalho pode vária de acordo com o processador sistema operacional ou tipo de programa. Comandos do sistema geralmente são mais simples. Mas os programas podem terem um cabeçalho bastante complexo. Além das áreas do sistema ele pode ter outros componentes como nome do usuário que criou o sistema data de criação ou os parâmetros de acesso.
Em alguns casos o recompilador poderá tirara esse tipo de informação ou redefinir elas para algum equivalente no sistema operacional destino ou simplesmente ignorar as informações inúteis. Alguns sistemas operacionais tem capacidade de gravarem informações como comentários dentro de um código compilado e esse pode ser o destino desse tipo de informação que será desnecessárias.
Em alguns casos na hora de recompilar o programa de um sistema com um cabeçalho mais simples para um cabeçalho mais complexo algumas informações deverão ser criadas se podem usarem informações adequadas para endereços devem serem criados e no caso terem valores default e em caso de informações auxiliares se podem criar dados como por exemple no usuário criador do sistema o nome do recompilador.
Temos que lembrar que as informações sobre o arquivo são gravadas em dois lugares. O Windows no caso grava todas essas informações dentro do diretório do disco. Mas existem vários sistemas até o antigo sistema de arquivos do Unix da década de 1970 que gravava essa informações dentro do próprio arquivo.

Etapa 11: Serviços do Computador.
Os serviços do sistema são distribuídos em duas forma. 
Os mais básicos estão distribuídos no Firmware do computador. Os dois firmwares mais usados são o BIOS do PC e o Firmware do Macintosh. Evidente que existem outros firmwares em outras arquiteturas de computadores como PS/2, Amiga, Estações Sum/Oracle, Estações IBM etc. Além disso vários dispositivos como Tabletes Celulares e dispositivos digitais tem seu próprio firmware.
Com um grande número de firmware diferentes essas funções quando chamadas pelo programa devem ser trocadas por uma equivalente no sistema destino. Essas funções que são consideradas de baixo nível devem ser identificadas e quando não houver um equivalente poderá ser recriada.

Essas funções básica, evidente, em sistemas modernos são acessadas pelos sistemas operacionais não pelos programas de usuários.  Devido a questões de segurança. Alguns sistemas até não permitem que elas sejam acessa das diretamente pelos programas como o Caso do OS/2 ou o Windows NT o MacOS X.
Evidentemente que existem um certo número de programas que fazem acesso e essas funções ou a algumas dessas funções.
Devido as práticas modernas de programação se recomenda que quando possível se troque essas funções de firmware por funções do sistema operacional, que essas são consideradas mais seguras. Na falta de equivalência podem serem criadas via software.
Nesse ponto temos que ver que algumas dessas funções não são mais permitidas pelo acesso direto como algumas funções de disco que permitem acesso direto aos setores do disco ou acesso a serviços da placa de rede. Devido a propagação de vírus ou serem consideradas.
Algumas dessa funções poderão serem virtualizadas usando os drivers do sistema operacional. Mas isso significa também que alguns programas não poderão serem recompilados. E uma mensagem de aviso deve ser emitida.

Etapa 12: Serviços do Sistema operacional API.
A forma como o sistema operacional se comunica com os programas de aplicação é através de um conjunto de funções variáveis e macros chamada de API.
A API de um sistema operacional podem terem milhares de funções.
Existem três formas dessa API funcionar que foram aparecendo durante a evolução dos sistema operacional.
Comandos: Os comandos foram a primeira forma de se comunicação que surgiu para sistemas operacionais um comando é uma instrução dada ao sistema operacional para realizar alguma coisa. Os comandos originalmente ficavam em programas separados no sistema operacional ou residentes na memória e podia serem usados tanto por programas como por usuários. Alguns comando podem retornar valores através de variáveis do sistema ou diretamente via propt de comando. Evidente que um programa em uma API de comandos deveria ser capaz de chamar uma rotina externa e poder receber os dados de retorno. Geralmente o retorno era feito por uma variável do sistema de uso geral.
Essa abordagem foi iniciada na década de 1960 quando os computadores mesmo que enormes tinham um pequena memória principal. Foi usado em sistemas mainframes e depois sistemas de 8 bits como o CP/M e o Atary TOS, Appel DOS etc. Evidente que depois disso surgiram computadores com maior capacidade de memória logo começou a aparecerem sistemas com API residente na memória como o UNIX e o MS-DOS. Evidente que os sistemas operacionais modernos são descendentes dos sistemas antigos então eles ainda tem comandos se são usados pelo sistema. Se quiser testar pegue o Windows 10 e comesse digitando comandos do DOS e descobrirá que eles ainda funcionam.

API de Interrupções (residentes na memória): Uma parte da API do sistema operacional fica armazenada na memória principal. Ou seja essas funções ficam em um programa que fica residente na memória do computador e podem ser chamadas pelo programa da aplicação por meio de interrupções do sistema operacional. Isso é chamado de Kernel Monolítico. No caso do Unix ou do DOS essas funções residentes são carregadas no momento que o sistema é iniciado no computador. E a alocado em uma área da memória RAM chamada área de acesso restrito. Esse programa á chamado internamente dentro do programa através de uma interrupção do sistema ou seja um comando que chame a interrupção que aciona assim a API. Em geral é preciso carregar os parâmetros nos registradores ou em um variável do sistema e depois se chama a interrupção. Essa aciona o programa que executará as funções do sistema necessárias e depois retornara a resposta. Por estarem na memória tem uma resposta bastante rápida. Mas por causa de usarem a memória apenas as funções mais importantes devem estarem ali.

API de bibliotecas: Uma biblioteca é uma coleção de funções que ficam em um arquivo. No momento que o programa é feito o programador adiciona essa biblioteca ao seu código fonte. Quando o programa é compilado a função é adicionada ao programa. É uma solução em vários sistemas mas devido ao fato de cada programa ter uma cópia da função dentro de si ele tem várias copias de cada função em programas diferentes gerando bastante código repetitivos. Essa abordagem foi usada para criar o Unix mais avançado.

API de bibliotecas dinâmicas. As bibliotecas dinâmicas surgiram mais recentemente e são o modelo de API mais modernos. As bibliotecas dinâmicas são como bibliotecas de funções que são anexadas aos programas na hora que estão sendo programados. Mas o código da biblioteca dinâmica não é colocado dentro do programa quando compilado. Mas permanece dentro do seu próprio Arquivo. Quando o programa é usado ele carrega o arquivo da biblioteca somente no momento que a função é necessária alguns sistemas podem fazerem o carregamento prévio de um DLL ou deixar ela na memória depois do uso para garantir uma velocidade de execução melhor. É o tipo de API mais eficiente em termos de performance e ocupação de memória sendo um bom meio termo em relação a API residente ou de biblioteca estática e a API de comandos. Com quase nenhuma deficiência.

Sobre a recompilação de um sistema operacional para outro na parte de API pode ser menos difícil que se imagina. As APIs de um sistema operacional para outro são bem diferentes tem formatos de acesso diferentes de um sistema para outro. Mas os sistemas operacionais são bastante similares entre si ou seja tem funções bem parecidas ou fazem coisas bem parecidas. Então encontrar uma função ou comando similar em outro sistema operacional deve ser bastante fácil. Atém mais fácil que no caso de processadores. E que não for encontrado pode ser criado funções que façam essa tarefa.


Recompilação
Se seguimos todas as etapas da programação será possível recodificar todos os programas existentes para qualquer tipo de computador e sistema operacional. Podendo esse programa funcionar de forma nativa e sem usos de máquina virtual.

O Recompilador funciona como uma macro compilador ou seja para cada um dos comandos do programa origem para o programa destino.
Cada parte do programa origem tem um comando correspondente para ser colocado no programa destino.

Essas etapas não formam um algoritmo em si mas uma relação de tarefas que o recompilador deve fazer antes de realizar a codificação. 
Espero que sejam suficientes para criar o software proposto. Ficam livre de proporem novas etapas para um software que funcione melhor. E principalmente tenha o melhor resultado final.