Skip to content

Instantly share code, notes, and snippets.

@aprell
Created March 7, 2019 19:01
Show Gist options
  • Select an option

  • Save aprell/b3c6e7877cf1a228a0166d0065a792ad to your computer and use it in GitHub Desktop.

Select an option

Save aprell/b3c6e7877cf1a228a0166d0065a792ad to your computer and use it in GitHub Desktop.
Wrapping library functions using LD_PRELOAD
CC := gcc
CFLAGS += -Wall -Wextra -std=c99
LDLIBS += -lpthread
all: test_preload preload.so
# Make sure that `num_threads` is added to the dynamic symbol table
test_preload: LDFLAGS += -Wl,--export-dynamic
test_preload: test_preload.c
preload.so: preload.c
$(CC) $(CFLAGS) $(CPPFLAGS) -shared -fPIC $< -o $@ -ldl
clean:
rm -rf test_preload preload.so
.PHONY: all clean
#define _GNU_SOURCE
#include <dlfcn.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
extern unsigned int num_threads;
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
{
int (*real_pthread_create)(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *);
char *error;
num_threads++;
// From the docs:
// There are two special pseudo-handles, RTLD_DEFAULT and RTLD_NEXT. The
// former will find the first occurrence of the desired symbol using the
// default library search order. The latter will find the next occurrence
// of a function in the search order after the current library. This allows
// one to provide a wrapper around a function in another shared library.
// (...) The symbols RTLD_DEFAULT and RTLD_NEXT are defined by <dlfcn.h>
// only when _GNU_SOURCE was defined before including it.
*(void **)&real_pthread_create = dlsym(RTLD_NEXT, "pthread_create");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
return real_pthread_create(thread, attr, start_routine, arg);
}
int pthread_join(pthread_t thread, void **retval)
{
int (*real_pthread_join)(pthread_t, void **);
char *error;
num_threads--;
*(void **)&real_pthread_join = dlsym(RTLD_NEXT, "pthread_join");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
return real_pthread_join(thread, retval);
}
#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
unsigned int num_threads;
static void *thread_entry_fn(void *arg)
{
(void)arg;
return NULL;
}
int main(int argc, char *argv[])
{
unsigned int n = argc > 1 && atoi(argv[1]) >= 0 ? atoi(argv[1]) : 4;
pthread_t *threads = malloc(n * sizeof(pthread_t));
if (!threads) return EXIT_FAILURE;
for (unsigned int i = 0; i < n; i++) {
pthread_create(&threads[i], NULL, thread_entry_fn, NULL);
}
assert(num_threads == n);
for (unsigned int i = 0; i < n; i++) {
pthread_join(threads[i], NULL);
}
assert(num_threads == 0);
free(threads);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment