COMPACTADORES DE EXECUTÁVEIS
Vale a pena ou não usar este recurso com cara de gambiarra?

A pergunta normalmente começa, em uma lista de discussão sobre Delphi, da seguinte forma:

"Ai pessoal! Descobri um programinha que diminui o tamanho dos executáveis. Ele é impressionante: um exe de 4,9 Mb reduziu para 900K. O que gostaria de saber, é se é confiável. Alguém já testou bem um executável reduzido gerado por este aplicativo??? Preciso saber desta informação antes de usá-lo na prática..."

E a polêmica começa a se arrastar pelos E-Mails.

O que realmente é um compactador de executáveis? Seria algo como o Pkzip que compacta um arquivo usando o algoritmo de tabelamento de bits? ou seria um programa que faz uma "faxina" no executável eliminando lixo de compilação e código desnecessário que é incorporado a ele no momento da linkedição?

Os compactadores de executáveis são uteis para compactar executáveis ou dll's gigantescas (algo que gira em torno dos 2MB pra cima) e descompactá-los no momento da execução do programa. Provavelmente ele emprega um algoritmo de compressão no executável original (do tipo ZIP) e engloba-o com seu próprio código de descompressão. Sempre que o arquivo for executado, o executável de descompressão embutido é iniciado antes, descompactando o executável principal, e expandindo todo o seu conteúdo para a memória (processo que consome um pouco de tempo e memória) e quando ele é finalizado, o executável de compressão faz o processo inverso. O problema destes compactadores não é o algoritmo usado. O problema é a forma como o programa irá se comportar com este recurso. Em alguns casos é bom, em outros não! Depende de como sua aplicação irá funcionar, se for um simples utilitário, aberto de vez em quando, não há restrições ao seu uso, mas se o software rodar em tempo integral, não seria recomendado este tipo de recurso. As opiniões são divididas e as experiencias com quem já usou este tipo de compactador variam:

"Eu já testei estes compactadores e sem dúvida fiquei espantado
com o resultado, porém, tive problemas que até hoje não sei pq
aconteceram mas, somente aconteciam com o EXE compactado!
Meu cliente me ligou com problemas e eu debugava o programa,
rodava testes e não encontrava, porém, tudo com o EXE normal.
Quando tive a feliz idéia de compactar e depois testar, tive o
mesmo problema que meu cliente. A partir deste dia, nunca mais
utilizei compactadores de executável."
Valfrid-Ly Silva Couto

"Colegas, já utilizamos o ASPACK com dll de tamanhos por volta de 3 a 4 mb,
e reduzia bastante o o uso ficava legal, mesmo com 30 usuarios usando as dll em rede."
Jr Terenzi.

"Eu já testei ele, e ele diminuiu muito o tamanho do exe, mas como eu uso um
único executável na rede, nos testes quem abria primeiro o programa primeiro
funcionava normal mas quem abria depois, ou dava pau ou demorava muito.
não sei se era pq a versão era demo, 30 dias."
Carlos.

"Tive uma experiência bem desagradável, utilizei o Aspack para compactar meus
aplicativos e acreditem, os mesmos ficaram com 25% do tamanho original em
média, mas dos 9 módulos compactados, 2 deram um "pau" daqueles, e pra
ajudar na casa do cliente, mesmo recompilando o programa e recompactando, o
"bug" persiste, o que me fez desistir do compactador."
Paulo Santana

A regra básica é sempre a da avaliação do produto antes de oficializar a ferramenta como parte integrande do processo de desenvolvimento do software. Também é totalmente desaconselhável implantar uma aplicação no cliente, que tenha sido submetida a um processo desta natureza, sem que você tenha consolidado a técnica de uso e a resultado final do aplicativo que irá fazer isto. Cliente não pode ser cobaia. Cliente é quem usará seu produto final. Se você quer fazer ele de cobaia, então faça-o profissionalmente, fazendo um contrato de "Beta tester" entre as partes e que exclareça todos os riscos e as necessidades de avaliações e acompanhamentos, o que lhe permitirá então ter um ambiente real de testes de seu produto novo, além do que, você não se queima com isto.

FUNCIONAMENTO...

Compactadores de executáveis não são coisa recente. Já nos tempos do Clipper, usavamos o Pklite que era um compactador de programas. Fabricado pela PKware (Fabricante do PKZip), tinha uma enorme vantagem para nós programadores: O programa compactado não podia ser descompilado. Muitos não tinham como perceber isto a não ser os programadores mais avançados que jogavam o Exe no debug do Dos e viam as tarjas do Pklite no código binário, ai o segredo era desmascarado! Outro problema do Pklite é que ele tinha um parametro que permitia o usuário descompactar o Executável no disco, o que novamente expunha o código à ação dos Walkyrie e Rescues da vida. Posteriormente, as versões do Pklite passaram a vir com um novo parametro que não permitia mais a descompactação do exe em disco. É bom lembrar que o Pklite só funciona com aplicativos MS-DOS que rodam em memória convencional. Se o executável foi gerado com um linkeditor de modo protegido (Rodar na memória expandida, acima dos 640K) tipo o Exospace, ou com o BLinker usando a configuração de linkedição para modo protegido, seu executável não podia ser compactado sob o risco de falhas de proteção ao ser carregado.

Com a tecnologia do Windows 32Bits, os executáveis mudaram radicalmente sua estrutura interna. Eles passaram a ser conhecidos como Executável PE (Portable Executable), pois podem assumir diversos formatos que não o .EXE tradicional, e também somente serem executados mediante certas circunstâncias ou somente dentro de outros aplicativos. Antes eles possuiam somente a área de Stack, Variaveis e do código que era dividida em binária e strings. Hoje além destas áreas, eles ainda possuem a área de Resources, que é onde ficam armazenados os ícones, imagens, telas e outros arquivos binários, que não precisam ou não podem ser compilados, mas que precisam estar junto com o executável, e também outras áreas e tabelas de controle interno dele. Portanto, nos dias de hoje, é muito mais facil você criar um arquivo executável de mais de 2 MB com apenas meia dúzia de linhas de código. O problema disto é que ele irá consumir muito mais memória do que um exe de apenas 300K.


O Aspack é um dos compactadores mais usados por programadores

O funcionamento de um compactador de EXE, é basicamente semelhante ao de um Zip. Ele comprime apenas os resources files (bmp, ico, wav, forms, wmf, languages, etc...) que contém o seu executável. Na área de Bytecode, ele pode atuar compactando algumas partes como as strings, porém sem tocar no código binário (Compactar o código de máquina), porque se ele fizer isto, seu executável corre o risco de deixar de ser um executavel Win32 e não carregar ou na melhor das hipóteses rodar com bugs estranhos e dando pau toda hora.

Você pode comprimir todas as sessões de um PE, menos a byte-code, que é a área onde se encontra o código fonte compilado + funções internas de gerenciamento do programa. Quando fazemos um run | debug no Delphi, e dissassemblamos o código, a parte que nos interessará será a do BYTECODE. O restante das sessões são importantes, mas não para depuração ou processamento do programa. Você pode ver isto de uma outra forma: Compile seu projeto mandando gerar um .map. Nele você poderia conferir essas sessões e para onde elas estão indo. Portanto, só de você comprimir todas as sessões, mantendo apenas o BYTECODE intactado, haveria economia de, pelo menos 60%, mas note que não econonizou nenhum pouco em RAM.

O .map será assunto para um artigo futuro. Mas, por enquanto, para você gerar um .map, basta ir em project | options | linker tab. Seleciona a geração do .map detalhado. (Detailed)

Aliás, o arquivo .map é um recurso muito útil quando se procura a causa daqueles Access Violations sobrenaturais que aparecem em seu programa de uma hora pra outra. Basta você anotar o endereço que ocorreu a exceção e em seguida procurar a referência dele no .map.

Uma ferramenta que auxilia a análise do .map é o AVFinder, que voce pode pegá-lo em http://www.cri.ch/downloads
Voce pode também criar um programa para scanear um .map. No site do Dr. Bob's, tem um exemplo de como fazê-lo: http://www.drbob42.com/delphi/findmap.htm

Mas, voltando ao assunto do compactador, um PE é cheio de partes que podem ou não ser úteis, a desvantagem de quem usa uma ferramenta de desenvolvimento do tipo RAD (Delphi ou C++ builder), é que a maioria destas sessões são criadas na compilação e não são usadas para nada. É o conhecido "Lixo de compilação" muito citado neste e outros artigos. Um programador em C++ ou Assembler acostumado a programar na munheca mesmo, descartaria a maioria destas sessões porque está acostumado a fazer faxina em seus programas. A otimização de código, a que referimos, deve ser o descarte delas. Pois o BYTECODE é o codigo do programador e não tem como otimizá-lo binariamente sem colocar em risco sua execução. Se tem alguma coisa que pode-se fazer para otimizar o BYTECODE, deve ser feita em tempo de codificação do projeto e não depois do projeto pronto.

Mas é aí que alguns compactadores podem atuar. Eles podem fazer este trabalho sujo para o usuário removendo as partes inúteis, tais como a tabela de realocações, tabela de exportações (que quase sempre não são utilizadas em versoes finais, como nos executaveis gerados pelo C++ Builder), Thread Local Storage, realocações, areas não inicializaveis. Além disso, alguns compactadores realinham totalmente o executável, reduzindo alguns Kb inúteis. Sendo assim, eles fazem mais do que compactar código e dados do executáveis, eles podem remover lixo interno que só serve para engordar o PE. Mas será que fazem isto bem feito e com competência??? Nem sempre. Ocasionalmente os compactadores que prometem "Otimizar o executável" acabam mesmo é introduzindo bugs no projeto. Isto porque eles, na tentativa de otimizar o código, podem chegar a afetar a área de BYTECODE de modo a não tornar o programa inválido mas apresentando um comportamento anormal no seu uso cotidiano. Daí a reclamação de alguns usuários com relação ao comportamento de seu programa após ter sido usado um compactador de EXE.

Por fim, um executavel compactado pode ter outros efeitos colaterais, ele gasta a memoria total associada ao executavel, desde sua inicialização. Por exemplo, um executável comum, de 1Mb, ao ser carregado pode consumir inicialmente uns 800Kb...e soh requisitar os outros 200Kb (geralmente de resources) mais tarde. O executavel compactado, já irá requisitar logo na inicialização o 1Mb inteiro. Em termos práticos, não há tanta diferença assim, visto que mesmo no caso do executável normal, os 200Kb que não foram "requisitados" estão reservados pelo sistema desde o início e, portanto, não podem ser usados por outros processos. Desse modo, o maior efeito colateral mesmo é o pequeno atraso no carregamento.

Quando o executavel compactado é descompactado, do disco para a memória, se o sistema estiver com baixos recursos de memória ou de processador, a operação irá exigir acesso e manipulação do arquivo de troca o que causará uma certa lentidão. Pior ainda será se o espaço disponível na área de Swap já estiver todo tomado. Seu executável corre o risco de não ser executado e ainda por cima derrubar o sistema.

Múltiplas instâncias de um executável ou dll compactados criam múltiplas instâncias do código na memória. Se você tem um EXE compactado, que contém 1 MB de código (antes de compactação), e o usuário executar 5 instâncias dele, aproximadamente 5 MB de memória serão gastos nesta brincadeira. Pior ainda é o caso da DLL: Se você tem uma DLL, que é de 1 MB de tamanho, e é usada por 5 aplicações correntes, aproximadamente 5 MB de memória serão requeridos neste processo, já que para cada requisição do processo, uma nova instância do código será criada. Imagine DLL's que são requeridas por 10 ou 20 aplicações? Com EXE's ou DLLs não compactados, o código só é instanciado uma única vez na memória e será compartilhado entre todas as instâncias necessárias.


O tElock é um compactador que encripta o executável também, protegendo-o da ação de desassembladores

CONCLUSÃO

Os compactadores, na minha opinião, não ajudam em nada. Eles compactam o .EXE, mas o programador de verdade sabe que um programa de 4MB compactados, digamos em 800KB, serão os mesmos 4MB depois de descompactados. E como só dá para usar um .EXE descompactado, então não houve nenhuma vantagem, se não a performance de nanosegundos de leitura em disco, algo que é desprezível levando-se em conta que hoje vivemos a era dos Pentium 1GHz pra cima e não os tempos do 386sx. A desilusão surge quando se percebe que mesmo compactado, o executável continuará lento, gordo e pesadão na memória, já que não houve economia de RAM neste processo. O executável, de 4MB compactados para 800K, continuará usando os 4 ou mais MB de RAM que usaria, se não fosse compactado. A memória que vale qualquer tipo de economia neste sentido é aquela ocupada na RAM, e não no disco. Hoje em dia, as unidades de Disco Rígido não são vendidas com menos de 20GB de espaço, portanto sempre haverá espaço de sobra pra um executável de 4MB. Então me respondam, pra que compactar um executável? A quem estão querendo enganar, tentando encolher um programa cheio de penduricálhos e bugigangas desnecessárias, fazendo-o passar por enxuto?? Só pode ser esta a explicação!

Outra coisa importante, o Delphi permite separar resources files do aplicativo e colocá-los em uma DLL (igual ao moreicons.dll no windows) para os mais puristas, em questão de performance, esta é uma solução muito melhor já que os resources não só estariam fora do executável, mas também poderiam ser usados por outros executáveis. Outra forma seria usar os Packages em run time... desta forma então nem se fala! Porém a desvantagem dos packages é justamente a primeira distribuição que faz-se necessário o envio das BPL's... Mas para atualizações você já não precisará enviá-las mais, a não ser que foi atualizado algum componente ou arquivo que esteja embutido nesta referida package. Mas isto gera uma certa resistência por parte dos programadores, já que tal procedimento irá exigir um controle mais rigoroso de versões do projeto.

Diante de tudo isto, podemos fazer uma conclusão lógica sobre compactação de executáveis:

- Se for compactar para distribuição: não vale a pena, os instaladores têm compactação superior à compactação do executável. Para melhorar a distribuição é melhor investir no uso de DLLs e BPLs.

- Se for para "proteger" o executável: não protege nada, se existe o compactador, existe o descompactador, basta procurar nos sites hacker. É melhor investir em softwares de proteção.

- Se for para diminuir o espaço em disco: o custo do mega de HD está bem abaixo do custo que você terá, testando a versão compactada do software (tempo é dinheiro). Não vale a pena investir nisto, a não ser que seu cliente insista neste ponto.

A "otimização de arquivo executável" pode ser muito bem substituída pela "otimização do código". Revisão em todo o projeto, eliminando componentes e forms desnecessários, bem como a utilização de API's para tarefas que já existem no Windows é uma ótima sugestão. Muitos componentes podem ser perfeitamente substituídos por simples funções, bem como muitos forms podem ser herdados de outros forms (Herança), o que irá reduzir bastante, o número de objetos inseridos no programa. Outros componentes, cujo propósito é apenas visual, podem ser removidos deixando as telas mais limpas e menos papagaiadas. Telas de softwares devem ter pouca poluição visual e não aparência de carro alegórico ou árvore de natal.

Se formos levar a coisa por este caminho, então podemos voltar mais atrás ainda, na análise do sistema e rever se realmente aquelas 15 janelas podem ser convertidas em apenas 2 ou 3... Na compilação final, você poderá perceber que seu executável já caiu de 4MB para apenas 2MB, parece que a coisa começa a melhorar. Então experimente ir no menu Project | Options | Orelha Compiler, e desmarcar as opções de Debugging, deleta todos os arquivos .DCU do projeto e recompila tudo novamente. Verá que ele caiu ainda mais e começou a ficar mais rápido para rodar. Este é o caminho pregado pela qualidade, ao contrário do caminho pregado pela porcariagem.

Herança de Forms, muito util além de reduzir o tamanho fisico do programa, reduz 90% do trabalho de desenvolvimento e manutenção... A ideia é ter um form "genérico" e todos os outros forms derivam dele, a maior parte do código fica nele, os derivados só ficam com as coisas q o diferenciam do resto ;) voce vai acabar tendo toda uma hierarquia de classes, para forms, datamodules, frames, etc....

Outro coisa interessante para ser feita, visando a melhoria do executável, é quebrá-lo em módulos (podendo ser DLL's). Nem todas as janelas serão frequentemente usadas no programa, você não concorda? Então que tal colocarmos as janelas menos usadas, tais como as de configurações do programa, em forma de um arquivo .CPL e deixá-la lá quietinha no painel de controle? Outras telas, tais como About, Cadastros pouco usados e rotinas de apoio ao programa (Funções e procedimentos genéricos) podem ser colocados em Dll's. No final da história você pode acabar com um Executável, que era de 4MB para 800Kb. Ou seja, você chegou ao tamanho do executável compactado sem precisar usar o compactador de executáveis. E ai? O que me diz?

Imagens e outros resources podem ser racionados ao máximo possível. Você não precisa colocar em seu projeto imagens de altíssima resolução (Que são bem maiores). Experimente reduzir o tamanho delas para apenas 256 cores e com qualidade média. Verá que o tamanho do arquivo caiu para mais da metade, ou seja, um arquivo .res que tinha inicialmente 600k, caiu para 200k. Conclusão? Na proxima compilação seu exe terá 400k a menos de tamanho por conta disto.
O carregamento de imagens a partir do disco, como muitos costumam fazer, é desaconselhável por requerer operações de I/O, reduzindo com isto, a performance do seu programa (Já que ele estará a mercê da velocidade do HD). Também está descartado o carregamento de imagens a partir de DLL's ou BPL's, já que isto requererá instanciamento da biblioteca o que irá não só reduzir a performance do seu programa (Tempo de instanciamento) como irá abocanhar recursos da maquina.

O Jordan Russel (que entre outras, é autor do Inno Setup e ToolBar (97 e 2000)), fez um programinha chamado StripReloc. Este programa remove a seção de recolocação (".reloc") dos executávies Win32 PE, reduzindo o tamanho deles. A maioria do compiladores/linkeditores (inclusive o do Delphi) insere esta seção no executável e é uma seção que nunca será usada e não tem outra finalidade senão desperdiçar espaço na memória e em disco.

Você pode acessar o site sobre este assunto em http://www.jrsoftware.org/striprlc.php#execomp e lá também fazer o download do programa neste endereço.


O StripReloc é uma ferramenta que remove parte do lixo de compilação de um Executável PE

Mas se mesmo assim você ainda pensa em usar compactadores de executáveis, deve tentar produtos mais amplamente testados e, de preferência, que não sejam freeware, beta, ou de autoria de crackers (crackers são os maiores autores de ferramentas de alteração de executáveis), pois assim, se você tiver problemas, terá a quem recorrer. Sem contar que os compactadores comerciais geralmente não tentam todos os tipos neuróticos de otimização citados acima e, portanto, causam menos problemas. Cuidado que muitos Freewares podem ser na verdade Trojans, Worms ou backdoors disfarçados de Compactadores e que tranformam seu executável em um "servidor" de dados para hackers, crackers e outros tipos possuídos pelo Espírito de porco. Lembre-se que o responsável pelas invasões ou vazamento de informações confidenciais por culpa de falhas de segurança interna no sistema será sempre do programador e nunca do cliente.

Abaixo os links dos compactadores mais conhecidos no merdado:

ASPACK SOFTWARE

UPX - the Ultimate Packer for eXecutables

E no mais, perdoem-me por viajar tanto em um assunto que divide tanto opiniões como este...



E-Mail: walterchagas@yahoo.com Clique aqui para cair fora desta pagina.