Um executável Linux compilado em um “sabor” do Linux é executado em um diferente?

55

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

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

int main()
{
  return (99);
}
    
por JCDeen 29.01.2018 / 21:47

6 respostas

45

Depende. Algo compilado para IA-32 (Intel 32-bit) pode rodar em AMD64, pois o Linux na Intel mantém a compatibilidade retroativa com aplicativos de 32 bits (com software adequado instalado). Aqui está o seu code compilado no sistema de 32 bits RedHat 7.3 (por volta de 2002, gcc versão 2.96) e então o binário copiado 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 antigo RedHat 7.3 ao 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 passar de uma instalação aleatória do "Linux do zero" de 2002 para algum outro Linux aleatório distribuição em 2018.

Algo compilado para o amd64 não seria executado em versões de 32 bits do Linux (o hardware antigo não sabe sobre o novo hardware). Isso também é verdade para o novo software compilado em sistemas modernos que devem ser executados em coisas antigas antigas, como bibliotecas e Mesmo as chamadas do 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 compilar no sistema antigo. (Esta é uma boa razão para manter as máquinas virtuais das antigas e antigas coisas por aí.)

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

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, ao contrário, em amd64 não suporta binários de 32 bits; a compatibilidade retroativa com antigos arcos dá espaço a invasores, por exemplo < CVE-2014-8866 e amigos). Enquanto isso, em um sistema MIPS big-endian main , em vez disso, 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

que um processador Intel não tem idéia do que fazer, e da mesma forma para a montagem Intel no MIPS.

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

No entanto! Seu código é muito simples, então terá menos problemas de portabilidade do que qualquer outra coisa; os programas tipicamente fazem uso de bibliotecas que mudaram com o tempo (glibc, openssl, ...); para aqueles que também podem precisar instalar versões mais antigas de várias bibliotecas (o RedHat, por exemplo, tipicamente coloca "compat" em algum lugar no nome do pacote para tal)

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

ou possivelmente se preocupar com alterações ABI (Application Binary Interface) para coisas antigas que usam glibc, ou alterações mais recentes devido a C ++ 11 ou outros releases C ++. Também é possível compilar a estática (aumentando bastante o tamanho binário no disco) para tentar evitar problemas de biblioteca, embora se algum antigo binário dependesse de a distribuição antiga do Linux estar compilando a maior parte da dinâmica (RedHat: yes) ou não. Por outro lado, coisas como patchelf podem reprojetar binários dinâmicos (ELF, mas provavelmente não a.out format) para usar outras bibliotecas.

No entanto! Ser capaz de executar um programa é uma coisa e realmente fazer algo útil com outro. Binários antigos de 32 bits da Intel podem ter problemas de segurança se dependerem de uma versão do OpenSSL que tenha algum problema de segurança horrível e não suportado, ou o programa pode não ser capaz de negociar com servidores web modernos (como o moderno os servidores rejeitam os protocolos e cifras antigos do programa antigo), ou a versão 1 do protocolo SSH não é mais suportada, ou ...

    
por 29.01.2018 / 22:13
65
Em resumo: Se você está pegando um binário compilado de um host para outro usando a mesma (ou compatível) arquitetura , você pode estar perfeitamente bem levando-o para outra distribuição . No entanto, conforme a complexidade do código aumenta, a probabilidade de estar vinculada 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 compilado com gcc -o exit-test exit-test.c em 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, este binário não será executado se eu chutar 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 querida. 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 este caso de uso, o forklifting falhou devido à falta de bibliotecas compartilhadas.

No entanto, se eu compilar com gcc -static exit-test-static exit-test.c , portá-lo para o sistema sem as bibliotecas funciona muito bem. À custa, é claro, do 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 skins, duas das quais são descritas acima.

    
por 29.01.2018 / 22:24
26

Somando-se às excelentes respostas @thrig e @DopeGhoti: Sistemas operacionais Unix ou Unix-like, incluindo Linux, foram tradicionalmente sempre projetados e alinhados mais pela portabilidade do código-fonte do que pelos 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 qualquer problema entre praticamente qualquer qualquer versão do Linux ou arquitetura como código-fonte contanto que os servidores de destino tenham os pacotes de desenvolvimento C instalados , as bibliotecas necessárias e as bibliotecas de desenvolvimento correspondentes instaladas.

Além de portar código mais avançado de versões mais antigas do Linux 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 considerar bibliotecas / APIs / ABIs obsoletas. / p>     

por 29.01.2018 / 22:21
19

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

Você pode , entretanto, compilar muitos programas - mesmo que não-triviais - para serem portáveis entre 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 o 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ê construir seu aplicativo usando as ferramentas LSB (como o lsbcc wrapper para gcc ) e vincular à versão LSB das bibliotecas, você criará um aplicativo portátil.

    
por 30.01.2018 / 00:37
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 do modo de usuário com binfmt_misc, mas essa não é uma configuração normal). Binários x86 podem funcionar em amd64, mas apenas se as bibliotecas de 32 bits necessárias estiverem disponíveis.
  2. Versões da biblioteca. Se a atitude estiver errada, não encontrará a biblioteca. Se o soversion é o mesmo, mas o binário é construído contra uma versão mais nova da biblioteca do que está sendo executado, então ele pode falhar ao carregar devido a novos símbolos ou novas versões de símbolos. Em particular, o glibc é um usuário pesado de versões de símbolos, então binários construídos contra um novo glibc provavelmente falharão com um glibc mais antigo.

Se você evitar o uso de bibliotecas que mudam rapidamente, evite mudanças de arquitetura e construa sobre a distro mais antiga que deseja direcionar, você terá uma boa chance de fazer um trabalho binário em muitas distros.

    
por 30.01.2018 / 15:35
4

Além de algumas das coisas mencionadas anteriormente, houve algumas alterações no formato de arquivo executável. Na maioria das vezes, o linux usa ELF, mas versões mais antigas usam a.out ou COFF.

O começo de um wikihole:

link

Pode haver uma maneira de obter versões mais antigas para executar formatos mais recentes, mas eu pessoalmente nunca analisei isso.

    
por 01.02.2018 / 13:55