Caos binário
Saiba por que a compatibilidade binária no Linux ainda é um problema, e o que é feito para resolvê-lo
Um dos grandes problemas que os desenvolvedores e integradores enfrentam no mundo Linux é a falta de compatibilidade total entre aplicações compiladas para diferentes distribuições do Linux. Não que este problema afete gravemente a maioria dos usuários - muitos deles necessitam apenas dos pacotes fornecidos no CD da sua distribuição preferida, nem que isto afete os usuários de softwares livres, afinal, basta recompilar e tudo está resolvido. Entretanto, usuários que necessitam de softwares proprietários em GNU/Linux, como o Kylix, o Oracle ou o Websphere, são freqüentemente afetados por problemas de compatibilidade.
Por exemplo, usuários do Kylix 1 não conseguiam executá-lo no Red Hat 7.1, apenas no 6.2 ou 7.0. Já os administradores de bancos de dados Oracle 9i necessitam do Red Hat 7.2 ou 7.3, mas não conseguem utilizar o 7.0, que foi lançado há apenas um ano. E aquele porte do DOOM para Linux, que de vez em quando dá saudades? Eu descobri que ele só roda no Red Hat 6.0 ou 6.1 (mas felizmente existe uma compilação mais atualizada disponível para download).
Neste artigo, vamos ver por que a compatibilidade binária ainda não é garantida no mundo Linux e vamos aprender como contornar os problemas mais comuns.
Bibliotecas e componentes
Costuma-se definir uma distribuição do GNU/Linux pela versão do kernel: distribuições baseadas no antigo 2.0.x, distribuições mais recentes baseadas no 2.2.x ou as distribuições atuais que são baseadas na série 2.4.x. Para o desenvolvedor de aplicações esta é a característica que menos importa. A não ser que os seus softwares dependam de recursos específicos do kernel, como se o firewall usado é ipfw, ipchains ou iptables, ou que haja dependência de um módulo de kernel (em geral um device driver), versões diferentes do kernel não vão afetar nem a compilação nem os executáveis da sua aplicação.
Outro fator que vem logo à mente, mas que também influi bem menos do que se imagina, são as versões de componentes importantes de uma distribuição, como o XFree, o Apache ou o Samba. A maioria dos grandes componentes do sistema já atingiu um nível de maturidade tal que as interfaces de programação (APIs) não mudam de uma versão para outra e, assim sendo, tanto faz utilizar a versão 3.3.6 de uma biblioteca quanto utilizar a versão 4.0.1.
O principal componente que torna a maioria dos executáveis compatíveis ou não com uma distribuição é a biblioteca C do sistema, ou libc. Mantendo o referencial na distribuição Red Hat, a série 4.x utilizava a libc5, enquanto que a série 5.x passou para a libc6 (glibc 2.0). A série 6.x adotou a glibc 2.1 e a 7.x adotou a glibc 2.2. Praticamente, 100% dos executáveis são linkeditados contra a forma dinâmica desta biblioteca, e nunca foi considerado prioritário (ou possível) manter a compatibilidade retroativa nas novas versões, mesmo quando a mudança era aparentemente pequena (como da 2.0 para a 2.1). Assim sendo, um programa compilado para a glibc 2.1 não vai rodar com a glibc 2.2 e vice-versa, mesmo que ambos se limitem a utilizar funções inalteradas nos últimos 10 anos.
A boa notícia é que o Linux permite que várias versões da mesma biblioteca estejam instaladas no sistema, e é capaz de manter várias delas ativas em memória simultaneamente, de modo que podemos ter várias versões da glibc instaladas e cada aplicativo será amarrado em tempo de execução com a versão apropriada. No caso do Red Hat, o CD de instalação fornece uma série de pacotes “compat” que são nada mais do que a glibc e outras bibliotecas muito utilizadas como estavam em séries anteriores da distribuição.
Usuários do Oracle 8i, por exemplo, devem instalar estes pacotes de compatibilidade sempre que desejarem rodar o banco na série 7.x. O servidor de banco de dados funciona perfeitamente, mas se você necessitar compilar algum cliente Oracle, digamos um aplicativo Gnome escrito em C++ ou um módulo de autenticação para o Apache, deverá enfrentar problemas, pois não podemos misturar referências das versões diferentes de uma mesma biblioteca no mesmo executável. É por isso que também são fornecidos os compiladores C/C++ nos pacotes de compatibilidade, de modo que seja possível compilar uma aplicação inteira utilizando as bibliotecas estáticas e dinâmicas de uma versão anterior. Isto resolve a compilação do módulo de autenticação para o Apache (você vai compilar um Apache inteiramente novo incluindo todos os módulos utilizados, como o PHP), mas não o da aplicação Gnome, a não ser que você realmente pretenda recompilar a Glib, o GTK+ e as bibliotecas do Xfree.
A compatibilidade de bibliotecas dinâmicas aparece em maior ou menor grau em outras bibliotecas além da glibc. Aplicativos escritos para o KDE, por exemplo, são particularmente sensíveis, pois o Qt exige a biblioteca padrão do g++ (libstdc++) que tem um histórico de mudanças incompatíveis bem mais freqüente do que a glibc. Já aplicações Gnome costumam funcionar bem dentro da gama maior de distribuições, como podemos atestar pelas distribuições binárias do Mozilla e do OpenOffice.
Pacotes e Diretórios
Os gerenciadores de pacotes, estilo RPM ou DEB, deveriam resolver estes problemas com os seus mecanismos de controle de dependências. Na prática, vemos que dificilmente os pacotes RPM compilados para uma distribuição funcionam em outra distribuição. Isto decorre de vários fatores, como por exemplo:
1. Diferentes distribuições empacotam componentes em grupos diferentes, como X-client e X-server, ou X, X-libs e X-devel, obrigando-nos a especificar o famigerado --nodeps para instalar os pacotes.
2. Mesmo quando os nomes dos pacotes conferem, ainda existe uma considerável variação sobre em que diretório cada componente é instalado (/usr, /opt, /usr/local, /var, ...). Este problema é resolvido facilmente com alguns links simbólicos - por que será que os empacotadores de RPMs nunca pensaram nisso?
3. Não é simples misturar componentes instalados pelo gerenciador de pacotes e componentes instalados por outros meios, como os compilados a partir dos fontes ou que utilizem um programa de instalação estilo InstallShield.
Infelizmente, esforços como o LSB (Linux Standard Base) ainda estão longe de ser incorporados na prática pela maioria das grandes distribuições, e cada uma delas tem que lidar com sua própria base instalada.
As diferenças relativas a nomes de pacotes e de diretórios se manifestam de maneiras mais sutis, pois é prática comum no mundo Unix (prática herdada pelo mundo GNU/Linux) que um programa execute outros programas para realizar suas tarefas. Se algum desses comandos muda a sua sintaxe (como o comando smbmount na série 2.0.x do Samba), nosso aplicativo não funciona mais. Mas e se o comando mudar do /bin para o /usr/bin, ou para o /usr/sbin ou ainda para o /usr/local/bin? Devemos invocar o comando pelo seu caminho completo ou confiar na correta configuração da variável de ambiente PATH?
No caso de softwares proprietários, existe uma completa ignorância em relação aos gerenciadores de pacotes das distribuições do GNU/Linux. Cada um fornece o seu próprio instalador proprietário, com a exceção talvez da IBM. O Kylix, por exemplo, instala entradas no banco de dados do RPM como se houvesse sido instalado pelo gerenciador de pacotes, mas você não pode desinstalar o Kylix ou qualquer de seus componentes pelo RPM. Menos mal, pois esta prática da Borland permite que meus RPMs possam incluir a dependência em relação aos componentes do Kylix.
Fontes x Binários
A comunidade de software livre desenvolveu uma série de mecanismos para lidar com estas pequenas variações de configuração que afetam a compatibilidade dos aplicativos, entre eles o GNU Autoconf (responsável pelo script “configure” que rodamos como primeiro passo na compilação da maioria dos softwares livres). A indústria de softwares proprietários está tendo mais dificuldades em se adaptar a estas pequenas variações devido aos seus processos mais burocráticos de controle de qualidade e à impossibilidade de participação de pessoas externas.
O software livre se adapta bem às variações e os autores de software não necessitam “suportar” todas as variações; os membros interessados da comunidade se encarregam de fornecer esse suporte. O software proprietário não tem uma comunidade que possa realizar o re-empacotamento e resolver problemas de compatibilidade. Assim, não resta outra alternativa a não ser “certificar” quais distribuições, e em que versões, podem ser utilizadas com cada pacote proprietário. Para a maioria dos usuários, não existe alternativa a não ser se limitar ao conjunto de distribuições “certificadas” pelo fornecedor.
É uma pena que as empresas de software proprietário não estejam incorporando os mecanismos já criados pela comunidade de software livre para suportar de forma economicamente viável mais distribuições, em vez de apenas SuSe e Red Hat ou algo semelhante.
Pior ainda é o fato de estas empresas não fornecerem informações suficientes sobre quais as dependências dos seus softwares, de modo que a comunidade ou os fornecedores de distribuições possam amenizar os problemas. Vários softwares proprietários vêm com especificações do tipo “requisitos de software: Linux com kernel 2.2.x”, o que, como já vimos, não significa nada na maioria das vezes.
O fato é que nunca devemos esperar por uma padronização “100%” no mundo GNU/Linux. O software livre é aberto à diversidade, e esta diversidade é necessária para que a tecnologia continue a evoluir sempre. Começamos com necessidades bem práticas de trabalhar com diversas variantes proprietárias do Unix e diferentes arquiteturas de processadores, que levaram ao foco na compatibilidade no nível de fontes. Verifique como é fácil compilar o Apache ou o Gnome em dezenas de combinações diferentes de SO e Processador, depois compare com a dificuldade de compilar corretamente um aplicativo Windows nas limitadas opções de sistemas e compiladores deste mundo.
A compatibilidade binária era inicialmente algo inatingível - um executável para Pentium é necessariamente diferente de um executável Sparc ou PowerPC. Foi apenas a popularização do Linux em PCs Intel que tornou esta questão importante, mas ficamos com o aparente absurdo de ter várias versões binárias de um mesmo software no mesmo SO (Linux) e no mesmo processador (Intel). O resultado final é conseqüência da liberdade: se cada um pode fazer como quiser, fatalmente cada um fará de um jeito diferente. Padrões como o RPM, o DEB e o LSB certamente amenizam o problema, mas não nos interessa, na verdade, atingir o nível de 100% de padronização, porque senão perderemos a oportunidade de descobrir formas melhores de fazer as coisas, ou seja, a comunidade perderá a sua capacidade de fazer evoluir a tecnologia.
olhos: O principal componente
que define a compatibilidade dos executáveis é a biblioteca C do sistema,
ou libc
Para o desenvolvedor de aplicações, a versão do Kernel é o que menos importa
Instaladores de softwares proprietários simplesmente ignoram os gerenciadores de pacotes das distribuições GNU/Linux
para saber mais:
Linux Standard Base - www.linuxbase.org
Página do autor - www.lozano.eti.br
Fernando Lozano - fernando@lozano.eti.br