web-dev-qa-db-fra.com

Comment obtenir le nom de la fonction du pointeur de la fonction en C?

Comment obtenir le nom de la fonction à partir de le pointeur de la fonction en C?

Edit: Le cas réel est le suivant: j'écris un module de noyau Linux et j'appelle des fonctions du noyau. Certaines de ces fonctions sont des pointeurs et je souhaite inspecter le code de cette fonction dans la source du noyau. Mais je ne sais pas quelle fonction il pointe. Je pensais que cela pouvait être fait car, lorsque le système échouait (panique du noyau), il imprimait à l'écran le callstack actuel avec les noms des fonctions. Mais je suppose que je me suis trompé… n'est-ce pas?

52
Daniel Silveira

Ce n'est pas directement possible sans aide supplémentaire.

Vous pourriez:

  1. maintenir une table dans votre programme en mappant les pointeurs sur les noms

  2. examinez la table de symboles de l'exécutable, s'il en a une.

Ce dernier, cependant, est difficile et n'est pas portable. La méthode dépendra du format binaire du système d'exploitation (ELF, a.out, .exe, etc.), ainsi que de toute relocalisation effectuée par l'éditeur de liens.

EDIT: Puisque vous avez maintenant expliqué votre cas d'utilisation réel, la réponse n'est en réalité pas si difficile. La table des symboles du noyau est disponible dans /proc/kallsyms, et il existe une API pour y accéder:

#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)

Pour des raisons de débogage simples, ce dernier fera probablement exactement ce dont vous avez besoin: il prend l'adresse, la formate et l'envoie à printk ou vous pouvez utiliser printk avec le spécificateur de format %pF.

29
Alnitak

Je suis surpris que tout le monde dise que ce n'est pas possible. C'est possible sous Linux pour des fonctions non statiques.

Je connais au moins deux façons d'y parvenir.

Il existe des fonctions GNU pour l'impression de la trace: backtrace() et backtrace_symbols() (voir man). Dans votre cas, vous n'avez pas besoin de backtrace() car vous avez déjà un pointeur de fonction, vous le transmettez simplement à backtrace_symbols().

Exemple (code de travail):

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

Compiler avec gcc test.c -rdynamic

Sortie: ./a.out(foo+0x0)[0x8048634]

Il vous donne un nom binaire, un nom de fonction, un décalage de pointeur par rapport au début de la fonction et une valeur de pointeur afin que vous puissiez l'analyser.

Une autre méthode consiste à utiliser dladdr() (une autre extension). Je suppose que print_backtrace() utilise dladdr(). dladdr() renvoie la structure Dl_info ayant le nom de la fonction dans le champ dli_sname. Je ne fournis pas d'exemple de code ici mais c'est évident - voir man dladdr pour plus de détails.

NB! Les deux approches exigent que la fonction soit non statique!

Eh bien, il existe un autre moyen - utiliser les informations de débogage à l’aide de libdwarf, mais cela nécessiterait des données binaires non fragmentées et pas très faciles à faire;.

62
qrdl

Dans le noyau Linux, vous pouvez utiliser directement "% pF" format de printk!

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

Ce qui suit me fonctionne sous Linux:

  • printf l'adresse de la fonction en utilisant %p
  • Ensuite, faites un nm <program_path> | grep <address> (sans le préfixe 0x)
  • Il devrait vous montrer le nom de la fonction. 

Cela ne fonctionne que si la fonction en question est dans le même programme (pas dans une bibliothèque liée dynamiquement ou autre).

Si vous pouvez trouver les adresses de chargement des bibliothèques partagées chargées, vous pouvez soustraire l'adresse du numéro imprimé et utiliser nm sur la bibliothèque pour rechercher le nom de la fonction.

5
Calmarius

Si la liste des fonctions pouvant être désignées n'est pas trop grande ou si vous soupçonnez déjà un petit groupe de fonctions, vous pouvez imprimer les adresses et les comparer à celle utilisée pendant l'exécution. Ex:

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

Sortie:

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

Cette approche fonctionnera aussi pour les fonctions statiques.

2
givanse

Vous ne pouvez pas diectly mais vous pouvez implémenter une approche différente de ce problème si vous le souhaitez. Vous pouvez créer un pointeur struct pointant vers une fonction ainsi qu'une chaîne descriptive que vous pouvez définir comme bon vous semble .. J'ai également ajouté une fonction de débogage car vous ne voulez probablement pas que ces fichiers vars soient définitivement imprimés.

// 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
2
eaanon01

Il n'y a aucun moyen de le faire en général.

Si vous compilez le code correspondant dans une DLL/bibliothèque partagée, vous devriez pouvoir inscrire tous les points d'entrée et les comparer au pointeur que vous avez. Je ne l’ai pas encore essayé, mais j’ai une certaine expérience des DLL/bibliothèques partagées et j’espère bien que cela fonctionnera. Cela pourrait même être implémenté pour travailler sur plusieurs plates-formes.

Quelqu'un d'autre a déjà mentionné la compilation avec les symboles de débogage, alors vous pourriez essayer de trouver un moyen d'analyser ceux-ci à partir de l'application en cours d'exécution, semblable à ce qu'un débogueur ferait . Mais c'est absolument propriétaire et non portable.

1
mh.
  1. Utilisez kallsyms_lookup_name() pour trouver l’adresse de kallsyms_lookup.

  2. Utilisez un pointeur de fonction qui pointe sur kallsyms_lookup pour l'appeler.

0
WindChaser

Pas exactement ce que la question demande, mais après avoir lu les réponses ici, j'ai pensé à cette solution à un problème similaire:

/**
* 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 votre programme en a beaucoup besoin, vous pouvez définir les noms de méthodes avec une chaîne dans un XMacro et utiliser #define X(name, str) ... #undef X dans le code pour obtenir la chaîne correspondante à partir du nom de la fonction.

0
The Gramm

Consultez Détecteur de fuite visuel pour voir comment ils obtiennent l'impression de leur pile d'appel. Cela suppose que vous utilisez Windows, cependant.

0
Jim Buck