hookwrapperglibc

Issue with overriding glibc malloc using --wrap option in statically linked application after glibc upgrade


After upgrading to the latest glibc, I noticed that malloc_hooks is deprecated, so I am trying to override certain glibc functions with user-defined ones using the --wrap symbol. For instance, I'm using --wrap=malloc to override the glibc malloc function with my custom wrap_malloc.

Direct calls to malloc in my code are correctly redirected to wrap_malloc. However, when calling glibc functions like fopen(), which internally invokes malloc, the internal malloc call is not being overridden by my wrap_malloc.

Does anyone have suggestions on how I can solve this issue and ensure that internal malloc calls (e.g., from fopen()) are overridden with my custom function? Any insights would be appreciated!

I've tried using the LD_PRELOAD option, but it doesn't seem to work because my application is built statically

Update :

As suggested I attempted to implement a custom memory allocation approach by declaring malloc() and free() as weak attributes in glibc malloc.c file:

//malloc.c
void free(void *mem) __attribute__((weak));
void* malloc(size_t size) __attribute__((weak));

also in the definition ,I defined it as follows: as malloc() is aliased to __libc_malloc

void* __attribute__((weak)) __libc_malloc(size_t bytes) {
    // code
}

However, I added debug prints to my custom malloc and free to test, but I don't see those prints, indicating that calls to malloc are not invoking my custom implementation.

the compilation command to build my application, looks like this:

$(CXX) -Wl,--verbose -o $@ -I$(SSL_NOTHREADS_INCDIR) -L$(SSL_NOTHREADS_LIBDIR) `sn-linktool $+` $(NPU_IXP_LIBS) $(NP4C_NLM11K_LIBS) $(BOXER_STATIC_LIBS) -lsn_memacct -lm -ldl -ltirpc -lssp -lc -Wl,--allow-multiple-definition $(STATIC_NSS_LIBS_CXX)

In this command, my custom malloc is part of libsn_memacct.a, which is linked with -lsn_memacct, while malloc.c from glibc is linked through -lc. Even though I defined glibc's malloc as a weak symbol, it seems my custom malloc is not overriding it.


Solution

  • For fully-static linking, your best (and likely only) choice is to replace the entire libc.a(malloc.o) with an alternate implementation.

    Note that you must replace all the symbols in malloc.o, or your link will fail with multiply-defined symbols.

    For example, on Fedora 40, the following "works":

    #include <stdio.h>
    #include <string.h>
    
    static char buf[10000] __attribute__((aligned(16)));
    static char *mptr = &buf[0];
    
    void *malloc(size_t sz)
    {
      char *r = mptr;
      mptr += (sz + 0xF) & ~0xF;
      fprintf(stderr, "malloc(%zu) -> %p\n", sz, r);
      return r;
    }
    
    void free(void *p)
    {
      fprintf(stderr, "free(%p)\n", p);
    }
    
    void *calloc(size_t nelem, size_t sz)
    {
      void *r = malloc(nelem * sz);
      fprintf(stderr, "calloc(%zu, %zu) -> %p\n", nelem, sz, r);
      return r;
    }
    
    void *realloc(void *p, size_t sz)
    {
      char *r = malloc(sz);
      memcpy(r, p, sz);
      fprintf(stderr, "realloc(%p, %zu) -> %p\n", p, sz, r);
      return r;
    }
    
    int main(int argc, char *argv[])
    {
      fprintf(stderr, "  main starts\n");
      if (argc < 2) return 0;
      fprintf(stderr, "  fopen(%s)\n", argv[1]);
      FILE *fp = fopen(argv[1], "w");
      fprintf(stderr, "  fopen(%s) -> %p\n", argv[1], fp);
    
      if (fp == NULL) return 1;
    
    
      fprintf(stderr, "  fclose(%p)\n", fp);
      fclose(fp);
    
      fprintf(stderr, "  main returns\n");
      return 0;
    }
    
    gcc -Wall -Wextra -static t.c && ./a.out foo.txt
    malloc(6) -> 0x49d1b0
    malloc(1241) -> 0x49d1c0
    calloc(1241, 1) -> 0x49d1c0
    malloc(24) -> 0x49d6a0
    malloc(160) -> 0x49d6c0
    malloc(160) -> 0x49d760
    malloc(256) -> 0x49d800
    malloc(256) -> 0x49d900
      main starts
      fopen(foo.txt)
    malloc(472) -> 0x49da00
      fopen(foo.txt) -> 0x49da00
      fclose(0x49da00)
    free(0x49da00)
      main returns
    free(0x49d900)
    free(0x49d800)
    

    Above you can clearly see malloc being called from fopen and free being called from fclose.

    Update:

    In my wrap_malloc function, after handling some memory accounting, I want to call the original malloc using __real_malloc.

    In that case the solution above will not work. But you can make it work by doing this:

    cat > to-weaken <<EOF
    malloc
    realloc
    calloc
    free
    EOF
    
    objcopy --weaken-symbols=to-weaken malloc.o malloc2.o
    

    Here it is all together:

    #include <stdio.h>
    
    extern void *__malloc(size_t),
                *__realloc(void *p, size_t),
                *__calloc(size_t, size_t);
    extern void __free(void*);
    
    void *malloc(size_t sz)
    {
      void *r = __malloc(sz);
      fprintf(stderr, "malloc(%zu) -> %p\n", sz, r);
      return r;
    }
    
    void free(void *p)
    {
      fprintf(stderr, "free(%p)\n", p);
      __free(p);
    }
    
    void *calloc(size_t nelem, size_t sz)
    {
      void *r = __calloc(nelem, sz);
      fprintf(stderr, "calloc(%zu, %zu) -> %p\n", nelem, sz, r);
      return r;
    }
    
    void *realloc(void *p, size_t sz)
    {
      char *r = __realloc(p, sz);
      fprintf(stderr, "realloc(%p, %zu) -> %p\n", p, sz, r);
      return r;
    }
    
    int main(int argc, char *argv[])
    {
      fprintf(stderr, "  main starts\n");
      if (argc < 2) return 0;
      fprintf(stderr, "  fopen(%s)\n", argv[1]);
      FILE *fp = fopen(argv[1], "w");
      fprintf(stderr, "  fopen(%s) -> %p\n", argv[1], fp);
    
      if (fp == NULL) return 1;
    
    
      fprintf(stderr, "  fclose(%p)\n", fp);
      fclose(fp);
    
      fprintf(stderr, "  main returns\n");
      return 0;
    }
    
    gcc -Wall -Wextra -static t.c malloc2.o && ./a.out foo.txt
    
    malloc(6) -> 0x1279fa0
    calloc(1241, 1) -> 0x1279fc0
    malloc(24) -> 0x127a4b0
    malloc(160) -> 0x127a4d0
    malloc(160) -> 0x127a580
    malloc(256) -> 0x127a630
    malloc(256) -> 0x127a740
      main starts
      fopen(foo.txt)
    malloc(472) -> 0x127a850
      fopen(foo.txt) -> 0x127a850
      fclose(0x127a850)
    free(0x127a850)
      main returns
    free(0x127a740)
    free(0x127a630)