GNU/Linux >> Linux Esercitazione >  >> Linux

Ottenere i nomi delle funzioni in una libreria condivisa a livello di codice

AGGIORNAMENTO | TL;DR :

In realtà ho trovato un modo più breve:

    auto library = dlopen("/path/to/lib.so", RTLD_LAZY | RTLD_GLOBAL);
    const char * libname = "lib.so";
    struct link_map * map = nullptr;
    dlinfo(library, RTLD_DI_LINKMAP, &map);

    Elf64_Sym * symtab = nullptr;
    char * strtab = nullptr;
    int symentries = 0;
    for (auto section = map->l_ld; section->d_tag != DT_NULL; ++section)
    {
        if (section->d_tag == DT_SYMTAB)
        {
            symtab = (Elf64_Sym *)section->d_un.d_ptr;
        }
        if (section->d_tag == DT_STRTAB)
        {
            strtab = (char*)section->d_un.d_ptr;
        }
        if (section->d_tag == DT_SYMENT)
        {
            symentries = section->d_un.d_val;
        }
    }
    int size = strtab - (char *)symtab;
    for (int k = 0; k < size / symentries; ++k)
    {
        auto sym = &symtab[k];
        // If sym is function
        if (ELF64_ST_TYPE(symtab[k].st_info) == STT_FUNC)
        {
            //str is name of each symbol
            auto str = &strtab[sym->st_name];
            printf("%s\n", str);
        }
    }

VECCHIO

Credo che l'autore non ne abbia più bisogno, ma forse qualcuno ha bisogno del codice effettivo ed eccolo qui (basato sulla risposta precedente)

Innanzitutto, abbiamo bisogno di una richiamata per dl_iterate_phdr() :

static int callback(struct dl_phdr_info *info, size_t size, void *data)
{
    // data is copy of 2nd arg in dl_iterate_phdr
    // you can use it for your lib name as I did
    const char * libname = (const char *)data;

    // if current elf's name contains your lib
    if (strstr(info->dlpi_name, libname))
    {

        printf("loaded %s from: %s\n", libname, info->dlpi_name);

        for (int j = 0; j < info->dlpi_phnum; j++)
        {
            // we need to save dyanmic section since it contains symbolic table
            if (info->dlpi_phdr[j].p_type == PT_DYNAMIC)
            {
                Elf64_Sym * symtab = nullptr;
                char * strtab = nullptr;
                int symentries = 0;
                auto dyn = (Elf64_Dyn *)(info->dlpi_addr + info->dlpi_phdr[j].p_vaddr);
                for (int k = 0; k < info->dlpi_phdr[j].p_memsz / sizeof(Elf64_Dyn); ++k)
                {
                    if (dyn[k].d_tag == DT_SYMTAB)
                    {
                        symtab = (Elf64_Sym *)dyn[k].d_un.d_ptr;
                    }
                    if (dyn[k].d_tag == DT_STRTAB)
                    {
                        strtab = (char*)dyn[k].d_un.d_ptr;
                    }
                    if (dyn[k].d_tag == DT_SYMENT)
                    {
                        symentries = dyn[k].d_un.d_val;
                    }
                }
                int size = strtab - (char *)symtab;
                // for each string in table
                for (int k = 0; k < size / symentries; ++k)
                {
                    auto sym = &symtab[k];
                    auto str = &strtab[sym->st_name];
                    printf("%s\n", str);
                }
                break;
            }
        }
    }
    return 0;
}

Successivamente, chiamiamo dl_iterate_phdr() :

int main()
{
    auto library = dlopen("/path/to/library.so", RTLD_LAZY | RTLD_GLOBAL);
    const char * libname = "library.so";
    dl_iterate_phdr(callback, (void*)libname);
    return 0;
}

Se hai bisogno di memorizzare quei nomi da qualche parte, puoi passare un puntatore al tuo contenitore, ripristinarlo con cast e scrivere lì.

Per la mia libreria di esempio:

#include "simple_lib.h"

#include <cstdio>

void __attribute__ ((constructor)) initLibrary(void)
{
    printf("Library is initialized\n");
}
void __attribute__ ((destructor)) cleanUpLibrary(void)
{

    printf("Library is exited\n");
}

void make_number()
{
    printf("1\n");
}

Stampa questo:

Library is initialized

_ITM_deregisterTMCloneTable
puts
__gmon_start__
_ITM_registerTMCloneTable
__cxa_finalize
_Z11initLibraryv
make_number
_Z14cleanUpLibraryv
Library is exited

Non esiste una funzione libc per farlo. Tuttavia, puoi scriverne uno tu stesso (o copiare/incollare il codice da uno strumento come readelf).

Su Linux, dlopen() restituisce l'indirizzo di un link_map struttura, che ha un membro chiamato l_addr che punta all'indirizzo di base dell'oggetto condiviso caricato (supponendo che il tuo sistema non randomizzi il posizionamento della libreria condivisa e che la tua libreria non sia stata precollegata).

Su Linux, un modo per trovare l'indirizzo di base (l'indirizzo di Elf*_Ehdr ) è usare dl_iterate_phdr() dopo dlopen() ing la libreria.

Avendo l'intestazione ELF, dovresti essere in grado di iterare su un elenco di simboli esportati (la tabella dei simboli dinamici), individuando prima Elf*_Phdr di tipo PT_DYNAMIC , quindi individuando DT_SYMTAB , DT_STRTAB voci e l'iterazione su tutti i simboli nella tabella dei simboli dinamici. Usa /usr/include/elf.h per guidarti.

Inoltre, potresti usare libelf, che personalmente non conosco molto bene.

Tuttavia, tieni presente che otterrai un elenco di funzioni definite, ma non avrai idea di come chiamarle.


Linux
  1. La libreria Libsdl condivisa non si apre?

  2. Libreria condivisa dinamica C++ su Linux

  3. Convertire una libreria statica in una libreria condivisa?

  4. Collegamento statico della funzione di libreria condivisa in gcc

  5. Ottieni in modo programmatico il pid genitore di un altro processo?

Inizia con GNUPlot

Introduzione alle librerie condivise Linux (come creare librerie condivise)

Che cosa sono le chiamate di sistema Linux e le funzioni di libreria?

Come posso visualizzare l'elenco delle funzioni che una libreria condivisa di Linux sta esportando?

Come posso collegarmi a una versione precedente di una libreria condivisa

Come verificare se è installata una libreria condivisa?