Um executável do Linux compilado em um "sabor" do Linux será executado em outro?

59

O executável de um programa pequeno e extremamente simples, como o mostrado abaixo, compilado em um sabor do Linux, será executado em um sabor diferente? Ou precisaria ser recompilado?

A arquitetura da máquina é importante em um caso como esse?

int main()
{
  return (99);
}
JCDeen
fonte
2
Obrigado a todos pelas excelentes respostas! Eu aprendi muito mais do que previ. Tornei o código artificialmente simples de propósito, para que dependesse do menor número possível de bibliotecas; mas eu realmente deveria ter declarado isso na frente. A maior parte do meu código C ++ em plataformas envolveu o desenvolvimento de uma ferramenta da Microsoft, como o Visual Studio, e a transferência do código para um sistema * nix e a recompilação.
JCDeen
4
As muitas facetas e considerações expressas aqui me surpreenderam! Sinceramente, desejo poder escolher vários como A resposta. Mais uma vez obrigado a todos! Atenciosamente.
JCDeen
2
O Android também é um sistema operacional baseado em Linux. Boa sorte, no entanto, executar qualquer código compilado glibclá ou vice-versa. Concedido, não é totalmente impossível .
undercat 31/01
2
Para obter compatibilidade máxima de uma ferramenta de linha de comando, convém usar uClibc, musl ou dietlibc em vez de glibc e vincular estaticamente seu executável de 32 bits ( gcc -m32 -static). Ao fazer isso, qualquer Linux i386 ou amd64 poderá executar o executável.
pts
10
Você deveria retornar 42 ! :)
Homunculus Reticulli

Respostas:

49

Depende. Algo compilado para IA-32 (Intel de 32 bits) pode ser executado no amd64, pois o Linux na Intel mantém a compatibilidade retroativa com aplicativos de 32 bits (com o software adequado instalado). Aqui está o seu codecompilado no sistema RedHat 7.3 de 32 bits (por volta de 2002, gcc versão 2.96) e o binário copiado para e executado em um sistema Centos 7.4 de 64 bits (por volta de 2017):

-bash-4.2$ file code
code: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.2.5, not stripped
-bash-4.2$ ./code
-bash: ./code: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
-bash-4.2$ sudo yum -y install glibc.i686
...
-bash-4.2$ ./code ; echo $?
99

O RedHat 7.3 antigo para o Centos 7.4 (essencialmente RedHat Enterprise Linux 7.4) permanece na mesma família "distribuição", portanto, provavelmente terá portabilidade melhor do que passar de uma instalação aleatória do "Linux a partir do zero" de 2002 para outra distribuição aleatória do Linux em 2018 .

Algo compilado para amd64 não seria executado apenas em versões de 32 bits do Linux (o hardware antigo não sabe sobre o novo hardware). Isso também é válido para novos softwares compilados em sistemas modernos destinados a serem executados em coisas antigas, já que bibliotecas e até chamadas de sistema podem não ser portáveis ​​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. (Esse é um bom motivo para manter as máquinas virtuais antigas e antigas).

Arquitetura importa; O amd64 (ou IA-32) é muito diferente do ARM ou MIPS, portanto, não se espera que o binário de um deles seja executado em outro. No nível do assembly, a mainseção do seu código no IA-32 compila via gcc -S code.cpara

main:
    pushl %ebp
    movl %esp,%ebp
    movl $99,%eax
    popl %ebp
    ret

com o qual um sistema amd64 pode lidar (em um sistema Linux - o OpenBSD, por outro lado, no amd64 não suporta binários de 32 bits; a compatibilidade retroativa com arcos antigos oferece aos atacantes espaço de manobra, por exemplo, CVE-2014-8866 e amigos). Enquanto isso, em um sistema MIPS big endian, main compila para:

main:
        .frame  $fp,8,$31
        .mask   0x40000000,-4
        .fmask  0x00000000,0
        .set    noreorder
        .set    nomacro
        addiu   $sp,$sp,-8
        sw      $fp,4($sp)
        move    $fp,$sp
        li      $2,99
        move    $sp,$fp
        lw      $fp,4($sp)
        addiu   $sp,$sp,8
        j       $31
        nop

com o qual um processador Intel não tem idéia do que fazer e da mesma forma para o conjunto Intel no MIPS.

Você poderia usar o QEMU ou algum outro emulador para executar código estrangeiro (talvez muito, muito lentamente).

Contudo! Seu código é muito simples, portanto, haverá menos problemas de portabilidade do que qualquer outra coisa; programas normalmente fazem uso de bibliotecas que foram alteradas ao longo do tempo (glibc, openssl, ...); para aqueles que também precisam instalar versões mais antigas de várias bibliotecas (o RedHat, por exemplo, geralmente coloca "compat" em algum lugar do nome do pacote)

compat-glibc.x86_64                     1:2.12-4.el7.centos

ou possivelmente se preocupe com as alterações ABI (Application Binary Interface) para coisas antigas que usam glibc ou alterações mais recentes devido ao C ++ 11 ou a outras versões do C ++. Também se pode compilar estático (aumentando muito o tamanho do binário no disco) para tentar evitar problemas de biblioteca, embora se algum binário antigo fizesse isso dependesse se a distribuição antiga do Linux estava compilando quase tudo dinâmico (RedHat: yes) ou não. Por outro lado, coisas como patchelfpodem reativar a.outbinários dinâmicos (ELF, mas provavelmente não formatados) para usar outras bibliotecas.

Contudo! Ser capaz de executar um programa é uma coisa e, na verdade, fazer algo útil com outra. Os binários antigos da Intel de 32 bits podem ter problemas de segurança se dependerem de uma versão do OpenSSL que contenha algum problema de segurança horrível e não suportado, ou o programa talvez não consiga negociar com os servidores da Web modernos (como os modernos servidores rejeitam os antigos protocolos e cifras do programa antigo), ou o protocolo SSH versão 1 não é mais suportado ou ...

agitar
fonte
14
primeiro parágrafo: não, a Intel o chama de "Intel 64" (hoje em dia, depois de passar por outros nomes anteriormente). IA-64 refere-se ao Itanium, não é compatível com x86.
Hbbs
11
@obbs obrigado, substituí essas referências por amd64; Vou deixar o nome das coisas para o departamento de marketing da Intel.
thrig
3
por que não mencionar a vinculação estática?
precisa saber é o seguinte
2
Não são apenas as ABIs da biblioteca que mudam - a interface syscall do kernel também se estende ao longo do tempo. Observe o for GNU/Linux 2.6.32(ou tal) na saída de file /usr/bin/ls.
Charles Duffy
11
@ Wilbert Você provavelmente está perdendo esse problema referindo-se ao Red Hat Linux que é distinto do Red Hat Enterprise Linux.
Bob
67

Resumindo: se você estiver levando um binário compilado de um host para outro usando a mesma arquitetura (ou uma compatível) , pode ser perfeitamente bom 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 como exemplo o seu código, para o qual lddrelata as seguintes dependências quando compiladas gcc -o exit-test exit-test.cem um host Ubuntu Linux (derivado do Debian):

$ ldd exit-test
    linux-gate.so.1 =>  (0xb7748000)
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb757b000)
    /lib/ld-linux.so.2 (0x8005a000)

Obviamente, esse binário não funcionará se eu chutá-lo para, digamos, um Mac ( ./exit-test: cannot execute binary file: Exec format error). Vamos tentar movê-lo para uma caixa RHEL:

$ ./exit-test
-bash: ./exit-test: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory

Oh céus. Por que isso pode ser?

$ ls /lib/ld-l* # reference the `ldd` output above
ls: cannot access /lib/ld-l*: No such file or directory

Mesmo para esse caso de uso, a empilhadeira falhou devido à falta de bibliotecas compartilhadas.

No entanto, se eu compilar gcc -static exit-test-static exit-test.c, transportá-lo para o sistema sem as bibliotecas funciona bem. À custa, é claro, de espaço em disco:

$ ls -l ./exit-test{,-static}
-rwxr-xr-x  1 username  groupname    7312 Jan 29 14:18 ./exit-test
-rwxr-xr-x  1 username  groupname  728228 Jan 29 14:27 ./exit-test-static

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 estão descritas acima.

DopeGhoti
fonte
4
Na verdade, eu esqueci binários estáticos. Alguns fornecedores adotar binários estáticos, e alguns autores de malware também para maximizar a compatibilidade binária entre versões de Linux da mesma arquitetura
Rui F Ribeiro
8
Empilhadeira ...?
usar o seguinte comando
2
@immibis Acho que isso significa copiar dados (o executável) de um ambiente (distro) para outro, onde os dados não são projetados para o ambiente de destino.
Wjandrea
13
Seu exemplo do Linux é artificial, infelizmente, e ilustra seu ponto de vista sobre arquiteturas e não sobre distribuições: você construiu um binário de 32 bits no Debian e tentou executá-lo no RHEL de 64 bits; essas são arquiteturas diferentes ... Os binários da mesma arquitetura com tão poucas dependências de biblioteca podem ser copiados perfeitamente.
Stephen Kitt
7
@ MSalters Não estou dizendo que não é razoável, estou dizendo que é um mau exemplo, dado o argumento que o DopeGhoti está tentando fazer (você não pode copiar binários de uma distribuição para outra - o que está errado). É claro que o Linux de 64 bits na Intel também suporta a execução de executáveis ​​de 32 bits, com a infraestrutura apropriada. Um exemplo válido, neste caso, o IMO seria criar um amd64binário e executá-lo em outra amd64distribuição, ou construir um i386binário e executá-lo em outra i386distribuição.
Stephen Kitt
25

Além das excelentes respostas @thrig e @DopeGhoti: os sistemas operacionais Unix ou do tipo Unix, incluindo Linux, eram tradicionalmente sempre projetados e alinhados mais para a portabilidade do código-fonte do que os binários.

Se não houver nada específico de hardware ou uma fonte simples, como no 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 à transferência de códigos mais avançados de versões antigas do Linux distantes no tempo ou de programas mais específicos, como módulos de kernel para diferentes versões de kernel, pode ser necessário adaptar e modificar o código-fonte para contabilizar as bibliotecas / APIs / ABIs obsoletas.

Rui F Ribeiro
fonte
19

Por padrão , você quase certamente terá problemas com bibliotecas externas. Algumas das outras respostas entram em mais detalhes sobre esses problemas, então não vou duplicar o trabalho deles.

No entanto, você pode compilar muitos programas - mesmo os não triviais - para serem portáveis ​​entre os sistemas Linux. A chave é o kit de ferramentas chamado Linux Standard Base . O LSB foi projetado para criar apenas 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 o 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 lsbccwrapper para gcc) e vincular à versão LSB das bibliotecas, criará um aplicativo portátil.

bta
fonte
e com qemuvocê mesmo pode executar programas compilados para archtecture diferente, (não alta performace, mas você pode executá-los)
Jasen
11
Eu nem sabia o kit de ferramentas Linux Standard Base, então obrigado! Comecei a trabalhar com C / C ++ há muito tempo, muitas informações nessas respostas são novas para mim. E muito útil.
precisa saber é o seguinte
11
O artigo da Wikipedia diz Debian e Ubuntu não implementar LSB (e não tenho nenhuma intenção de fazê-lo.)
BlackJack
2
@ BlackJack- A distribuição em si não implementa 100% dela como parte do sistema operacional principal, mas você pode instalar bibliotecas e kits de ferramentas compatíveis com LSB como pacotes opcionais. Eu usei o Ubuntu (por exemplo) para criar programas compatíveis com LSB que rodavam no Suse e no Centos, você só precisa de apt-get installalguns pacotes.
BTA
10

Talvez.

Coisas que tendem a quebrá-lo incluem.

  1. Arquiteturas diferentes. Obviamente, arquiteturas totalmente diferentes não funcionarão (a menos que você tenha algo como o qemu no modo de usuário com binfmt_misc, mas essa dificilmente é uma configuração normal). Os binários x86 podem funcionar no amd64, mas apenas se as bibliotecas de 32 bits necessárias estiverem disponíveis.
  2. Versões da biblioteca. Se a soversion estiver errada, ela não encontrará a biblioteca. Se a soversion for a mesma, mas o binário for construído em uma versão mais recente da biblioteca em relação à qual está sendo executado, poderá falhar ao carregar devido a novos símbolos ou novas versões de símbolos. Em particular, o glibc é um usuário pesado do controle de versão de símbolos; portanto, os binários criados com base em um glibc mais novo provavelmente irão falhar com um glibc mais antigo.

Se você evitar o uso de bibliotecas que mudam rapidamente, evite alterações na arquitetura e, com base na distribuição mais antiga que você deseja atingir, terá uma boa chance de fazer um binário funcionar em muitas distribuições.

plugwash
fonte
4

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 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 rodar formatos mais novos, mas eu pessoalmente nunca examinei isso.

Ben
fonte
"mais velho" é neste momento muito antigo agora.
Plugwash