Qual chamada de sistema é usada para carregar bibliotecas no Linux?

22

Em strace outputs, os caminhos para as bibliotecas que os executáveis chamam estão em chamadas para open() . Esta é a chamada do sistema usada pelos executáveis que estão dinamicamente vinculados? E quanto a dlopen() ? open() não é uma ligação que eu teria imaginado que teria um papel na execução de programas.

    
por Melab 31.08.2015 / 08:56

5 respostas

32

dlopen não é uma chamada de sistema, é uma função de biblioteca na biblioteca libdl . Apenas as chamadas do sistema aparecem em strace .

No Linux e em muitas outras plataformas (especialmente aquelas que usam o formato ELF para executáveis), dlopen é implementado abrindo a biblioteca de destino com open() e mapeando-a na memória com mmap() . mmap() é realmente a parte crítica aqui, é o que incorpora a biblioteca no espaço de endereço do processo, para que a CPU possa executar seu código. Mas você precisa open() do arquivo antes de poder mmap() !

    
por 31.08.2015 / 09:42
5

O dlopen não tem nada a ver com bibliotecas compartilhadas como você pensa delas. Existem dois métodos para carregar um objeto compartilhado:

  1. Você diz ao vinculador em tempo de compilação (ld, embora geralmente seja chamado por meio do compilador) que deseja usar funções de uma biblioteca compartilhada específica. Com essa abordagem, você deve saber em que nome estará a biblioteca quando o vinculador de tempo de compilação for executado, mas poderá chamar as funções da biblioteca como se estivessem vinculadas estaticamente ao seu programa. Quando o aplicativo é executado, o vinculador dinâmico, em tempo de execução (ld.so), será chamado logo antes da função main ser chamada e configurará o espaço de processo do aplicativo para que o aplicativo localize as funções da biblioteca. Isso envolve open() ing o lubrary e, em seguida, mmap() it, seguido pela configuração de algumas tabelas de consulta.
  2. Você diz ao vinculador em tempo de compilação que deseja vincular com libdl , a partir do qual você (usando o primeiro método) pode chamar as funções dlopen() e dlsym() . Com dlopen, você obtém um identificador para a biblioteca, que você pode usar com dlsym para receber um ponteiro de função para uma função específica. Este método é muito mais complicado para o programador do que o primeiro método (desde que você tenha que fazer a configuração manualmente, ao invés de fazer o linker fazer isso automaticamente para você), e também é mais frágil (já que você não obtém a compilação) As verificações de tempo que você está chamando funcionam com os tipos de argumentos corretos conforme você obtém no primeiro método), mas a vantagem é que você pode decidir qual objeto compartilhado carregar em tempo de execução (ou até mesmo se deseja carregá-lo), tornando isso significa uma interface para a funcionalidade do tipo de plugin. Finalmente, a interface dlopen também é menos portável do que a outra maneira, já que sua mecânica depende da implementação exata do vinculador dinâmico (daí o libltdl da libtool, que tenta abstrair essas diferenças).
por 01.09.2015 / 02:14
4

Hoje, a maioria dos sistemas operacionais usa o método para bibliotecas compartilhadas introduzido no final de 1987 pelo SunOS-4.0. Este método é baseado no mapeamento de memória via mmap ().

Dado que no início dos anos 90, a Sun até doou o antigo código baseado em a.out (Solaris na época já era ELF) para o pessoal do FreeBSD e que este código foi posteriormente entregue a muitos outros sistemas ( incluindo Linux), você pode entender porque não há grande diferença entre as plataformas.

    
por 31.08.2015 / 11:24
3
A análise de

ltrace -S de um exemplo mínimo mostra que mmap é usado em glibc 2.23

No glibc 2.23, Ubuntu 16.04, executando latrace -S em um programa mínimo que usa dlopen com:

ltrace -S ./dlopen.out

mostra:

dlopen("libcirosantilli_ab.so", 1 <unfinished ...>
SYS_open("./x86_64/libcirosantilli_ab.so", 524288, 06267650550)      = -2
SYS_open("./libcirosantilli_ab.so", 524288, 06267650550)             = 3
SYS_read(3, "7ELF
ltrace -S ./dlopen.out
2
dlopen("libcirosantilli_ab.so", 1 <unfinished ...>
SYS_open("./x86_64/libcirosantilli_ab.so", 524288, 06267650550)      = -2
SYS_open("./libcirosantilli_ab.so", 524288, 06267650550)             = 3
SYS_read(3, "7ELF%pre%2%pre%1%pre%1", 832)                              = 832
SYS_brk(0)                                                           = 0x244c000
SYS_brk(0x246d000)                                                   = 0x246d000
SYS_fstat(3, 0x7fff42f9ce30)                                         = 0
SYS_getcwd("/home/ciro/bak/git/cpp-cheat"..., 128)                   = 54
SYS_mmap(0, 0x201028, 5, 2050)                                       = 0x7f1c323fe000
SYS_mprotect(0x7f1c323ff000, 2093056, 0)                             = 0
SYS_mmap(0x7f1c325fe000, 8192, 3, 2066)                              = 0x7f1c325fe000
SYS_close(3)                                                         = 0
SYS_mprotect(0x7f1c325fe000, 4096, 1)                                = 0
1%pre%1", 832) = 832 SYS_brk(0) = 0x244c000 SYS_brk(0x246d000) = 0x246d000 SYS_fstat(3, 0x7fff42f9ce30) = 0 SYS_getcwd("/home/ciro/bak/git/cpp-cheat"..., 128) = 54 SYS_mmap(0, 0x201028, 5, 2050) = 0x7f1c323fe000 SYS_mprotect(0x7f1c323ff000, 2093056, 0) = 0 SYS_mmap(0x7f1c325fe000, 8192, 3, 2066) = 0x7f1c325fe000 SYS_close(3) = 0 SYS_mprotect(0x7f1c325fe000, 4096, 1) = 0

para que possamos ver imediatamente que dlopen chama open + mmap .

A incrível ferramenta ltrace rastreia as chamadas da biblioteca e do sistema e, portanto, é perfeita para examinar o que está acontecendo nesse caso.

Uma análise mais detalhada mostra que open retorna o descritor de arquivo 3 (o próximo é livre depois de stdin, out e err).

read , em seguida, usa esse descritor de arquivo, mas TODO por que os argumentos de mmap estão limitados a quatro, e não podemos ver qual fd foi usado lá, já que é o quinto argumento . strace confirma como esperado que 3 é o único, e a ordem do universo é restaurada.

Almas corajosas também podem se aventurar no código da glibc, mas não consegui encontrar o mmap depois de um grep rápido e sou preguiçoso.

Testado com este exemplo mínimo com clichê de construção no GitHub .

    
por 15.08.2018 / 11:32
2

strace relata as chamadas do sistema (ou seja, funções implementadas diretamente pelo kernel). Bibliotecas dinâmicas não são uma função do kernel; dlopen é parte da biblioteca C, não do kernel. A implementação de dlopen chamará open (que é uma chamada do sistema) para abrir o arquivo da biblioteca para que possa ser lido.

    
por 31.08.2015 / 09:42