O executável de um programa pequeno e extremamente simples, como o mostrado abaixo, compilado em um tipo de Linux, será executado em um tipo diferente? Ou precisaria ser recompilado?
A arquitetura da máquina importa em um caso como este?
int main()
{
return (99);
}
Resumindo: se você estiver levando um binário compilado de um host para outro usando a mesma arquitetura (ou uma compatível) , você pode perfeitamente levá-lo para outra distribuição . No entanto, à medida que a complexidade do código aumenta, a probabilidade de ser vinculado a uma biblioteca que não está instalada; instalado em outro local; ou instalado em uma versão diferente, aumenta. Tomando por exemplo o seu código, para o qual
ldd
relata as seguintes dependências quando compiladogcc -o exit-test exit-test.c
em um host Ubuntu Linux (derivado do Debian):Obviamente, este binário não será executado se eu o transferir para, digamos, um Mac (
./exit-test: cannot execute binary file: Exec format error
). Vamos tentar movê-lo para uma caixa RHEL:Oh céus. Por que isso pode ser?
Mesmo para este caso de uso, o empilhamento falhou devido à falta de bibliotecas compartilhadas.
No entanto, se eu compilá-lo com
gcc -static exit-test-static exit-test.c
, portá-lo para o sistema sem as bibliotecas funcionará bem. À custa, é claro, do espaço em disco:Outra solução viável seria instalar as bibliotecas necessárias no novo host.
Tal como acontece com muitas coisas no universo U&L, este é um gato com muitas peles, duas das quais são descritas acima.
Depende. Algo compilado para IA-32 (Intel 32 bits) pode ser executado em amd64, pois o Linux em Intel mantém a compatibilidade com aplicativos de 32 bits (com software adequado instalado). Aqui está o
code
compilado no sistema RedHat 7.3 de 32 bits (por volta de 2002, gcc versão 2.96) e, em seguida, o binário copiado e executado em um sistema Centos 7.4 de 64 bits (por volta de 2017):O antigo RedHat 7.3 para Centos 7.4 (essencialmente RedHat Enterprise Linux 7.4) permanece na mesma família de "distribuição", então provavelmente terá melhor portabilidade do que ir de uma instalação aleatória "Linux do zero" de 2002 para alguma outra distribuição Linux aleatória em 2018 .
Algo compilado para amd64 não funcionaria apenas em versões de 32 bits do Linux (o hardware antigo não conhece o novo hardware). Isso também é verdade para novos softwares compilados em sistemas modernos destinados a serem executados em coisas antigas, pois bibliotecas e até mesmo chamadas de sistema podem não ser portáteis para trás, portanto, podem exigir truques de compilação ou obter um compilador antigo e assim por diante, ou possivelmente em vez disso compilando no sistema antigo. (Esta é uma boa razão para manter as máquinas virtuais de coisas antigas por perto.)
A arquitetura importa; amd64 (ou IA-32) é muito diferente de ARM ou MIPS, portanto, não se espera que o binário de um deles seja executado em outro. No nível de montagem, a
main
seção do seu código em IA-32 compila viagcc -S code.c
paracom o qual um sistema amd64 pode lidar (em um sistema Linux - o OpenBSD, ao contrário do amd64 , não suporta binários de 32 bits; a compatibilidade com versões anteriores com archs antigos oferece espaço de manobra aos invasores, por exemplo, CVE-2014-8866 e amigos). Enquanto isso, em um sistema MIPS big-endian,
main
ele compila para:com o qual um processador Intel não terá ideia do que fazer e, da mesma forma, para o conjunto Intel no MIPS.
Você poderia usar QEMU ou algum outro emulador para executar código estrangeiro (talvez muito, muito lentamente).
No entanto! Seu código é um código muito simples, então terá menos problemas de portabilidade do que qualquer outra coisa; os programas normalmente fazem uso de bibliotecas que foram alteradas ao longo do tempo (glibc, openssl, ...); para aqueles, também pode ser necessário instalar versões mais antigas de várias bibliotecas (RedHat, por exemplo, normalmente coloca "compat" em algum lugar no nome do pacote para tal)
ou possivelmente se preocupe com alterações de ABI (Application Binary Interface) para coisas muito antigas que usam glibc ou alterações mais recentes devido a C++ 11 ou outras versões de C++. Pode-se também compilar estático (aumentando muito o tamanho do binário no disco) para tentar evitar problemas de biblioteca, embora algum binário antigo faça isso depende se a distribuição Linux antiga estava compilando quase tudo dinâmico (RedHat: sim) ou não. Por outro lado, coisas como
patchelf
podem reajustar binários dinâmicos (ELF, mas provavelmente nãoa.out
formatar) para usar outras bibliotecas.No entanto! Ser capaz de executar um programa é uma coisa e realmente fazer algo útil com ele é outra. Antigos binários Intel de 32 bits podem ter problemas de segurança se dependerem de uma versão do OpenSSL que tenha algum problema de segurança horrível e sem suporte, ou o programa pode não ser capaz de negociar com servidores web modernos (como o moderno os servidores rejeitam os antigos protocolos e cifras do antigo programa), ou a versão 1 do protocolo SSH não é mais suportada, ou ...
Somando-se às excelentes respostas de @thrig e @DopeGhoti: sistemas operacionais Unix ou semelhantes a Unix, incluindo Linux, sempre foram tradicionalmente projetados e alinhados mais para a portabilidade do código-fonte do que binários.
Se não tiver nada específico de hardware ou ser uma fonte simples como no seu exemplo, você pode movê-lo sem nenhum problema entre praticamente qualquer versão do Linux ou arquitetura como código-fonte , desde que os servidores de destino tenham os pacotes de desenvolvimento C instalados , as bibliotecas necessárias e as bibliotecas de desenvolvimento correspondentes instaladas.
No que diz respeito à portabilidade de código mais avançado de versões mais antigas do Linux distantes no tempo, ou programas mais específicos, como módulos do kernel para diferentes versões do kernel, você pode ter que adaptar e modificar o código-fonte para levar em consideração bibliotecas/APIs/ABIs obsoletos.
Por padrão , você quase certamente terá problemas com bibliotecas externas. Algumas das outras respostas dão mais detalhes sobre esses problemas, então não vou duplicar o trabalho deles.
Você pode , no entanto, compilar muitos programas - mesmo os não triviais - para serem portáveis entre sistemas Linux. A chave é um kit de ferramentas chamado Linux Standard Base . O LSB foi projetado para criar exatamente esses tipos de aplicativos portáteis. Compile um aplicativo para LSB v5.0 e ele será executado em qualquer outro ambiente Linux (da mesma arquitetura) que implemente LSB v5.0. Algumas distribuições Linux são compatíveis com LSB e outras incluem kits de ferramentas/bibliotecas LSB como um pacote instalável. Se você criar seu aplicativo usando as ferramentas LSB (como o
lsbcc
wrapper paragcc
) e vincular à versão LSB das bibliotecas, criará um aplicativo portátil.Pode ser.
Coisas que tendem a quebrá-lo incluem.
Se você evitar o uso de bibliotecas que mudam rapidamente, evitar mudanças de arquitetura e construir na distro mais antiga que deseja atingir, terá uma boa chance de fazer um binário funcionar em muitas distros.
Além de algumas das coisas mencionadas anteriormente, houve algumas alterações no formato do arquivo executável. Na maioria das vezes, o Linux usa ELF, mas as versões mais antigas usavam a.out ou COFF.
O início de um wikihole:
https://en.wikipedia.org/wiki/Comparison_of_executable_file_formats
Pode haver uma maneira de obter versões mais antigas para executar formatos mais novos, mas eu pessoalmente nunca examinei isso.