web-dev-qa-db-fra.com

Créer une fonction wrapper pour malloc et gratuit en C

J'essaie de créer des fonctions d'encapsuleur pour free et malloc en C pour m'aider à me signaler des fuites de mémoire. Est-ce que quelqu'un sait comment déclarer ces fonctions, donc quand j'appelle malloc() et free() il appellera mes fonctions personnalisées et non les fonctions standards lib?

39
Dan Snyder

Vous avez quelques options:

  1. Solution spécifique à GLIBC (principalement Linux). Si votre environnement de compilation est glibc avec gcc, la méthode préférée est à utiliser crochets malloc . Non seulement il vous permet de spécifier des malloc et free personnalisés, mais il identifiera également l'appelant par l'adresse de retour sur la pile.

  2. Solution spécifique à POSIX. Définissez malloc et free comme wrappers aux routines d'allocation d'origine dans votre exécutable, qui "remplacer" la version de libc. À l'intérieur de l'encapsuleur, vous pouvez appeler dans l'implémentation d'origine malloc, que vous pouvez rechercher en utilisant dlsym avec RTLD_NEXT manipuler. Votre application ou bibliothèque qui définit les fonctions d'encapsuleur doit être liée à -ldl.

    #define _GNU_SOURCE
    #include <dlfcn.h>
    #include <stdio.h>
    
    void* malloc(size_t sz)
    {
        void *(*libc_malloc)(size_t) = dlsym(RTLD_NEXT, "malloc");
        printf("malloc\n");
        return libc_malloc(sz);
    }
    
    void free(void *p)
    {
        void (*libc_free)(void*) = dlsym(RTLD_NEXT, "free");
        printf("free\n");
        libc_free(p);
    }
    
    int main()
    {
        free(malloc(10));
        return 0;
    }
    
  3. Spécifique à Linux. Vous pouvez remplacer les fonctions des bibliothèques dynamiques de manière non invasive en les spécifiant dans le LD_PRELOAD variable d'environnement.

    LD_PRELOAD=mymalloc.so ./exe
    
  4. Spécifique à Mac OSX.

    Identique à Linux, sauf que vous utiliserez DYLD_INSERT_LIBRARIES variable d'environnement.

75
Alex B

Vous pouvez faire un wrapper et une fonction "écraser" avec LD_PRELOAD - de manière similaire à l'exemple montré plus haut.

LD_PRELOAD=/path.../lib_fake_malloc.so ./app

Mais je recommande de le faire "légèrement" plus intelligemment, je veux dire appeler une fois dlsym .

#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>

void* malloc(size_t size)
{
    static void* (*real_malloc)(size_t) = NULL;
    if (!real_malloc)
        real_malloc = dlsym(RTLD_NEXT, "malloc");

    void *p = real_malloc(size);
    fprintf(stderr, "malloc(%d) = %p\n", size, p);
    return p;
}

exemple que j'ai trouvé ici: http://www.jayconrod.com/cgi/view_post.py?2 post de Jay Conrod.

Mais ce que j'ai trouvé vraiment cool sur cette page, c'est que: l'éditeur de liens GNU fournit une option utile, - envelopper . Lorsque je vérifie "man ld", il y a l'exemple suivant:

void *
__wrap_malloc (size_t c)
{
    printf ("malloc called with %zu\n", c);
    return __real_malloc (c);
}

Je suis d'accord avec eux, c'est "un exemple trivial" :). Même dlsym n'est pas nécessaire.

Permettez-moi de citer une autre partie de ma page "man ld":

--wrap=symbol
       Use a wrapper function for symbol.
       Any undefined reference to symbol will be resolved to "__wrap_symbol".
       Any undefined reference to "__real_symbol" will be resolved to symbol.

J'espère que la description est complète et montre comment utiliser ces choses.

17

Désolé d'avoir rouvert un post de 7 ans.

Dans mon cas, j'avais besoin d'envelopper memalign/align_malloc sous malloc. Après avoir essayé d'autres solutions, j'ai fini par mettre en œuvre celle répertoriée ci-dessous. Cela semble bien fonctionner.

mymalloc.c .

/* 
 * Link-time interposition of malloc and free using the static
 * linker's (ld) "--wrap symbol" flag.
 * 
 * Compile the executable using "-Wl,--wrap,malloc -Wl,--wrap,free".
 * This tells the linker to resolve references to malloc as
 * __wrap_malloc, free as __wrap_free, __real_malloc as malloc, and
 * __real_free as free.
 */
#include <stdio.h>

void *__real_malloc(size_t size);
void __real_free(void *ptr);


/* 
 * __wrap_malloc - malloc wrapper function 
 */
void *__wrap_malloc(size_t size)
{
    void *ptr = __real_malloc(size);
    printf("malloc(%d) = %p\n", size, ptr);
    return ptr;
}

/* 
 * __wrap_free - free wrapper function 
 */
void __wrap_free(void *ptr)
{
    __real_free(ptr);
    printf("free(%p)\n", ptr);
}
9
user9869932

Voici un ensemble de fonctions d'encapsuleur que j'ai utilisées pendant des années (et que je fais toujours lorsque je plonge dans C) pour détecter la mémoire non libérée, la mémoire libérée plusieurs fois, les références à la mémoire libérée, les débordements/débordements de tampon et la libération de mémoire qui n'a pas été attribué.

ftp://ftp.digitalmars.com/ctools.Zip

Ils existent depuis 25 ans et ont fait leurs preuves.

Vous pouvez utiliser le préprocesseur de macro pour redéfinir malloc et libre d'utiliser ceux du package mem, mais je le déconseille, car il ne redirigera pas les appels de bibliothèque vers malloc comme ce que fait strdup.

5
Walter Bright

En C, la méthode que j'ai utilisée était similaire à:

#define malloc(x) _my_malloc(x, __FILE__, __LINE__)
#define free(x) _my_free(x)

Cela m'a permis de détecter la ligne et le fichier où la mémoire a été allouée sans trop de difficulté. Il doit être multiplateforme, mais rencontrera des problèmes si la macro est déjà définie (ce qui ne devrait être le cas que si vous utilisez un autre détecteur de fuite de mémoire.)

Si vous voulez implémenter la même chose en C++, la procédure est un peu plus complexe mais utilise la même astuce.

5

Si votre objectif est d'éliminer les fuites de mémoire, un moyen plus simple et moins intrusif consiste à utiliser un outil comme Valgrind (gratuit) ou Purifier (coûteux).

4
Don Wakefield

Si vous définissez vos propres fonctions pour malloc () et free () et que vous les liez explicitement à vos applications, vos fonctions doivent être utilisées de préférence à celles de la bibliothèque.

Cependant, votre fonction appelée "malloc" ne peut pas alors appeler la fonction malloc de la bibliothèque, car dans "c" il n'y a pas de concept d'espaces de noms séparés. En d'autres termes, vous devez implémenter les composants internes de malloc et vous libérer.

Une autre approche serait d'écrire les fonctions my_malloc () et my_free (), qui appellent celles de la bibliothèque standard. Cela signifierait que tout code appelant malloc devrait être modifié pour appeler vos fonctions my_xxx.

2
Roddy

Si vous ne parlez que de la mémoire que vous avez sous contrôle, c'est-à-dire que vous malloc et libre par vous-même, vous pouvez jeter un œil sur rmdebug . C'est probablement ce que vous allez écrire de toute façon, vous pouvez donc économiser un jour. Il a une licence très libérale, si cela doit être important pour vous.

Je l'utilise personnellement dans un projet, pour rechercher des fuites de mémoire, ce qui est bien, c'est qu'il est beaucoup plus rapide que valgrind, mais ce n'est pas si puissant que vous n'obtenez pas la pile d'appel complète.

0
quinmars

Si vous utilisez Linux, vous pouvez utiliser malloc_hook () (avec GNU glibc). Cette fonction vous permet de demander à malloc d'appeler votre fonction avant d'appeler le malloc réel. La page de manuel contient un exemple sur la façon de l'utiliser.

0
Sonja