Usando a libc nativa do Windows

Caloni, 2007-11-21 computer blog

Por padrão, todo projeto no Visual Studio depende da libc. Isso quer dizer que, mesmo que você não use nem um mísero printf em todos os projetos criados, está atrelado a essa dependência. Em tempos onde fazer um "Hello World" pode custar 56 KB em Release - Visual Studio 2005, configuração padrão sem "buffer security check" - vale a pena economizar alguns KBytes que não se vão usar. Principalmente se essa possibilidade existe desde o cavernoso Windows 95.

Crie um novo projeto console Win32 vazio (File, New, Project, blá blá blá) e coloque um código de Hello, World nele. Configure para ele usar uma runtime estática e veja o tamanho do executável gerado. Aqui após configurar um projeto ordinário que compila um executável console ordinário que não depende de runtimes novas (exceto a kernel32.dll) meu arquivo está com 96 KB.

#include <stdio.h>

int main()
{
  puts("oi, mundo!\n");
}

Desde o Windows 95, existe uma DLL na pasta de sistema chamada msvcrt.dll com a maioria das funções da libc disponíveis para link dinâmico (update 2026-02-20: sim, ela ainda está lá). Só que, com o uso padrão do Visual C++, é usada sempre a biblioteca que vem junto com o ambiente, com suas trocentas funções (e consequentes bytes enche-linguiça). Porém, é possível utilizar diretamente a msvcrt.dll distribuída no diretório do sistema se criarmos uma LIB de importação para ela.

Tudo que você precisa fazer é gerar um msvcrt.def com as funções exportadas por essa dll usando o comando dumpbin.exe /exports msvcrt.dll e gerar uma lib de importação com o comando lib.exe passando no parâmetro /DEF o arquivo gerado. Abaixo um exemplo de como deve estar esse arquivo antes do comando lib /def:msvcrt.def:

LIBRARY msvcrt
EXPORTS
_CrtCheckMemory
_CrtDbgBreak
...
wprintf_s
wscanf
wscanf_s

Depois desse último passo geramos a LIB que precisávamos e agora só falta integrar com o projeto. Copie o msvcrt.lib para o diretório do projeto e configure no projeto esse arquivo na lista de LIBs a serem incluídas (Properties, Linker, Input, Additional Dependencies). Lembre-se de ignorar todas as LIBs padrão (Linker, Input, Ignore All Default Libraries). Para evitar unresolved external em frescuras de segurança ignore as firulas de checagem (C/C++, Code Generation, Buffer Security Check, e Basic Runtime Checks em Debug). Antes de mais nada deixe explícito o nome da função de entrada de seu programa para main, pois do contrário ele irá usar um bootstrap que inicia a libc (Linker, Advanced, Entry Point), compile e linke. E voilà!

E agora o tamanho final de nosso executável passou para espantosos 3 KB! Isso a princípio parece ótimo e dá vontade de usar em todos os projetos, mas existe um porém ainda não resolvido: as limitações da falta de um runtime. Essa é uma solução bem bobinha que não tem nada a ver com uma solução profissional 100% garantida e com suporte técnico 24 horas. Algumas coisas não vão funcionar, como inicialização de variáveis estáticas, exceções, redirecionamento de entrada/saída, etc. Contudo, para projetos simples e pequenos, isso não deverá ser um problema. No entanto, eu não garanto qualquer coisa que advier de compilações inspiradas neste artigo.

// Comments

2008-04-15 Yorick:

Recentemente encontrei isso, acho q tem a v :)

Testei o metodo do Koby e funciona muito bem, como Koby menciona em seu site, eh bem menos sujeito a erros para programas mais complexos, estou usando em compilações em modo release, em debug mode continuo com o msvcr90d.dll do Visual C++ 2008. Vinculação à msvcp que contém C++ stuff como STL não é possivel de forma dinâmica utilizando o mesmo método, se for seguidos os passos do WDK é possível a vinculação estática. A seguir os procedimentos que usei para poder usar a crt dinâmica + stl estática:

Additional Include Directories:
C:\WinDDK\inc\crt;C:\WinDDK\inc\api;C:\WinDDK\inc\api\crt\stl70

Preprocessor Definitions
WIN32;NDEBUG;_CONSOLE;_STATIC_CPPLIB;_STL70_

Additional Library Directories
C:\WinDDK\lib\crt\i386;C:\WinDDK\lib\w2K\i386

Generate Manifest
No

Additional Dependencies
msvcrt_win2000.obj ntstc_msvcrt.lib msvcprt_btowc.lib

2008-10-28 George Luiz Bittencourt:

Olá,

Com o parâmetro /MD do compilador é possível fazer isso sem a necessidade de criar uma lib.

-George


2008-10-28 Caloni:

Olá, George.

O parâmetro /MD cria um projeto que depende da DLL de runtime do Visual Studio (a msvcXX.dll, sendo XX a versão atual). Esse artigo demonstra como criar um projeto que não dependa de nenhum tipo de LIB estática ou distribuída pelo Visual Studio. No entanto, os projetos compilados com essa solução passarão a depender da DLL de runtime localizada no diretório de sistema do próprio Windows.

[]s


2008-11-13 George Luiz Bittencourt:

Olá,

Concordo com você, realmente desse jeito não irá depender de nenhuma LIB do Visual C, seja ela estática ou dinâmica.

Alêm do tamanho reduzido outra vantagem de se linkar de forma dinâmica é que uma eventual atualização no runtime beneficia todos os programas que utilizem essa DLL. De forma estática isso não é possível já que o conteúdo dos arquivos OBJ (archive members da LIB) é copiado para o módulo final.

A propósito, você trabalha com C/C++ em SP? Como que é o mercado ai nesse ramo? Tem muita demanda?

Valeu e o seu artigo ficou muito bom! Legal também é a LIBCTINY.LIB do Pietrek.

[]s

-George


2008-11-13 Caloni:

Sim, trabalho atualmente em São Paulo, Brasil. O mercado? Não sou uma pessoa muito indicada para falar sobre isso, pois trabalhei até hoje apenas em dois lugares. O que ouvimos falar quase sempre no grupo de oportunidades C++ são de empregos para essa área, quase a maioria.

[]s

[comment] [Carregando DLLs arbitrárias pelo WinDbg]