Skip to content

cc.php: Clarify weak symbols and symbol interposition #45

Description

Reference:

Weak symbols and libc

Most libc implementations declare their methods "weak" so users can intercept them. This is not always as convenient as it seems. Let's look at how to intercept malloc.

// mymalloc.c

#define _GNU_SOURCE // Could have been defined with -D on command-line

#include "stddef.h"
#include "dlfcn.h"
#include "stdio.h"
#include "stdlib.h"


void* malloc(size_t sz) {
  void *(*libc_malloc)(size_t) = dlsym(RTLD_NEXT, "malloc");
  printf("malloced %zu bytes\n", sz);
  return libc_malloc(sz);
}

int main() {
  char* x = malloc(100);
  return 0;
}

This program will enter an infinite loop until it segfaults. This is because dlsym calls malloc.

$ clang mymalloc.c
$ ./a.out
Segmentation fault (core dumped)

For such cases, GNU's libc used to provide special hooks such as __malloc_hook...but they became deprecated. Now the best way is to MITM via the loader and LD_PRELOAD.

 // mtrace.c

#include <stdio.h>
#include <dlfcn.h>

static void* (*real_malloc)(size_t) = nullptr;

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

    printf("malloc(%d) = ", size);
    return real_malloc(size);
}
$ clang -shared -fPIC -D_GNU_SOURCE -o mtrace.so mtrace.c
$ LD_PRELOAD=./mtrace.so ls
malloc(472) = 0xaaab24e4b2a0
malloc(120) = 0xaaab24e4b480
malloc(1024) = 0xaaab24e4b500
malloc(5) = 0xaaab24e4b910
...
$

Trivia: Weak symbols are also paramount for C++ and especially the STL (see below).

  1. Both mymalloc.c and mtrace.c demonstrate symbol interposition, but not the use of weak symbols. Specifically, malloc is defined as a strong symbol in glibc (malloc.c). As noted in the manual:

    The GNU C Library supports replacing the built-in malloc implementation with a different allocator with the same interface. For dynamically linked programs, this happens through ELF symbol interposition, either using shared object dependencies or LD_PRELOAD. For static linking, the malloc replacement library must be linked in before linking against libc.a (explicitly or implicitly).

  2. Based on my testing, both mymalloc.c and mtrace.c crash at runtime due to recursion caused by printf (but not dlsym). This happens because printf internally calls malloc (filedoalloc.c), while dlsym only calls malloc in error-handling paths (dlerror.c). Replacing:

    printf("malloced %zu bytes\n", sz);

    with:

    char buffer[50];
    sprintf(buffer, "malloced %zu bytes\n", sz);
    write(STDOUT_FILENO, buffer, strlen(buffer));

    allows mymalloc.c to run successfully (CE). The same applies to mtrace.c.

  3. To match the output shown above, the format string used in mtrace.c should be (with the changes from item 2 applied):

    sprintf(buffer, "malloc(%d) = %p\n", size, ptr);

    where ptr is the return value of real_malloc(size).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions