问题描述
我想要做的很简单.当启动猫鼬服务器时,我想创建一个额外的线程来做一些额外的工作.为此,我认为我需要LD_PRELOAD
服务器的__libc_start_main
.
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;
}
但是,我不知道如何为此编写Makefile.有人可以给我任何帮助吗?我有什么需要注意的吗?
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?
推荐答案
关于makefile的问题的答案是,您想要编写类似以下内容的文件:
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) $^
使用隐式规则来生成共享对象所需的.o文件. (对于x86_64,您还需要CFLAGS中的-fPIC,为了更好地使用它,可能还需要-pthread,而在LDFLAGS中则需要-ldl.)
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).
您的问题的症结可以大大简化.要在初始化期间尽早在LD_PRELOADed库中创建一个额外的线程,您可以执行以下操作:
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
}
使用gcc __attribute__
扩展名注册要在加载库期间(即在main()
被命中之前)自动调用的功能.
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).
此外,即使您想要按照显示的方式进行操作,也可以使用伪库句柄RTLD_NEXT
查找当前库之后的下一个出现的符号,从而对其进行实质性的简化.
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.
所以当你写的时候:
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库插入到同一函数中.
Which also lets multiple LD_PRELOAD libraries interpose on the same functions together by chaining them.
即使您确实出于某种原因确实想在libc上显式调用dlopen
(尽管有RTLD_NEXT/RTLD_DEFAULT),我还是建议您将RTLD_NOLOAD添加到用来打开它的标志中,这样您最终就不会在打开两个不同的libc副本的一些晦涩的配置上出现了一个奇怪的错误!
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!
这篇关于LD_PRELOAD __libc_start_main的Makefile的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!