GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo enumerar sobre la marcha todas las funciones/símbolos disponibles en código C en una arquitectura Linux?

Como tenía la misma necesidad de recuperar todos los nombres de símbolos cargados en tiempo de ejecución, investigué un poco en función de la respuesta de R.. Así que aquí hay una solución detallada para bibliotecas compartidas de Linux en formato ELF que funciona con mi gcc 4.3.4, pero con suerte también con versiones más nuevas.

Usé principalmente las siguientes fuentes para desarrollar esta solución:

  • Página de manual de ELF
  • Algún código de muestra (lo encontré mientras buscaba "dl_iterate_phdr")

Y aquí está mi código. Usé nombres de variables autoexplicativos y agregué comentarios detallados para hacerlo comprensible. Si algo está mal o falta, hágamelo saber... (Editar:me acabo de dar cuenta de que la pregunta era para C y mi código es para C++. Pero si omite el vector y la cadena, también debería funcionar para 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;
}

En sistemas basados ​​en ELF de enlace dinámico, es posible que tenga una función dl_iterate_phdr disponible. Si es así, se puede usar para recopilar información sobre cada archivo de biblioteca compartida cargado, y la información que obtiene es suficiente para examinar las tablas de símbolos. El proceso es básicamente:

  1. Obtenga la dirección de los encabezados del programa del dl_phdr_info estructura devuelta a usted.
  2. Utilice el PT_DYNAMIC encabezado del programa para encontrar el _DYNAMIC tabla para el módulo.
  3. Utilice el DT_SYMTAB , DT_STRTAB y DT_HASH entradas de _DYNAMIC para encontrar la lista de símbolos. DT_HASH solo se necesita para obtener la longitud de la tabla de símbolos, ya que no parece estar almacenada en ningún otro lugar.

Los tipos que necesita deben estar todos en <elf.h> y <link.h> .


Esto no es realmente específico de C, sino del sistema operativo y el formato binario e (para símbolos de depuración y nombres de símbolos de C ++ no alterados) incluso una pregunta específica del compilador. No existe una forma genérica, y tampoco una forma verdaderamente elegante.

La forma más portátil y preparada para el futuro probablemente sea ejecutar un programa externo como nm , que está en POSIX. La versión de GNU que se encuentra en Linux probablemente tenga un montón de extensiones, que debe evitar si busca portabilidad y seguridad para el futuro.

Su salida debería permanecer estable, e incluso si los formatos binarios cambian, también se actualizará y seguirá funcionando. Simplemente ejecútelo con los interruptores correctos, capture su salida (probablemente ejecutándolo a través de popen para evitar un archivo temporal) y analizar eso.


Linux
  1. Cómo enumerar todas las configuraciones regionales disponibles en RHEL7 Linux

  2. Cómo averiguar la lista de todos los puertos abiertos en Linux

  3. Cómo enumerar las dependencias de un paquete en Linux

  4. ¿Cómo enumerar todos los usuarios en un grupo de Linux?

  5. ¿Cómo enumerar todos los archivos de una partición en Linux?

Cómo encontrar la lista de repositorios instalados en Linux

Cómo encontrar todas las fuentes instaladas en Linux

¿Cómo enumerar los nombres de colores disponibles?

Cómo enumerar todos los usuarios de un grupo en Linux

¿Cómo enumerar todos los grupos en Linux?

¿Cómo enumerar todos los archivos ordenados por tamaño en Linux?