Eu sei que isso pode parecer bastante básico para geeks. Mas quero deixar isso bem claro.
Quando eu quero usar uma DLL Win32, geralmente eu apenas chamo as APIs como LoadLibrary () e GetProcAdderss (). Mas recentemente, estou desenvolvendo com DirectX9 e preciso adicionar os arquivos d3d9.lib , d3dx9.lib , etc.
Já ouvi o suficiente que LIB é para links estáticos e DLLs para links dinâmicos.
Portanto, meu entendimento atual é que o LIB contém a implementação dos métodos e está estaticamente vinculado no momento do link como parte do arquivo EXE final. Enquanto o DLL é carregado dinamicamente em tempo de execução e não faz parte do arquivo EXE final.
Mas, às vezes, alguns arquivos LIB vêm com os arquivos DLL, então:
- Para que servem esses arquivos LIB?
- Como eles alcançam o que pretendem?
- Existe alguma ferramenta que me permita inspecionar os componentes internos desses arquivos LIB?
Atualização 1
Após verificar a Wikipedia, lembro que esses arquivos LIB são chamados de biblioteca de importação . Mas estou querendo saber como isso funciona com meu aplicativo principal e as DLLs a serem carregadas dinamicamente.
Atualização 2
Exatamente como RBerteig disse, há alguns códigos de stub nos arquivos LIB nascidos com as DLLs. Portanto, a sequência de chamada deve ser assim:
Meu aplicativo principal -> stub no LIB -> DLL de destino real
Então, quais informações devem estar contidas nesses LIBs? Eu poderia pensar no seguinte:
- O arquivo LIB deve conter o caminho completo da DLL correspondente; Portanto, a DLL pode ser carregada pelo tempo de execução.
- O endereço relativo (ou deslocamento do arquivo?) De cada ponto de entrada do método de exportação DLL deve ser codificado no stub; Assim, saltos / chamadas de métodos corretos podem ser feitos.
Estou certo sobre isso? Existe algo mais?
BTW: Existe alguma ferramenta que pode inspecionar uma biblioteca de importação? Se eu puder ver, não haverá mais dúvidas.
fonte
lib /list xxx.lib
elink /dump /linkermember xxx.lib
. Veja esta pergunta sobre Stack Overflow .dumpbin -headers xxx.lib
fornece algumas informações mais detalhadas, em comparação com os utilitárioslib
elink
.Respostas:
A vinculação a um arquivo DLL pode ocorrer implicitamente em
tempo delink decompilaçãoou explicitamente em tempo de execução. De qualquer forma, a DLL acaba sendo carregada no espaço de memória dos processos e todos os seus pontos de entrada exportados ficam disponíveis para o aplicativo.Se usado explicitamente em tempo de execução, você usa
LoadLibrary()
eGetProcAddress()
para carregar manualmente a DLL e obter ponteiros para as funções que precisa chamar.Se vinculados implicitamente quando o programa é criado, os stubs para cada exportação de DLL usada pelo programa são vinculados ao programa a partir de uma biblioteca de importação e esses stubs são atualizados à medida que o EXE e a DLL são carregados quando o processo é iniciado. (Sim, simplifiquei mais do que um pouco aqui ...)
Esses stubs precisam vir de algum lugar e, na cadeia de ferramentas da Microsoft, eles vêm de uma forma especial de arquivo .LIB chamada biblioteca de importação . O .LIB necessário geralmente é criado ao mesmo tempo que a DLL e contém um esboço para cada função exportada da DLL.
Surpreendentemente, uma versão estática da mesma biblioteca também seria enviada como um arquivo .LIB. Não há uma maneira trivial de diferenciá-los, exceto que os LIBs que são bibliotecas de importação para DLLs geralmente serão menores (geralmente muito menores) do que o LIB estático correspondente seria.
Se você usa o conjunto de ferramentas GCC, por acaso, você não precisa importar bibliotecas para corresponder às suas DLLs. A versão do vinculador Gnu transferida para o Windows entende DLLs diretamente e pode sintetizar quase todos os stubs necessários na hora.
Atualizar
Se você simplesmente não consegue resistir a saber onde todas as porcas e parafusos realmente estão e o que realmente está acontecendo, sempre há algo no MSDN para ajudar. Artigo de Matt Pietrek Uma análise detalhada do formato de arquivo executável portátil Win32 é uma visão geral muito completa do formato do arquivo EXE e de como ele é carregado e executado. Até foi atualizado para cobrir o .NET e muito mais, desde que apareceu originalmente na MSDN Magazine ca. 2002
Além disso, pode ser útil saber como aprender exatamente quais DLLs são usadas por um programa. A ferramenta para isso é Dependency Walker, também conhecido como Depends.exe. Uma versão dele está incluída no Visual Studio, mas a versão mais recente está disponível com seu autor em http://www.dependencywalker.com/ . Ele pode identificar todas as DLLs que foram especificadas no momento do link (carregamento antecipado e atraso no carregamento) e também pode executar o programa e observar se há DLLs adicionais carregadas no tempo de execução.
Atualização 2
Eu reformulei parte do texto anterior para esclarecê-lo na releitura e para usar os termos da arte vinculação implícita e explícita para consistência com o MSDN.
Portanto, temos três maneiras pelas quais as funções da biblioteca podem ser disponibilizadas para serem usadas por um programa. A pergunta de acompanhamento óbvia é: "Como escolher qual caminho?"
A vinculação estática é como a maior parte do programa em si é vinculada. Todos os seus arquivos de objeto são listados e coletados juntos no arquivo EXE pelo vinculador. Ao longo do caminho, o vinculador cuida de tarefas menores, como consertar referências a símbolos globais para que seus módulos possam chamar as funções uns dos outros. As bibliotecas também podem ser vinculadas estaticamente. Os arquivos de objeto que constituem a biblioteca são coletados por um bibliotecário em um arquivo .LIB no qual o vinculador procura por módulos contendo símbolos necessários. Um efeito da vinculação estática é que apenas os módulos da biblioteca usados pelo programa são vinculados a ela; outros módulos são ignorados. Por exemplo, a biblioteca matemática tradicional C inclui muitas funções de trigonometria. Mas se você ligar contra ele e usar
cos()
, você não acaba com uma cópia do código parasin()
ou atan()
menos que também tenha chamado essas funções. Para grandes bibliotecas com um rico conjunto de recursos, essa inclusão seletiva de módulos é importante. Em muitas plataformas, como sistemas embarcados, o tamanho total do código disponível para uso na biblioteca pode ser grande em comparação com o espaço disponível para armazenar um executável no dispositivo. Sem a inclusão seletiva, seria mais difícil gerenciar os detalhes da construção de programas para essas plataformas.No entanto, ter uma cópia da mesma biblioteca em todos os programas em execução sobrecarrega o sistema que normalmente executa muitos processos. Com o tipo certo de sistema de memória virtual, as páginas de memória com conteúdo idêntico precisam existir apenas uma vez no sistema, mas podem ser usadas por vários processos. Isso cria um benefício para aumentar as chances de que as páginas que contêm código sejam provavelmente idênticas a alguma página em tantos outros processos em execução quanto possível. Mas, se os programas se vinculam estaticamente à biblioteca de tempo de execução, então cada um tem uma combinação diferente de funções, cada uma disposta no mapa de memória de processos em locais diferentes, e não há muitas páginas de código compartilháveis, a menos que seja um programa que por si só é executar em mais de um processo. Portanto, a ideia de um DLL ganhou outra vantagem importante.
Uma DLL para uma biblioteca contém todas as suas funções, prontas para serem usadas por qualquer programa cliente. Se muitos programas carregarem essa DLL, todos eles poderão compartilhar suas páginas de código. Todo mundo ganha. (Bem, até você atualizar uma DLL com a nova versão, mas isso não faz parte desta história. Google DLL Hell para esse lado da história.)
Portanto, a primeira grande escolha a fazer ao planejar um novo projeto é entre a vinculação dinâmica e estática. Com a vinculação estática, você tem menos arquivos para instalar e fica imune à atualização de uma DLL usada por terceiros. No entanto, seu programa é maior e não é tão bom cidadão do ecossistema do Windows. Com a vinculação dinâmica, você tem mais arquivos para instalar, pode ter problemas com terceiros atualizando uma DLL que você usa, mas geralmente você está sendo mais amigável com outros processos no sistema.
Uma grande vantagem de uma DLL é que ela pode ser carregada e usada sem recompilar ou mesmo religar o programa principal. Isso pode permitir que um provedor de biblioteca de terceiros (pense na Microsoft e no tempo de execução C, por exemplo) consertar um bug em sua biblioteca e distribuí-lo. Depois que um usuário final instala a DLL atualizada, ele imediatamente obtém o benefício dessa correção de bug em todos os programas que usam essa DLL. (A menos que quebre as coisas. Veja DLL Hell.)
A outra vantagem vem da distinção entre carregamento implícito e explícito. Se você fizer um esforço extra de carregamento explícito, a DLL pode nem mesmo ter existido quando o programa foi escrito e publicado. Isso permite mecanismos de extensão que podem descobrir e carregar plug-ins, por exemplo.
fonte
Esses arquivos de biblioteca de importação .LIB são usados na seguinte propriedade do projeto
Linker->Input->Additional Dependencies
, ao construir um monte de dlls que precisam de informações adicionais no momento do link que são fornecidas pelos arquivos .LIB da biblioteca de importação. No exemplo abaixo, para não obter erros do vinculador, preciso fazer referência às dll's A, B, C e D por meio de seus arquivos lib. (nota para o vinculador encontrar esses arquivos, você pode precisar incluir seu caminho de implantação, casoLinker->General->Additional Library Directories
contrário, você obterá um erro de compilação sobre ser incapaz de encontrar qualquer um dos arquivos lib fornecidos.)Se sua solução for construir todas as bibliotecas dinâmicas, você pode ter conseguido evitar essa especificação de dependência explícita, confiando nos sinalizadores de referência expostos na
Common Properties->Framework and References
caixa de diálogo. Esses sinalizadores parecem fazer a vinculação automaticamente em seu nome usando os arquivos * .lib.No entanto, isso é como diz as Propriedades Comuns , que não é uma configuração ou plataforma específica. Se você precisar oferecer suporte a um cenário de construção mista como em nosso aplicativo, tínhamos uma configuração de construção para renderizar uma construção estática e uma configuração especial que construía uma construção restrita de um subconjunto de montagens que foram implantadas como bibliotecas dinâmicas. Eu tinha usado os sinalizadores
Use Library Dependency Inputs
eLink Library Dependencies
definidos como verdadeiros em vários casos para fazer as coisas serem construídas e, posteriormente, percebendo como simplificar as coisas, mas ao apresentar meu código às construções estáticas, introduzi uma tonelada de avisos do linker e a construção foi incrivelmente lenta para as construções estáticas. Acabei introduzindo um monte desses tipos de avisos ...warning LNK4006: "bool __cdecl XXX::YYY() already defined in CoreLibrary.lib(JSource.obj); second definition ignored D.lib(JSource.obj)
E acabei usando a especificação manual de
Additional Dependencies
para satisfazer o vinculador para as compilações dinâmicas enquanto mantinha os construtores estáticos felizes por não usar uma propriedade comum que os tornava mais lentos. Quando implanto a compilação de subconjunto dinâmico, implanto apenas os arquivos dll, pois esses arquivos lib são usados apenas no tempo de link, não no tempo de execução.fonte
Existem três tipos de bibliotecas: bibliotecas estáticas, compartilhadas e carregadas dinamicamente.
As bibliotecas estáticas são vinculadas ao código na fase de vinculação, portanto, elas estão realmente no executável, ao contrário da biblioteca compartilhada, que tem apenas stubs (símbolos) para procurar no arquivo da biblioteca compartilhada, que é carregado em tempo de execução antes do a função principal é chamada.
As carregadas dinamicamente são muito parecidas com as bibliotecas compartilhadas, exceto que são carregadas quando e se a necessidade surgir pelo código que você escreveu.
fonte
LoadLibrary()
e as APIs relacionadas.Aqui estão alguns tópicos do MSDN relacionados para responder à minha pergunta:
Vinculando um executável a uma DLL
Vinculando implicitamente
Determinando qual método de vinculação usar
Construindo uma Biblioteca de Importação e Arquivo de Exportação
fonte