GNU/Linux >> Tutoriels Linux >  >> Linux

Obtenir des noms de fonctions dans une bibliothèque partagée par programme

MISE À JOUR | TL;DR :

En fait, j'ai trouvé un chemin plus court :

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

ANCIEN

Je crois que l'auteur n'en a plus besoin mais peut-être que quelqu'un a besoin de code réel et le voici (basé sur la réponse précédente)

Tout d'abord, nous avons besoin d'un rappel pour 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;
}

Ensuite, nous appelons 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;
}

Si vous avez besoin de stocker ces noms quelque part, vous pouvez passer un pointeur vers votre conteneur, le restaurer avec cast et y écrire.

Pour mon exemple de bibliothèque :

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

Imprime ceci :

Library is initialized

_ITM_deregisterTMCloneTable
puts
__gmon_start__
_ITM_registerTMCloneTable
__cxa_finalize
_Z11initLibraryv
make_number
_Z14cleanUpLibraryv
Library is exited

Il n'y a pas de fonction libc pour faire cela. Cependant, vous pouvez en écrire un vous-même (ou copier/coller le code depuis un outil comme readelf).

Sous Linux, dlopen() retourne l'adresse d'un link_map structure, qui a un membre nommé l_addr qui pointe vers l'adresse de base de l'objet partagé chargé (en supposant que votre système ne randomise pas le placement de la bibliothèque partagée et que votre bibliothèque n'a pas été préliée).

Sous Linux, un moyen de trouver l'adresse de base (l'adresse de Elf*_Ehdr ) est d'utiliser dl_iterate_phdr() après dlopen() la bibliothèque.

Avec l'en-tête ELF, vous devriez pouvoir parcourir une liste de symboles exportés (la table de symboles dynamiques), en localisant d'abord le Elf*_Phdr de type PT_DYNAMIC , puis en localisant DT_SYMTAB , DT_STRTAB entrées et en itérant sur tous les symboles de la table de symboles dynamiques. Utilisez /usr/include/elf.h pour vous guider.

De plus, vous pouvez utiliser libelf, que je ne connais pas très bien personnellement.

Cependant, notez que vous obtiendrez une liste de fonctions définies, mais vous ne saurez pas comment les appeler.


Linux
  1. Introduction aux bibliothèques partagées Linux (comment créer des bibliothèques partagées)

  2. Bibliothèque partagée dynamique C++ sous Linux

  3. Lien statique de la fonction de bibliothèque partagée dans gcc

  4. Obtenir par programmation le pid parent d'un autre processus ?

  5. Obtenir une liste de noms de fonctions dans un script shell

Démarrer avec GNUPlot

Qu'est-ce que les appels système Linux et les fonctions de bibliothèque ?

Comment faire le versioning d'une bibliothèque partagée sous Linux ?

Impossible de trouver l'erreur de bibliothèque libcrypto

Comment configurer googleTest en tant que bibliothèque partagée sous Linux

Comment vérifier si une bibliothèque partagée est installée ?