Como o linux resolve símbolos não resolvidos para bibliotecas que são usadas como extensões

9

Existe um mistério que estou tentando entender:

Eu fiz um aplicativo que pode ser estendido com bibliotecas dinâmicas que contêm algum código que, no entanto, precisa acessar algumas funções que são definidas no próprio aplicativo. Para deixar claro:

Eu tenho aplicativo, vamos chamá-lo de APP, então eu tenho extensão EXT. O APP é estendido com alguns recursos que são implementados no EXT, mas o EXT precisa chamar algumas funções que são definidas no APP para "enganchar" a ele (por exemplo, registrar novos itens no layout do aplicativo, etc.). No MS Windows eu não seria capaz de compilar EXT por causa de símbolos não resolvidos - isso faz sentido - como eu chamaria funções que estão no APP sem realmente ter nada para vinculá-los, então eu criei uma biblioteca dll de APP que é basicamente APP acaba de construir como uma DLL com todas essas funções que eu preciso acessar exportado usando __declspec (dllexport) (vamos chamá-lo apenas LIB), então funciona assim:

APP carrega EXT e EXT chamando as funções do APP por meio do LIB. É uma solução desagradável em algum momento, mas não consegui pensar em nada melhor. E o que é mais importante - funciona perfeito.

Agora o que me deixa louco é como tudo isso funciona no Linux sem ter que criar o LIB? Essa coisa do Windows é ruim, mas faz todo o sentido, mas no Linux eu posso construir o EXT mesmo sem ter que compilar o APP ou o LIB, de alguma forma ignorar esses símbolos não resolvidos e vinculá-los de qualquer maneira. Toda a biblioteca os contém, posso verificar isso chamando:

ld: warning: cannot find entry symbol _start; not setting start address
libhuggle_md.so: undefined reference to 'Huggle::Query::NetworkManager'
libhuggle_md.so: undefined reference to 'Huggle::Syslog::HuggleLogs'
libhuggle_md.so: undefined reference to 'Huggle::Core::HuggleCore'
libhuggle_md.so: undefined reference to 'Huggle::QueryPool::HugglePool'
libhuggle_md.so: undefined reference to 'Huggle::Localizations::HuggleLocalizations'
libhuggle_md.so: undefined reference to 'Huggle::Configuration::HuggleConfiguration'
libhuggle_md.so: undefined reference to 'Huggle::GC::gc'
libhuggle_md.so: undefined reference to 'Huggle::WikiUser::WikiUser(QString)'
libhuggle_md.so: undefined reference to 'Huggle::WikiUtil::MessageUser(Huggle::WikiUser*, QString, QString, QString, bool, Huggle::Query*, bool, bool, bool, QString, bool, bool)'

Assim, você pode ver que o EXT está se referindo a algumas funções do APP, mas nunca foi vinculado a nenhuma biblioteca que as implementaria. Eles são apenas não resolvidos.

Quando eu carrego o EXT no APP, alguma mágica dentro do kernel de alguma forma acontece e todos funcionam magicamente. Por que o APP no linux não precisa de LIB enquanto o windows precisa dele? Por que é possível ligar algo no linux com símbolos externos não resolvidos? Como sabe a que símbolos estou me referindo? Ele os encontra no APP e os resolve em tempo de execução?

Para quem estiver interessado aqui é uma fonte completa: link se você clonar isso no linux e executar ./configure --extension e então faça você ver que primeiro constrói uma das extensões (mesmo que não haja nada com o qual linkar), então ele cria o aplicativo, e se você executar make install e então tentar executá-lo, verá que ele carrega apenas bem e usando alguma mágica, corrija os símbolos não resolvidos dentro da biblioteca durante o tempo de execução. Como é que isso funciona? E por que não funciona no Windows?

    
por Petr 10.12.2014 в 14:20
fonte

1 resposta

2

Acho que está relacionado ao formato ELF usado para executáveis e bibliotecas no linux (e muitos outros * NIXes) e vinculador dinâmico .

Quando o programa vinculado dinamicamente é iniciado (o processo é criado), o vinculador dinâmico prepara o espaço de endereço desse processo. As bibliotecas Linux são compiladas usando PIC (Position Independent Code), para que possam ser colocadas em qualquer lugar no espaço de endereço do processo. Links entre funções de diferentes módulos em tempo de execução são resolvidos através das tabelas PLT (Pesquisa de Procedimentos) e GOT (Global Offset). PLT (somente leitura, seção executável) contém instruções de salto indireto para endereços na tabela GOT (leitura-gravação, seção não executável). A primeira chamada para a função via PLT leva a uma função de vinculador de tempo de execução, que atualiza a entrada GOT (e salta para o endereço real). Chamadas subseqüentes para a mesma função saltam diretamente para ela.

Pelo que entendi, o compilador tem informações suficientes (protótipos de funções e outros dados de arquivos de cabeçalho) para construir a biblioteca corretamente. Mas, para construir um executável, você terá que fornecer todas as bibliotecas necessárias (ainda que em tempo de execução, você pode alterar as bibliotecas usadas desde que elas forneçam todas as funções usadas).

Eu assumo que a vinculação dinâmica funciona assim, como em outros SOs semelhantes ao UNIX, que usam o formato ELF.

Eu não estou muito familiarizado com o formato executável do Windows, por isso não posso comentar por que um truque semelhante não funciona lá.

    
por kestasx 16.12.2014 / 02:30
fonte