GNU/Linux >> Znalost Linux >  >> Linux

Jak vypsat za běhu všechny funkce/symboly dostupné v kódu C na architektuře Linuxu?

Protože jsem měl stejnou potřebu načíst všechny načtené názvy symbolů za běhu, provedl jsem nějaký průzkum založený na odpovědi R.. Zde je tedy podrobné řešení pro linuxové sdílené knihovny ve formátu ELF, které funguje s mým gcc 4.3.4, ale doufejme, že i s novějšími verzemi.

K vývoji tohoto řešení jsem většinou použil následující zdroje:

  • Manuální stránka ELF
  • Nějaký ukázkový kód (našel jsem ho při hledání „dl_iterate_phdr“)

A tady je můj kód. Použil jsem samovysvětlující názvy proměnných a přidal podrobné komentáře, aby to bylo srozumitelné. Pokud je něco špatně nebo chybí, dejte mi prosím vědět...(Edit:Právě jsem si uvědomil, že otázka byla pro C a můj kód je pro C++. Ale pokud vynecháte vektor a řetězec, mělo by to fungovat i pro C )

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

Na dynamicky propojených systémech založených na ELF můžete mít funkci dl_iterate_phdr dostupný. Pokud ano, lze jej použít ke shromáždění informací o každém načteném souboru sdílené knihovny a získané informace postačují k prozkoumání tabulek symbolů. Proces je v podstatě:

  1. Získejte adresu záhlaví programu z dl_phdr_info struktura vám předána zpět.
  2. Použijte PT_DYNAMIC záhlaví programu, abyste našli _DYNAMIC tabulka pro modul.
  3. Použijte DT_SYMTAB , DT_STRTAB a DT_HASH položky _DYNAMIC najít seznam symbolů. DT_HASH je potřeba pouze k získání délky tabulky symbolů, protože se zdá, že není uložena nikde jinde.

Všechny typy, které potřebujete, by měly být v <elf.h> a <link.h> .


Toto není ve skutečnosti specifické pro C, ale o operační systém a binární formát a (pro ladění symbolů a neporušených názvů symbolů C++) dokonce o otázku specifickou pro kompilátor. Neexistuje žádný obecný způsob a také žádný skutečně elegantní způsob.

Nejpřenosnějším a nejperspektivnějším způsobem je pravděpodobně spuštění externího programu, jako je nm , který je v POSIX. Verze GNU nalezená v Linuxech má pravděpodobně spoustu rozšíření, kterým byste se měli vyhnout, pokud usilujete o přenositelnost a zabezpečení do budoucna.

Jeho výstup by měl zůstat stabilní, a i když se binární formáty změní, bude také aktualizován a bude fungovat. Stačí jej spustit pomocí správných přepínačů, zachytit jeho výstup (pravděpodobně spuštěním přes popen abyste se vyhnuli dočasnému souboru) a analyzujte jej.


Linux
  1. Jak vypsat všechny nainstalované balíčky v Linuxu

  2. Jak najít všechny řídké soubory v Linuxu

  3. Jak vypsat všechny soubory oddílu na linuxu?

  1. Jak vypsat všechna dostupná národní prostředí na RHEL7 Linux

  2. Jak zjistit seznam všech otevřených portů v Linuxu

  3. Jak vypsat seznam závislostí balíčku v Linuxu

  1. Jak vypsat všechny uživatele ve skupině Linuxu?

  2. Jak zobrazím seznam funkcí, které exportuje sdílená knihovna Linuxu?

  3. Jak mohu uvést seznam všech zamčených uživatelů v Linuxu?