¿Cómo obtener el nombre de la función a partir del puntero de la función en C?

StackOverflow https://stackoverflow.com/questions/351134

  •  20-08-2019
  •  | 
  •  

Pregunta

Cómo obtener el nombre de la función puntero de función ¿Cª?

Editar:El caso real es:Estoy escribiendo un módulo del kernel de Linux y estoy llamando a funciones del kernel.Algunas de estas funciones son punteros y quiero inspeccionar el código de esa función en la fuente del kernel.Pero no sé a qué función apunta.Pensé que se podría hacer porque, cuando el sistema falla (pánico del kernel), imprime en la pantalla la pila de llamadas actual con los nombres de las funciones.Pero supongo que me equivoqué...¿lo soy?

¿Fue útil?

Solución

Eso no es directamente posible sin asistencia adicional.

Podrías:

  1. mantenga una tabla en los punteros de la función de mapeo de su programa a los nombres

  2. examine la tabla de símbolos del ejecutable, si tiene una.

El último, sin embargo, es difícil y no es portátil. El método dependerá del formato binario del sistema operativo (ELF, a.out, .exe, etc.), y también de cualquier reubicación realizada por el vinculador.

EDITAR: dado que ya ha explicado cuál es su caso de uso real, la respuesta no es tan difícil. La tabla de símbolos del núcleo está disponible en /proc/kallsyms, y hay una API para acceder a ella:

#include <linux/kallsyms.h>

const char *kallsyms_lookup(unsigned long addr, unsigned long *symbolsize,
                            unsigned long *ofset, char **modname, char *namebuf)

void print_symbol(const char *fmt, unsigned long addr)

Para propósitos simples de depuración, este último probablemente hará exactamente lo que necesita: toma la dirección, la formatea y la envía a printk, o puede usar %pF con el especificador de formato <=>.

Otros consejos

Me sorprende que todo el mundo diga que no es posible.Es posible en Linux para funciones no estáticas.

Conozco al menos dos formas de lograrlo.

Hay funciones GNU para la impresión de seguimiento: backtrace() y backtrace_symbols() (Ver man).En tu caso no necesitas backtrace() como ya tienes un puntero de función, simplemente pásalo a backtrace_symbols().

Ejemplo (código de trabajo):

#include <stdio.h>
#include <execinfo.h>

void foo(void) {
    printf("foo\n");
}

int main(int argc, char *argv[]) {
    void    *funptr = &foo;

    backtrace_symbols_fd(&funptr, 1, 1);

    return 0;
}

Compilar con gcc test.c -rdynamic

Producción: ./a.out(foo+0x0)[0x8048634]

Le proporciona el nombre binario, el nombre de la función, el desplazamiento del puntero desde el inicio de la función y el valor del puntero para que pueda analizarlo.

Otra forma es utilizar dladdr() (otra extensión), supongo print_backtrace() usos dladdr(). dladdr() devoluciones Dl_info estructura que tiene el nombre de la función en dli_sname campo.No proporciono un ejemplo de código aquí, pero es obvio; consulte man dladdr para detalles.

¡NÓTESE BIEN!¡Ambos enfoques requieren que la función no sea estática!

Bueno, hay una forma más: utilizar la información de depuración utilizando libdwarf pero requeriría binario sin eliminar y no es muy fácil de hacer, por lo que no lo recomiendo.

En el kernel de Linux, puede usar directamente el formato "% pF " de printk!

void *func = &foo;
printk("func: %pF at address: %p\n", func, func);

Lo siguiente me funciona en Linux:

  • printf la dirección de la función usando %p
  • Luego haga un nm <program_path> | grep <address> (sin el prefijo 0x)
  • Debería mostrarte el nombre de la función.

Funciona solo si la función en cuestión está en el mismo programa (no en una biblioteca vinculada dinámicamente o algo así).

Si puede encontrar las direcciones de carga de las bibliotecas compartidas cargadas, puede restar la dirección del número impreso y usar nm en la biblioteca para averiguar el nombre de la función.

No puede hacer nada bien, pero puede implementar un enfoque diferente para este problema si lo desea. En su lugar, puede hacer un puntero de estructura apuntando a una función, así como a una cadena descriptiva que puede establecer en lo que desee. También agregué una posebilety de depuración ya que probablemente no desea que estos vars se impriman para siempre.

// Define it like this
typedef struct
{
  char        *dec_text;
  #ifdef _DEBUG_FUNC
  void        (*action)(char);
  #endif
} func_Struct;

// Initialize it like this
func_Struct func[3]= {
#ifdef _DEBUG_FUNC
{"my_Set(char input)",&my_Set}};
{"my_Get(char input)",&my_Get}};
{"my_Clr(char input)",&my_Clr}};
#else
{&my_Set}};
{&my_Get}};
{&my_Clr}};
#endif 

// And finally you can use it like this
func[0].action( 0x45 );
#ifdef _DEBUG_FUNC
printf("%s",func.dec_text);
#endif

Si la lista de funciones a las que se puede apuntar no es demasiado grande o si ya sospecha de un pequeño grupo de funciones, puede imprimir las direcciones y compararlas con la utilizada durante la ejecución. Ej:

typedef void (*simpleFP)();
typedef struct functionMETA {
    simpleFP funcPtr;
    char * funcName;
} functionMETA;

void f1() {/*do something*/}
void f2() {/*do something*/}
void f3() {/*do something*/}

int main()
{
    void (*funPointer)() = f2; // you ignore this
    funPointer(); // this is all you see

    printf("f1 %p\n", f1);
    printf("f2 %p\n", f2);
    printf("f3 %p\n", f3);

    printf("%p\n", funPointer);

    // if you want to print the name
    struct functionMETA arrFuncPtrs[3] = {{f1, "f1"}, {f2, "f2"} , {f3, "f3"}};

    int i;
    for(i=0; i<3; i++) {
        if( funPointer == arrFuncPtrs[i].funcPtr )
            printf("function name: %s\n", arrFuncPtrs[i].funcName);
    }
}

Salida:

f1 0x40051b
f2 0x400521
f3 0x400527
0x400521
function name: f2

Este enfoque también funcionará para funciones estáticas.

No hay forma de hacerlo en general.

Si compila el código correspondiente en un archivo DLL / Biblioteca compartida, debería poder alistar todos los puntos de entrada y comparar con el puntero que tiene. Todavía no lo he probado, pero tengo algo de experiencia con DLL / Shared Libs y espero que funcione. Esto incluso podría implementarse para trabajar multiplataforma.

Alguien más ya mencionó que compila con símbolos de depuración, entonces podría intentar encontrar una manera de analizarlos desde la aplicación en ejecución, de forma similar a lo que haría un depurador. Pero esto es absolutamente propietario y no portátil.

  1. Use kallsyms_lookup_name() para encontrar la dirección de kallsyms_lookup.

  2. Use un puntero de función que apunte a <=>, para llamarlo.

Consulte Visual Leak Detector para ver cómo obtienen su impresión de pila de llamadas trabajando. Sin embargo, esto supone que está utilizando Windows.

No es exactamente lo que pide la pregunta, pero después de leer las respuestas aquí Pensé en esta solución a un problema similar mío:

/**
* search methods */
static int starts(const char *str, const char *c);
static int fuzzy(const char *str, const char *c);

int (*search_method)(const char *, const char *);

/* asign the search_method and do other stuff */
[...]

printf("The search method is %s\n", search_method == starts ? "starts" : "fuzzy")

Si su programa lo necesita mucho, puede definir los nombres de los métodos junto con una cadena en un XMacro y usar #define X(name, str) ... #undef X en el código para obtener la cadena correspondiente del nombre de la función.

No puedes. El nombre de la función no está asociado a la función cuando se compila y vincula. Todo es por dirección de memoria en ese punto, no por nombre.

No sabrías cómo te ves sin un espejo reflectante. Tendrá que usar un lenguaje con capacidad de reflexión como C #.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top