Makefile for LD_PRELOAD __libc_start_main

Makefile for LD_PRELOAD __libc_start_main

本文关键字:start main libc PRELOAD LD Makefile for      更新时间:2023-10-16

我想做的很简单。当我启动猫鼬服务器时,我想创建一个额外的线程来做一些额外的工作。为此,我认为我需要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;
}

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

关于makefiles问题的答案是,你想写这样的东西:

CFLAGS ?= -Wall -Wextra
all: mylib.so
mylib.so: mylib.o 
    gcc -o $@ -shared $(CFLAGS) $(LDFLAGS) $^

它使用隐式规则来生成共享对象所需的 .o 文件。(你还需要 -fPIC 在 CFLAGS 中用于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副本!