Makefile为LD_PRELOAD __libc_start_main

时间:2016-02-14 09:52:18

标签: c++ makefile

我想要做的很简单。当我启动mongoose服务器时,我想创建一个额外的线程来做一些额外的工作。为此,我认为我需要LD_PRELOAD服务器的__libc_start_main

/* 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;
}

但是,我不知道如何为此编写Makefile。有人可以给我任何帮助吗?有什么我需要注意的吗?

1 个答案:

答案 0 :(得分:2)

关于makefile的问题的答案是你想要写一些类似的东西:

CFLAGS ?= -Wall -Wextra

all: mylib.so

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

使用隐式规则生成共享对象所需的.o文件。 (您还需要在CFLAGS中使用-fPIC for x86_64,并且可能需要-pthread以获得良好的度量,在LDFLAGS中使用-ldl)。

您的问题的症结可以大大简化。要在初始化期间尽早在LD_PRELOADed库中创建额外的线程,您只需执行以下操作:

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

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

使用gcc __attribute__扩展来注册在加载库期间自动调用的函数(即在main()被命中之前)。

另外,即使你想按照你所展示的方式进行操作,也可以通过使用伪库句柄RTLD_NEXT来查找下一个符号,库。

所以当你写道:

  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");

这可以写成:

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

这也让多个LD_PRELOAD库通过链接将相同的函数放在一起。

即使你确实想要出于某种原因明确地在libc上调用dlopen(尽管有RTLD_NEXT / RTLD_DEFAULT)我建议将RTLD_NOLOAD添加到用于打开它的标志中,这样你就不会结束在一些模糊的配置上有一个奇怪的错误,有两个不同的libc副本打开!