dlopen carrega dinamicamente bibliotecas compartilhadas

Link do texto original: http://www.ccccxy.top/coding/archives/2020/10/01/dlopen_load_shared_library_11/Bem-vindos
os grandes deuses para comentar, orientar e corrigir

1. Instruções básicas

Explicit Run-time Linking (Explicit Run-time Linking), às vezes chamado de carregamento em tempo de execução , o próprio programa controla o carregamento do módulo especificado em tempo de execução, ou seja, a biblioteca dinâmica, e pode descarregar o módulo quando não for necessário.

Em comparação com a vinculação dinâmica comum, a vinculação de tempo de execução explícita permite que o programa carregue o módulo correspondente quando precisar usar um determinado plug-in ou driver, em vez de carregar todos os módulos de plug-in e driver no início O carregamento nunca reduziu o tempo de inicialização do programa e uso de memória. E o programa pode recarregar dinamicamente o módulo durante o processo de execução, de modo a realizar a adição, exclusão e atualização de módulos sem reiniciar o programa.

Os links dinâmicos comuns são carregados e vinculados pelo vinculador dinâmico antes do início do programa, e esse processo é concluído automaticamente (por exemplo: ); no entanto, o gcc -o demo demo.c -ltestlink de execução explícito da biblioteca dinâmica é fornecido por meio de uma série de API de vinculadores dinâmicos (dlopen, dlsym , dlerror, dlclose), a intervenção manual é feita.

2. Introdução à interface

O carregamento em tempo de execução é concluído por quatro funções: abrir a biblioteca dinâmica (dlopen), encontrar símbolos (dlsym), tratamento de erros (dlerror) e descarregar a biblioteca dinâmica (dlclose). Essas quatro APIs são implementadas em /lib/libdl.so.2, e as declarações e constantes relacionadas são definidas nos arquivos de cabeçalho padrão do sistema <dlfcn.h>.

  1. dlopen: Abra uma biblioteca dinâmica e carregue-a no espaço de endereço do processo para concluir o processo de inicialização

    1. Protótipo de função:void * dlopen(const char *filename, int flag);

    2. Descrição do parâmetro:

    3. const char *filename: o caminho da biblioteca dinâmica carregada

      O caminho pode ser um caminho absoluto. Se for usado um caminho absoluto, a função dlopen tentará abrir diretamente a biblioteca dinâmica correspondente ao caminho; também pode ser usado um caminho relativo. Nesse caso, dlopen tentará encontrar o caminho dinâmico biblioteca em uma determinada ordem. Se encontrado, abra-o e retorne o identificador correspondente.

      A seguir está a ordem na qual a função dlopen pesquisa ao usar caminhos relativos:

      (1) (Somente ELF) Se o arquivo executável do programa contiver o rótulo DT_RPATH e não contiver o rótulo DT_RUNPATH, procure o diretório especificado no DT_RPATH

      (2) Encontre uma série de diretórios especificados pela variável de ambiente LD_LIBRARY_PATH

      (3) (somente ELF) Se o rótulo DT_RUNPATH estiver incluído no arquivo executável, procure o diretório especificado em DT_RUNPATH

      (4) Encontre o caminho da biblioteca compartilhada especificado em /etc/ld.so.cache

      (5)/lib、/usr/lib

      (1 e 3, do Linux Programmer's Manual, algumas pessoas dizem que a descrição aqui está errada, e a correção ainda não está clara)

      filename também pode ser definido como 0, então dlopen retornará o identificador da tabela de símbolos global, ou seja, qualquer símbolo na tabela de símbolos global pode ser encontrado em tempo de execução e pode ser executado. A tabela de símbolos globais inclui: o próprio arquivo executável do programa, todos os módulos compartilhados carregados no processo pela biblioteca de vínculo dinâmico e todos os símbolos nos módulos abertos por dlopen() e usando o modo RTLD_GLOBAL em tempo de execução.

    4. sinalizador int: O método de análise do símbolo de função, existem os seguintes tipos e alguns métodos podem ser usados ​​juntos por meio da operação "ou"

      • RTLD_LAZY: Use ligação preguiçosa.

        A vinculação (pesquisa de símbolo, realocação, etc.) é executada quando a função é chamada pela primeira vez e nenhuma vinculação é executada se não for usada. Portanto, quando a função dlopen() retorna, as chamadas de função chamadas entre os módulos não são vinculadas, mas o vinculador dinâmico é responsável pela vinculação quando necessário, o que pode melhorar a velocidade de carregamento do módulo.

      • RTLD_NOW:

        Quando o módulo é carregado, a vinculação de todas as funções é concluída. Se a vinculação de qualquer referência de símbolo indefinido não puder ser concluída, dlopen() retornará um erro.

      • RTLD_GLOBAL:

        Os símbolos globais dos módulos carregados são mesclados na tabela de símbolos globais do processo, tornando-os disponíveis para os módulos carregados posteriormente.

      • RTLD_LOCAL:

        Ao contrário do efeito de RTLD_GLOBAL, os símbolos definidos na biblioteca dinâmica não podem ser realocados pela biblioteca dinâmica carregada posteriormente, ou seja, a biblioteca dinâmica carregada posteriormente não pode utilizar os símbolos da biblioteca.

        Observação: RTLD_LAZY e RTLD_NOW devem usar um; RTLD_GLOBAL e RTLD_LOCLA podem ser usados ​​com qualquer um de RTLD_LAZY e RTLD_NOW, geralmente a operação é "ou", o padrão de RTLD_LOCLA e RTLD_GLOBAL é RTLD_LOCLA

    5. Precauções:

      O valor de retorno de dlopen é o identificador do módulo carregado, que é necessário para o uso subseqüente de dlsym() e dlclose(). Retorna NULL se o carregamento falhar.

      Se o módulo já tiver sido carregado, ele retornará o mesmo identificador da última vez que foi carregado.

      Se houver uma dependência entre os módulos carregados, essa dependência requer intervenção manual. Por exemplo, se o módulo A depende do módulo B, então B deve ser carregado primeiro e, em seguida, A deve ser carregado.

  2. dlsym: O núcleo carregado em tempo de execução, através do qual os símbolos necessários são encontrados

    1. Protótipo de função:void * dlsym(void *handle, char *symbol);

    2. Introdução do parâmetro:

      1. void *handle: o identificador da biblioteca dinâmica retornado por dlopen();

      2. char *symbol: O nome do símbolo a ser pesquisado, uma string C terminando com '\0'.

      3. Instruções de uso:

      Se dlsym() encontrar um símbolo correspondente, ele retornará o valor desse símbolo; se não for encontrado, retornará NULL.

      O valor retornado por dlsym() tem significados diferentes para diferentes tipos de símbolos. Se o símbolo procurado for uma função, será retornado o endereço da função; se for uma variável, será retornado o endereço da variável; se for uma constante, será retornado o valor da constante, e se o símbolo Se o valor da constante for NULL ou 0, use a função dlerror() para julgar, se o símbolo for encontrado, dlerror() retornará NULL; se não for encontrado, dlerror() retornará a mensagem de erro correspondente .

      1. Precauções:

      Há um problema com a precedência de símbolo ao pesquisar símbolos de biblioteca dinâmica. O método de prioridade adotado pela vinculação dinâmica convencional é a sequência de carregamento , ou seja, o símbolo carregado primeiro tem precedência. No entanto, dlsym() tem duas situações para a prioridade de pesquisa de símbolos:

      (1) Se a pesquisa de símbolos for realizada na tabela de símbolos globais, ou seja, o parâmetro de nome de arquivo de dlopen() for NULL, como a tabela de símbolos globais usa a sequência de carregamento, dlsym() também usa a sequência de carregamento ;

      (2) Se uma pesquisa de símbolo for realizada em um objeto compartilhado aberto por dlopen(), então uma prioridade chamada sequência de dependência será usada . A sequência de dependência leva o objeto compartilhado aberto por dlopen() como o nó raiz e executa a travessia em largura em todos os objetos compartilhados dos quais depende até que o símbolo seja encontrado.

  3. dlerror

    1. Protótipo de função:char * dlerror (void);

    2. Instruções de uso:

      Após cada chamada para dlopen(), dlsym() ou dlclose(), você pode chamar a função dlerror() para determinar se a última chamada foi bem-sucedida. Se for bem-sucedido, dlerror() retorna NULL; se falhar, retorna a mensagem de erro correspondente.

  4. dlclose

    1. Protótipo de função:int dlclose (void * handle);

    2. Instruções de uso:

      A função de dlclose() é oposta à de dlopen(), e sua função é descarregar um módulo que já foi carregado. O sistema mantém um contador de referência de carga e toda vez que um módulo é carregado com dlopen(), o contador correspondente é incrementado em 1; toda vez que um módulo é descarregado com dlclose(), o contador correspondente é decrementado em 1. Somente quando o contador é reduzido a 0, o módulo é realmente descarregado. O processo de descarregamento é oposto ao processo de carregamento. Primeiro execute o código de segmento ".finit", depois remova os símbolos correspondentes da tabela de símbolos, cancele o relacionamento de mapeamento entre o espaço do processo e o módulo e, em seguida, feche o arquivo do módulo.

apêndice

A seguir, uma demonstração simples da interface, como dlopen:

//"a.h"
//libadd.so头文件
#ifndef __A_H__
#define __A_H__
#ifdef __cplusplus
extern "C"{
#endif

int add(int x, int y);
  
#ifdef __cplusplus
}
#endif
#endif // !__A_H__
//a.c
//libadd.so的实现,简单实现两个int类型整数的相加
#include "a.h"

int add(int x, int y)
{
    
    
    return x+y;
}

//cmd: gcc -fpic -shared -o libadd.so -g a.c
//main.c
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>

typedef int(*func_add)(int, int);

//相对路径
const char* relativePath_a = "./libadd.so";
//绝对路径
const char* absPath_a = "/home/44124/test/libadd.so";

int main(int argc, char** argv)
{
    
    
    void* handle = dlopen(absPath_a, RTLD_LAZY);

    if (!handle){
    
    
        fprintf(stderr, "[%s](%d) dlopen get error: %s\n", __FILE__, __LINE__, dlerror());
        exit(EXIT_FAILURE);
    }

    do{
    
    
        func_add add = (func_add)dlsym(handle, "add");
        printf("1 add 2 is [%d]\n", add(1, 2));
    }while(0);

    dlclose(handle);

    return 0;
}

//cmd: gcc -o demo -g main.c -ldl; ./demo
//output: 1 add 2 is [3]

Acho que você gosta

Origin blog.csdn.net/qq_38894585/article/details/109310085
Recomendado
Clasificación