GNU/Linux >> Linux Esercitazione >  >> Linux

Come può una libreria condivisa (.so) chiamare una funzione implementata nel suo programma di caricamento?

Hai due opzioni, tra le quali puoi scegliere:

Opzione 1:esporta tutti i simboli dal tuo eseguibile. Questa è un'opzione semplice, proprio durante la creazione dell'eseguibile, aggiungi un flag -Wl,--export-dynamic . Ciò renderebbe tutte le funzioni disponibili per le chiamate alla libreria.

Opzione 2:creare un file di simboli di esportazione con un elenco di funzioni e utilizzare -Wl,--dynamic-list=exported.txt . Ciò richiede un po' di manutenzione, ma più accurato.

Per dimostrare:semplice libreria eseguibile e caricata dinamicamente.

#include <stdio.h>
#include <dlfcn.h>

void exported_callback() /*< Function we want to export */
{
    printf("Hello from callback!\n");
}

void unexported_callback() /*< Function we don't want to export */
{
    printf("Hello from unexported callback!\n");
}

typedef void (*lib_func)();

int call_library()
{
   void     *handle  = NULL;
   lib_func  func    = NULL;
   handle = dlopen("./libprog.so", RTLD_NOW | RTLD_GLOBAL);
   if (handle == NULL)
   {
       fprintf(stderr, "Unable to open lib: %s\n", dlerror());
       return -1;
   }
   func = dlsym(handle, "library_function");

   if (func == NULL) {
       fprintf(stderr, "Unable to get symbol\n");
      return -1;
   }

   func();
   return 0;
}

int main(int argc, const char *argv[])
{
    printf("Hello from main!\n");
    call_library();
    return 0;
}

Codice libreria (lib.c):

#include <stdio.h>
int exported_callback();

int library_function()
{
    printf("Hello from library!\n");
    exported_callback();
    /* unexported_callback(); */ /*< This one will not be exported in the second case */
    return 0;
}

Quindi, prima costruisci la libreria (questo passaggio non differisce):

 gcc -shared -fPIC lib.c -o libprog.so

Ora crea un eseguibile con tutti i simboli esportati:

 gcc -Wl,--export-dynamic main.c -o prog.exe -ldl

Eseguire l'esempio:

 $ ./prog.exe
 Hello from main!
 Hello from library!
 Hello from callback!

Simboli esportati:

 $ objdump -e prog.exe -T | grep callback
 00000000004009f4 g    DF .text  0000000000000015  Base        exported_callback
 0000000000400a09 g    DF .text  0000000000000015  Base        unexported_callback

Ora con l'elenco esportato (exported.txt ):

{
    extern "C"
    {
       exported_callback;
    };
};

Costruisci e controlla i simboli visibili:

$ gcc -Wl,--dynamic-list=./exported.txt main.c -o prog.exe -ldl
$ objdump -e prog.exe -T | grep callback
0000000000400774 g    DF .text  0000000000000015  Base        exported_callback

Dovrai creare una funzione di registro nel tuo .so in modo che l'eseguibile possa fornire un puntatore a funzione al tuo .so per un uso successivo.

In questo modo:

void in_main_func () {
// this is the function that need to be called from a .so
}

void (*register_function)(void(*)());
void *handle = dlopen("libmylib.so");

register_function = dlsym(handle, "register_function");

register_function(in_main_func);

la register_function deve memorizzare il puntatore di funzione in una variabile nel .so dove l'altra funzione nel .so può trovarlo.

Il tuo mylib.c dovrebbe assomigliare a questo:

void (*callback)() = NULL;

void register_function( void (*in_main_func)())
{
    callback = in_main_func();
}

void function_needing_callback() 
{
     callback();
}

  1. Inserisci il prototipo della tua funzione principale in un file .h e includilo sia nel codice della libreria principale che in quello dinamico.

  2. Con GCC, compila semplicemente il tuo programma principale con -rdynamic bandiera.

  3. Una volta caricata, la tua libreria sarà in grado di chiamare la funzione dal programma principale.

Una piccola ulteriore spiegazione è che una volta compilata, la tua libreria dinamica avrà un simbolo indefinito per la funzione che si trova nel codice principale. Dopo che l'app principale ha caricato la libreria, il simbolo verrà risolto dalla tabella dei simboli del programma principale. Ho usato lo schema sopra numerose volte e funziona a meraviglia.


Linux
  1. Linux:come garantire che una libreria condivisa abbia le sue pagine di memoria condivise da diversi processi?

  2. Perché /dev/null è un file? Perché la sua funzione non è implementata come un semplice programma?

  3. Come chiamare una funzione Bash nello script Bash all'interno di Awk?

  4. Come creare e chiamare funzioni in Bash

  5. Come chiamare la funzione C in C++, la funzione C++ in C (mix di C e C++)

Introduzione alle librerie condivise Linux (come creare librerie condivise)

Collegamento statico della funzione di libreria condivisa in gcc

Come implementare un timeout nella chiamata alla funzione di lettura?

exit() può non riuscire a terminare il processo?

Come configurare googleTest come libreria condivisa su Linux

Errore durante il caricamento della libreria condivisa (glew)