Wie listet man spontan alle Funktionen/Symbole auf, die in C-Code auf einer Linux-Architektur verfügbar sind?

Lesezeit: 10 Minuten

Benutzer-Avatar
0x90

Davon ausgehen main.c verwendet Symbole aus gemeinsam genutzten Bibliotheken und lokalen Funktionen, die in deklariert sind main.c.

Gibt es eine schöne und elegante Möglichkeit, zur Laufzeit eine Liste aller verfügbaren Funktionsnamen und Symbole zu drucken?

Es sollte möglich sein, da die Daten in die geladen werden .code Segment.

  • nicht C Bibliotheksfunktionen, aber API aus dem Import/Export-Abschnitt ist möglich. Wo C-Funktion möglich ist, wenn sie als Labels im Code-Abschnitt verwendet werden

    – Grijesh Chauhan

    3. April 2013 um 5:04 Uhr


Benutzer-Avatar
Kanalpiroge

Da ich das gleiche Bedürfnis hatte, alle geladenen Symbolnamen zur Laufzeit abzurufen, habe ich basierend auf der Antwort von R.. einige Nachforschungen angestellt. Hier ist also eine detaillierte Lösung für gemeinsam genutzte Linux-Bibliotheken im ELF-Format, die mit meinem gcc 4.3.4, aber hoffentlich auch mit neueren Versionen funktioniert.

Ich habe hauptsächlich die folgenden Quellen verwendet, um diese Lösung zu entwickeln:

Und hier ist mein Code. Ich habe selbsterklärende Variablennamen verwendet und detaillierte Kommentare hinzugefügt, um es verständlich zu machen. Wenn etwas falsch ist oder fehlt, lassen Sie es mich bitte wissen … (Bearbeiten: Ich habe gerade festgestellt, dass die Frage für C war und mein Code für C ++. Aber wenn Sie den Vektor und die Zeichenfolge weglassen, sollte es auch für C funktionieren )

#include <link.h>
#include <string>
#include <vector>

using namespace std;

/* Callback for dl_iterate_phdr.
 * Is called by dl_iterate_phdr for every loaded shared lib until something
 * else than 0 is returned by one call of this function.
 */
int retrieve_symbolnames(struct dl_phdr_info* info, size_t info_size, void* symbol_names_vector) 
{

    /* ElfW is a macro that creates proper typenames for the used system architecture
     * (e.g. on a 32 bit system, ElfW(Dyn*) becomes "Elf32_Dyn*") */
    ElfW(Dyn*) dyn;
    ElfW(Sym*) sym;
    ElfW(Word*) hash;

    char* strtab = 0;
    char* sym_name = 0;
    ElfW(Word) sym_cnt = 0;

    /* the void pointer (3rd argument) should be a pointer to a vector<string>
     * in this example -> cast it to make it usable */
    vector<string>* symbol_names = reinterpret_cast<vector<string>*>(symbol_names_vector);

    /* Iterate over all headers of the current shared lib
     * (first call is for the executable itself) */
    for (size_t header_index = 0; header_index < info->dlpi_phnum; header_index++)
    {

        /* Further processing is only needed if the dynamic section is reached */
        if (info->dlpi_phdr[header_index].p_type == PT_DYNAMIC)
        {

            /* Get a pointer to the first entry of the dynamic section.
             * It's address is the shared lib's address + the virtual address */
            dyn = (ElfW(Dyn)*)(info->dlpi_addr +  info->dlpi_phdr[header_index].p_vaddr);

            /* Iterate over all entries of the dynamic section until the
             * end of the symbol table is reached. This is indicated by
             * an entry with d_tag == DT_NULL.
             *
             * Only the following entries need to be processed to find the
             * symbol names:
             *  - DT_HASH   -> second word of the hash is the number of symbols
             *  - DT_STRTAB -> pointer to the beginning of a string table that
             *                 contains the symbol names
             *  - DT_SYMTAB -> pointer to the beginning of the symbols table
             */
            while(dyn->d_tag != DT_NULL)
            {
                if (dyn->d_tag == DT_HASH)
                {
                    /* Get a pointer to the hash */
                    hash = (ElfW(Word*))dyn->d_un.d_ptr;

                    /* The 2nd word is the number of symbols */
                    sym_cnt = hash[1];

                }
                else if (dyn->d_tag == DT_STRTAB)
                {
                    /* Get the pointer to the string table */
                    strtab = (char*)dyn->d_un.d_ptr;
                }
                else if (dyn->d_tag == DT_SYMTAB)
                {
                    /* Get the pointer to the first entry of the symbol table */
                    sym = (ElfW(Sym*))dyn->d_un.d_ptr;


                    /* Iterate over the symbol table */
                    for (ElfW(Word) sym_index = 0; sym_index < sym_cnt; sym_index++)
                    {
                        /* get the name of the i-th symbol.
                         * This is located at the address of st_name
                         * relative to the beginning of the string table. */
                        sym_name = &strtab[sym[sym_index].st_name];

                        symbol_names->push_back(string(sym_name));
                    }
                }

                /* move pointer to the next entry */
                dyn++;
            }
        }
    }

    /* Returning something != 0 stops further iterations,
     * since only the first entry, which is the executable itself, is needed
     * 1 is returned after processing the first entry.
     *
     * If the symbols of all loaded dynamic libs shall be found,
     * the return value has to be changed to 0.
     */
    return 1;

}

int main()
{
    vector<string> symbolNames;
    dl_iterate_phdr(retrieve_symbolnames, &symbolNames);

    return 0;
}

  • Die Verwendung von DT_HASH zum Abrufen der Symbolanzahl scheint unzuverlässig zu sein. Wenn ich den obigen Code ausführe, gibt es interessanterweise nie einen DT_HASH. Außerdem sollte symbol_count auf 0 initialisiert werden, sonst kommt es zu Spielereien.

    – justin.m.chase

    27. April 2015 um 23:08 Uhr

  • Es stellt sich heraus, dass es möglich ist, einen DT_GNU_HASH anstelle eines DT_HASH zu erhalten. Weiß jemand wie man die bekommt sym_cnt aus einem Gnu-Hash stattdessen?

    – justin.m.chase

    28. April 2015 um 4:20 Uhr


  • @justin.m.chase: DT_GNU_HASH bietet keine einfache Möglichkeit, die Symbolanzahl zu ermitteln, ohne einfach alle Hash-Buckets zu durchlaufen und zu zählen. Sie können meinen Code dazu hier sehen: git.musl-libc.org/cgit/musl/tree/src/ldso/…

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    28. April 2015 um 14:13 Uhr

  • Vielen Dank für dieses Snippet, ich habe festgestellt, dass das Aufrufen von einem .so selbst Ihnen DT_GNU_HASH gibt, das relevant ist, und Sie brauchen etwas mehr Code wie diesen github.com/axlecrusher/hgengine3/blob/C++/Mercury3/… was selbst nicht alle meine Probleme löst.

    – h4ck3rm1k3

    27. Mai 2018 um 22:26 Uhr

Auf dynamisch verknüpften ELF-basierten Systemen haben Sie möglicherweise eine Funktion dl_iterate_phdr verfügbar. Wenn dies der Fall ist, kann es verwendet werden, um Informationen zu jeder geladenen gemeinsam genutzten Bibliotheksdatei zu sammeln, und die Informationen, die Sie erhalten, reichen aus, um die Symboltabellen zu untersuchen. Der Prozess ist im Wesentlichen:

  1. Holen Sie sich die Adresse der Programmköpfe aus der dl_phdr_info Struktur an Sie zurückgereicht.
  2. Verwenden Sie die PT_DYNAMIC Programmkopf zu finden _DYNAMIC Tabelle für das Modul.
  3. Verwenden Sie die DT_SYMTAB, DT_STRTABund DT_HASH Einträge von _DYNAMIC um die Liste der Symbole zu finden. DT_HASH wird nur benötigt, um die Länge der Symboltabelle zu erhalten, da sie anscheinend nirgendwo anders gespeichert ist.

Die Typen, die Sie benötigen, sollten alle enthalten sein <elf.h> und <link.h>.

  • Was ist mit Symbolen, die nicht dynamisch verknüpft sind? Oder ist so etwas wie libc auch eine gemeinsam genutzte Bibliothek?

    – dkuh

    3. April 2013 um 5:54 Uhr

  • Ja, vorausgesetzt, Sie verwenden dynamisches Linken, “libc” ist eine gemeinsam genutzte Bibliothek, und Sie können ihre Symboltabelle auch auf diese Weise erhalten.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    3. April 2013 um 6:03 Uhr

Benutzer-Avatar
Hyde

Dies ist nicht wirklich C-spezifisch, sondern Betriebssystem- und Binärformat und (zum Debuggen von Symbolen und unverfälschten C++-Symbolnamen) sogar Compiler-spezifische Frage. Es gibt keinen generischen Weg und auch keinen wirklich eleganten Weg.

Der portabelste und zukunftssicherste Weg ist wahrscheinlich das Ausführen eines externen Programms wie z nmdie sich in POSIX befindet. GNU-Version gefunden in Linux hat wahrscheinlich eine Reihe von Erweiterungen, die Sie vermeiden sollten, wenn Sie auf Portabilität und Zukunftssicherheit abzielen.

Seine Ausgabe sollte stabil bleiben, und selbst wenn sich Binärformate ändern, wird es auch aktualisiert und funktioniert weiter. Führen Sie es einfach mit den richtigen Schaltern aus, erfassen Sie seine Ausgabe (wahrscheinlich, indem Sie es durchlaufen popen um eine temporäre Datei zu vermeiden) und analysieren Sie diese.

Benutzer-Avatar
Andrej Belich

Ich habe den Code aus der Antwort von Kanalpiroge aktualisiert, damit er auch funktioniert, wenn DT_HASH fehlt (z. B. RHEL). Es ist für 64 Bit, aber es ist relativ einfach, es so zu ändern, dass es auch 32 Bit unterstützt. Die Inspiration kam von hier: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/876879/18/snapshot/elf/elf_image_reader.cc#b512.

#include <link.h>
#include <string>
#include <vector>

using namespace std;

static uint32_t GetNumberOfSymbolsFromGnuHash(Elf64_Addr gnuHashAddress)
{
    // See https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/ and
    // https://sourceware.org/ml/binutils/2006-10/msg00377.html
    typedef struct
    {
        uint32_t nbuckets;
        uint32_t symoffset;
        uint32_t bloom_size;
        uint32_t bloom_shift;
    } Header;

    Header* header = (Header*)gnuHashAddress;
    const void* bucketsAddress = (void*)gnuHashAddress + sizeof(Header) + (sizeof(uint64_t) * header->bloom_size);

    // Locate the chain that handles the largest index bucket.
    uint32_t lastSymbol = 0;
    uint32_t* bucketAddress = (uint32_t*)bucketsAddress;
    for (uint32_t i = 0; i < header->nbuckets; ++i)
    {
        uint32_t bucket = *bucketAddress;
        if (lastSymbol < bucket)
        {
            lastSymbol = bucket;
        }
        bucketAddress++;
    }

    if (lastSymbol < header->symoffset)
    {
        return header->symoffset;
    }

    // Walk the bucket's chain to add the chain length to the total.
    const void* chainBaseAddress = bucketsAddress + (sizeof(uint32_t) * header->nbuckets);
    for (;;)
    {
        uint32_t* chainEntry = (uint32_t*)(chainBaseAddress + (lastSymbol - header->symoffset) * sizeof(uint32_t));
        lastSymbol++;

        // If the low bit is set, this entry is the end of the chain.
        if (*chainEntry & 1)
        {
            break;
        }
    }

    return lastSymbol;
}

/* Callback for dl_iterate_phdr.
 * Is called by dl_iterate_phdr for every loaded shared lib until something
 * else than 0 is returned by one call of this function.
 */
int retrieve_symbolnames(struct dl_phdr_info* info, size_t info_size, void* symbol_names_vector) 
{

    /* ElfW is a macro that creates proper typenames for the used system architecture
     * (e.g. on a 32 bit system, ElfW(Dyn*) becomes "Elf32_Dyn*") */
    ElfW(Dyn*) dyn;
    ElfW(Sym*) sym;
    ElfW(Word*) hash;

    char* strtab = 0;
    char* sym_name = 0;
    ElfW(Word) sym_cnt = 0;

    /* the void pointer (3rd argument) should be a pointer to a vector<string>
     * in this example -> cast it to make it usable */
    vector<string>* symbol_names = reinterpret_cast<vector<string>*>(symbol_names_vector);

    /* Iterate over all headers of the current shared lib
     * (first call is for the executable itself) */
    for (size_t header_index = 0; header_index < info->dlpi_phnum; header_index++)
    {

        /* Further processing is only needed if the dynamic section is reached */
        if (info->dlpi_phdr[header_index].p_type == PT_DYNAMIC)
        {

            /* Get a pointer to the first entry of the dynamic section.
             * It's address is the shared lib's address + the virtual address */
            dyn = (ElfW(Dyn)*)(info->dlpi_addr +  info->dlpi_phdr[header_index].p_vaddr);

            /* Iterate over all entries of the dynamic section until the
             * end of the symbol table is reached. This is indicated by
             * an entry with d_tag == DT_NULL.
             *
             * Only the following entries need to be processed to find the
             * symbol names:
             *  - DT_HASH   -> second word of the hash is the number of symbols
             *  - DT_STRTAB -> pointer to the beginning of a string table that
             *                 contains the symbol names
             *  - DT_SYMTAB -> pointer to the beginning of the symbols table
             */
            while(dyn->d_tag != DT_NULL)
            {
                if (dyn->d_tag == DT_HASH)
                {
                    /* Get a pointer to the hash */
                    hash = (ElfW(Word*))dyn->d_un.d_ptr;

                    /* The 2nd word is the number of symbols */
                    sym_cnt = hash[1];

                }
                else if (dyn->d_tag == DT_GNU_HASH && sym_cnt == 0)
                {
                    sym_cnt = GetNumberOfSymbolsFromGnuHash(dyn->d_un.d_ptr);
                }
                else if (dyn->d_tag == DT_STRTAB)
                {
                    /* Get the pointer to the string table */
                    strtab = (char*)dyn->d_un.d_ptr;
                }
                else if (dyn->d_tag == DT_SYMTAB)
                {
                    /* Get the pointer to the first entry of the symbol table */
                    sym = (ElfW(Sym*))dyn->d_un.d_ptr;


                    /* Iterate over the symbol table */
                    for (ElfW(Word) sym_index = 0; sym_index < sym_cnt; sym_index++)
                    {
                        /* get the name of the i-th symbol.
                         * This is located at the address of st_name
                         * relative to the beginning of the string table. */
                        sym_name = &strtab[sym[sym_index].st_name];

                        symbol_names->push_back(string(sym_name));
                    }
                }

                /* move pointer to the next entry */
                dyn++;
            }
        }
    }

    /* Returning something != 0 stops further iterations,
     * since only the first entry, which is the executable itself, is needed
     * 1 is returned after processing the first entry.
     *
     * If the symbols of all loaded dynamic libs shall be found,
     * the return value has to be changed to 0.
     */
    return 1;

}

int main()
{
    vector<string> symbolNames;
    dl_iterate_phdr(retrieve_symbolnames, &symbolNames);

    return 0;
}

Benutzer-Avatar
Yoyokko

Es sollte sein dl_iterate_phdr(retrieve_symbolnames, &symbolNames);

1352250cookie-checkWie listet man spontan alle Funktionen/Symbole auf, die in C-Code auf einer Linux-Architektur verfügbar sind?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy