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

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

Edit: le cas réel est: j'écris un module du noyau linux et j'appelle des fonctions du noyau. Certaines de ces fonctions sont des pointeurs et je veux inspecter le code de cette fonction dans la source du noyau. Mais je ne sais pas quelle fonction il pointe. Je pensais que cela pourrait être fait parce que, lorsque le système échoue (panique du noyau), il imprime dans l'écran le callstack actuel avec les noms de la fonction. Mais, je suppose que je avait tort... suis-je?

49
demandé sur Peter N Lewis 2008-12-09 01:18:17

11 réponses

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

Vous pourriez:

  1. Maintenez une table dans votre fonction de mappage de programme pointeurs vers les noms

  2. Examinez la table de symboles de l'exécutable, si elle en a une.

Le 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), et aussi sur tout déplacement effectué par l'éditeur de liens.

EDIT: puisque vous avez maintenant expliqué Quel Est votre cas d'utilisation réel, la réponse n'est pas si difficile. La table des symboles du noyau est disponible dans /proc/kallsyms, et il y a 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)

À des fins 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.

26
répondu Alnitak 2018-08-27 11:21:39

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

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

Il existe des fonctions GNU pour l'impression en arrière-plan: backtrace() et backtrace_symbols() (Voir man). Dans votre cas, vous n'avez pas besoin de backtrace() comme vous avez déjà un pointeur de fonction, vous le passez 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, nom de la fonction, décalage du pointeur par rapport au début de la fonction et valeur du pointeur afin que vous puissiez l'analyser.

Une Autre façon est d'utiliser dladdr() (une autre extension), je suppose que print_backtrace() utilise dladdr(). dladdr() renvoie la structure Dl_info qui a 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 y a une autre façon - utiliser des informations de débogage en utilisant libdwarf mais cela nécessiterait remontées binaire et pas très facile à faire, donc je ne le recommande pas.

61
répondu qrdl 2008-12-09 06:44:09

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

void *func = &foo;
printk("func: %pF at address: %p\n", func, func);
12
répondu Zskdan 2014-02-19 10:32:20

Ce qui suit me fonctionne sur 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 quelque chose).

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 le bibliothèque pour trouver le nom de la fonction.

5
répondu Calmarius 2013-08-29 18:53:19

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 plutôt vers une fonction ainsi qu'une chaîne descriptive que vous pouvez définir sur ce que vous voulez. J'ai également ajouté un posebilety de débogage puisque vous ne voulez probablement pas que ces vars soient imprimés pour toujours.

// 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
répondu eaanon01 2008-12-08 22:56:32

Si la liste des fonctions qui peuvent être pointé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 lors de 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
répondu givanse 2012-01-06 17:48:29

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 être en mesure d'enrôler tous les points d'entrée et de comparer avec le pointeur que vous avez. Je ne l'ai pas encore essayé, mais j'ai une certaine expérience avec les DLL / bibliothèques partagées et je m'attendrais à ce que cela fonctionne. Cela pourrait même être mis en œuvre pour travailler cross-plarform.

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

1
répondu mh. 2008-12-08 22:58:26

Consultez Visual Leak Detector pour voir comment ils font fonctionner leur impression callstack. Cela suppose que vous utilisez Windows, cependant.

0
répondu Jim Buck 2008-12-08 22:24:39
  1. Utiliser 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
répondu WindChaser 2016-06-27 05:27:56

Vous ne pouvez pas. le nom de la fonction n'est pas attaché à la fonction au moment où elle est compilée et liée. Tout est par adresse mémoire à ce moment-là, pas par nom.

-2
répondu sblundy 2008-12-08 22:21:39

Vous ne sauriez pas à quoi vous ressemblez sans un miroir réfléchissant. Vous devrez utiliser un langage capable de réflexion comme C#.

-4
répondu yogman 2008-12-08 23:10:23