79 votes

Annuler un appel de fonction en C

Je veux remplacer certaines des appels de fonction pour différentes Api pour l'amour de l'enregistrement des appels, mais j'ai aussi susceptibles de vouloir manipuler les données avant de les envoyer à la fonction réelle.

Par exemple, dis-je utiliser une fonction appelée getObjectName des milliers de fois dans mon code source. Je veux désactiver temporairement cette fonction, parfois, parce que je veux changer le comportement de cette fonction pour voir le résultat différent.

J'ai créer un nouveau fichier source comme ceci:

#include <apiheader.h>    

const char *getObjectName (object *anObject)
{
    if (anObject == NULL)
        return "(null)";
    else
        return "name should be here";
}

J'ai compiler tous mes autres source comme je le feriez normalement, mais je le lier à l'encontre de cette fonction première, avant la liaison avec l'API de la bibliothèque. Cela fonctionne bien sauf que je peut évidemment pas à l'appel de la fonction réelle à l'intérieur de ma fonction dominante.

Est-il un moyen plus facile de "remplacer" une fonction sans se relier/compilation les erreurs/avertissements? Idéalement, je veux être en mesure de remplacer la fonction par suffit de compiler et lier un fichier supplémentaire ou deux, plutôt que de bricoler avec des options de liaison ou de modifier le code source de mon programme.

93voto

codelogic Points 22722

Avec gcc sous Linux, vous pouvez utiliser l' --wrap linker drapeau comme ceci:

gcc program.c -Wl,-wrap,getObjectName -o program

et de définir votre fonction:

const char *__wrap_getObjectName (object *anObject)
{
    if (anObject == NULL)
        return "(null)";
    else
        return __real_getObjectName( anObject ); // call the real function
}

Cela permettra d'assurer que tous les appels d' getObjectName() sont redirigés vers votre fonction wrapper (au moment de la liaison). Très utile, le drapeau est cependant absent dans gcc sous Mac OS X.

N'oubliez pas de déclarer la fonction wrapper avec extern "C" si vous êtes à la compilation avec g++.

82voto

paxdiablo Points 341644

Si c'est seulement pour votre source que vous souhaitez capturer/modifier le appels, la solution la plus simple est de mettre en place un fichier d'en-tête (intercept.h) avec:

#ifdef INTERCEPT
    #define getObjectName(x) myGetObectName(x)
#endif

et de mettre en œuvre la fonction comme suit (en intercept.c qui ne comprennent intercept.h):

const char *myGetObjectName (object *anObject) {
    if (anObject == NULL)
        return "(null)";
    else
        return getObjectName(anObject);
}

Ensuite, assurez-vous que chaque fichier source où vous souhaitez intercepter l'appel a:

#include "intercept.h"

dans la partie supérieure.

Ensuite, lorsque vous compilez avec "-DINTERCEPT", tous les fichiers d'appel de votre fonction, plutôt que le réel et la fonction que vous pouvez toujours appeler le réel.

Compilation sans le "-DINTERCEPT" permettra d'éviter l'interception de se produire.

C'est un peu plus compliqué si vous voulez intercepter tous les appels (et pas seulement ceux de votre source) - ce qui peut généralement être effectué avec le chargement dynamique et la résolution de la fonction réelle (avec dlload- et dlsym-type d'appels) mais je ne pense pas que c'est nécessaire dans votre cas.

40voto

qrdl Points 17813

Vous pouvez remplacer une fonction à l'aide d' LD_PRELOAD astuce - voir man ld.so. Vous compilez partagé lib avec votre fonction et de lancer le binaire (vous n'avez même pas besoin de modifier le binaire!) comme LD_PRELOAD=mylib.so myprog.

Dans le corps de votre fonction (partagée de la lib) vous écrire comme ceci:

const char *getObjectName (object *anObject) {
  static char * (*func)();

  if(!func)
    func = (char *(*)()) dlsym(RTLD_NEXT, "getObjectName");
  printf("Overridden!\n");     
  return(func(anObject));    // call original function
}

Vous pouvez remplacer n'importe quelle fonction de bibliothèque partagée, même à partir de stdlib, sans modifier/recompiler le programme, de sorte que vous pourrait faire l'affaire sur les programmes que vous n'avez pas de source pour. N'est-il pas beau?

28voto

Si vous utilisez GCC, vous pouvez faire de votre fonction weak. Ceux qui peuvent être remplacées par des non-faible fonctions:

test.c:

#include <stdio.h>

__attribute__((weak)) void test(void) { 
    printf("not overridden!\n"); 
}

int main() {
    test();
}

Que faut-il faire?

$ gcc test.c
$ ./a.out
not overridden!

test1.c:

#include <stdio.h>

void test(void) {
    printf("overridden!\n");
}

Que faut-il faire?

$ gcc test1.c test.c
$ ./a.out
overridden!

Malheureusement, cela ne fonctionnera pas pour les autres compilateurs. Mais vous pouvez avoir la faiblesse des déclarations qui contiennent des substituables fonctions dans leur propre fichier, en plaçant simplement l'inclure dans l'implémentation de l'API de fichiers, si vous êtes à la compilation à l'aide de GCC:

weakdecls.h:

__attribute__((weak)) void test(void);
... other weak function declarations ...

fonctions.c:

/* for GCC, these will become weak definitions */
#ifdef __GNUC__
#include "weakdecls.h"
#endif

void test(void) { 
    ...
}

... other functions ...

Inconvénient de cette est que cela ne fonctionne pas entièrement sans faire quelque chose pour les fichiers api (besoin de ces trois lignes et la weakdecls). Mais une fois que vous avez fait ce changement, les fonctions peuvent être remplacées facilement par l'écriture d'une définition globale dans un fichier et en l'associant dans.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X