Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Makefile for LD_PRELOAD __libc_start_main

Tags:

c++

makefile

What I am trying to do is quite simple. When I start the mongoose server, I want to create an additional thread to do some extra work. To do this, I think I need to LD_PRELOAD the __libc_start_main of the server.

/* This is spec_hooks.cpp */
typedef int (*main_type)(int, char**, char**);

struct arg_type
{
  char **argv;
  int (*main_func) (int, char **, char **);
};

main_type saved_init_func = NULL;
void tern_init_func(int argc, char **argv, char **env){
  dprintf("%04d: __tern_init_func() called.\n", (int) pthread_self());
  if(saved_init_func)
    saved_init_func(argc, argv, env);
  __tern_prog_begin(); //create a new thread in this function
}

extern "C" int my_main(int argc, char **pt, char **aa)
{
  int ret;
  arg_type *args = (arg_type*)pt;
  dprintf("%04d: __libc_start_main() called.\n", (int) pthread_self());
  ret = args->main_func(argc, args->argv, aa);
  return ret;
}

extern "C" int __libc_start_main(
  void *func_ptr,
  int argc,
  char* argv[],
  void (*init_func)(void),
  void (*fini_func)(void),
  void (*rtld_fini_func)(void),
  void *stack_end)
{
  typedef void (*fnptr_type)(void);
  typedef int (*orig_func_type)(void *, int, char *[], fnptr_type,
                                fnptr_type, fnptr_type, void*);
  orig_func_type orig_func;
  arg_type args;

  void * handle;
  int ret;

  // Get lib path.
  Dl_info dli;
  dladdr((void *)dlsym, &dli);
  std::string libPath = dli.dli_fname;
  libPath = dli.dli_fname;
  size_t lastSlash = libPath.find_last_of("/");
  libPath = libPath.substr(0, lastSlash);
  libPath += "/libc.so.6";
  libPath = "/lib/x86_64-linux-gnu/libc.so.6";

  if(!(handle=dlopen(libPath.c_str(), RTLD_LAZY))) {
    puts("dlopen error");
    abort();
  }

  orig_func = (orig_func_type) dlsym(handle, "__libc_start_main");

  if(dlerror()) {
    puts("dlerror");
    abort();
  }

  dlclose(handle);

  dprintf("%04d: __libc_start_main is hooked.\n", (int) pthread_self());

  args.argv = argv;
  args.main_func = (main_type)func_ptr;
  saved_init_func = (main_type)init_func;

  saved_fini_func = (fini_type)rtld_fini_func;
  ret = orig_func((void*)my_main, argc, (char**)(&args),
                  (fnptr_type)tern_init_func, (fnptr_type)fini_func,
                  rtld_fini_func, stack_end);
  return ret;
}

However, I have no idea how to write the Makefile for this. Can someone give me any help? Is there anything I need to pay attention to?

like image 359
HuangJie Avatar asked Jun 13 '26 13:06

HuangJie


1 Answers

The answer to your question about makefiles is that you want to write something like:

CFLAGS ?= -Wall -Wextra

all: mylib.so

mylib.so: mylib.o 
    gcc -o $@ -shared $(CFLAGS) $(LDFLAGS) $^

Which uses an implicit rule to generate the .o files required by your shared object. (You'll also need -fPIC in CFLAGS for x86_64 and probably -pthread as well for good measure, with -ldl in LDFLAGS).


The crux of your problem can be simplified substantially. To create an extra thread in an LD_PRELOADed library early during initialization you can simply do:

static void start_my_thread() __attribute__((constructor));

static void start_my_thread() {
    // Call pthread_create here and then return
}

Which uses the gcc __attribute__ extension to register the function to be called automatically during loading of the library (i.e. before main() gets hit).


As a further aside, even if you wanted to do it the way you've shown you can simplify it substantially, by using the pseudo library handle RTLD_NEXT to find the next occurrence of a symbol, after the current library.

So when you wrote:

  Dl_info dli;
  dladdr((void *)dlsym, &dli);
  std::string libPath = dli.dli_fname;
  libPath = dli.dli_fname;
  size_t lastSlash = libPath.find_last_of("/");
  libPath = libPath.substr(0, lastSlash);
  libPath += "/libc.so.6";
  libPath = "/lib/x86_64-linux-gnu/libc.so.6";

  if(!(handle=dlopen(libPath.c_str(), RTLD_LAZY))) {
    puts("dlopen error");
    abort();
  }

  orig_func = (orig_func_type) dlsym(handle, "__libc_start_main");

That can just be written as:

orig_func = (orig_func_type) dlsym(RTLD_NEXT, "__libc_start_main");

Which also lets multiple LD_PRELOAD libraries interpose on the same functions together by chaining them.


And even if you really did want to call dlopen on libc explicitly for some reason (despite RTLD_NEXT/RTLD_DEFAULT) I'd recommend adding RTLD_NOLOAD into the flags you use to open it with so you don't end up with a weird bug on some obscure configuration of having two different copies of libc open!

like image 151
Flexo Avatar answered Jun 15 '26 02:06

Flexo