GNU/Linux >> Znalost Linux >  >> Linux

Jak může sdílená knihovna (.so) volat funkci, která je implementována v jejím načítacím programu?

Máte dvě možnosti, ze kterých si můžete vybrat:

Možnost 1:exportujte všechny symboly ze spustitelného souboru. Toto je jednoduchá možnost, stačí při sestavování spustitelného souboru přidat příznak -Wl,--export-dynamic . Tím by byly všechny funkce dostupné pro volání knihovny.

Možnost 2:vytvořte soubor se symbolem exportu se seznamem funkcí a použijte -Wl,--dynamic-list=exported.txt . To vyžaduje určitou údržbu, ale je přesnější.

Pro demonstraci:jednoduchá spustitelná a dynamicky načítaná knihovna.

#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;
}

Kód knihovny (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;
}

Nejprve tedy vytvořte knihovnu (tento krok se neliší):

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

Nyní sestavení spustitelného souboru se všemi exportovanými symboly:

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

Spustit příklad:

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

Exportované symboly:

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

Nyní s exportovaným seznamem (exported.txt ):

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

Vytvořte a zkontrolujte viditelné symboly:

$ 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

Budete muset vytvořit funkci registru ve vašem .so, aby spustitelný soubor mohl poskytnout ukazatel funkce na váš .so pro jeho pozdější použití.

Takhle:

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);

funkce register musí uložit ukazatel funkce do proměnné v .so, kde jej může najít jiná funkce v .so.

Váš mylib.c by musel vypadat nějak takto:

void (*callback)() = NULL;

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

void function_needing_callback() 
{
     callback();
}

  1. Vložte prototyp své hlavní funkce do souboru .h a zahrňte jej do kódu hlavní i dynamické knihovny.

  2. S GCC jednoduše zkompilujte svůj hlavní program s -rdynamic vlajka.

  3. Po načtení bude vaše knihovna schopna volat funkci z hlavního programu.

Trochu další vysvětlení je, že po zkompilování bude mít vaše dynamická knihovna nedefinovaný symbol pro funkci, která je v hlavním kódu. Jakmile vaše hlavní aplikace načte knihovnu, bude symbol vyřešen tabulkou symbolů hlavního programu. Výše uvedený vzor jsem použil mnohokrát a funguje jako kouzlo.


Linux
  1. Úvod do sdílených knihoven Linuxu (Jak vytvořit sdílené knihovny)

  2. Jak volat funkci C v C++, funkci C++ v C (Mix C a C++)

  3. Jak inicializovat sdílenou knihovnu v Linuxu

  1. Jak udělat verzování sdílené knihovny v Linuxu?

  2. Může exit() selhat při ukončení procesu?

  3. Jak lze v makefile použít funkci eval?

  1. Linux – Jak zajistit, aby sdílená knihovna měla své paměťové stránky sdílené několika procesy?

  2. Načítání sdílených knihoven a využití RAM?

  3. Proč je /dev/null soubor? Proč není jeho funkce implementována jako jednoduchý program?