Linux下的so文件通常是作为动态链接库使用的,但其实so文件跟可执行程序一样都是ELF格式,所以应该都是可以直接执行的。 在Linux下的很多so库文件都是可以直接执行的,不过通常只是打印出编译信息。例如:
/lib64/ld-linux-x86-64.so.2
还有
/lib/x86_64-linux-gnu/libpthread.so.0
还有
/lib/x86_64-linux-gnu/libc.so.6
特别说明一下,我的系统为Ubuntu 10.04 64bit版本。
要让一个so文件可直接执行,只需要在编译时指定入口函数即可,否则强制执行一个so文件会导致出core。gcc和g++,即C和C++在实现步骤上略有不同。
例如有源文件service.c
#include <stdio.h>
#include <stdlib.h>
const char service_interp[] __attribute__((section(".interp"))) = "/lib64/ld-linux-x86-64.so.2";
void lib_service()
{
printf("This is a service of the shared library\n");
}
void lib_entry()
{
printf("Entry point of the service library\n");
exit(0);
}
注意,service_interp变量指明了ld.so的位置,不同的Linux系统略有不同,函数lib_entry()必须以exit(0)
结束。 编译指令:
gcc -shared service.c -o libservice.so -Wl,-e,lib_entry -fPIC
-Wl
表示传递给链接器ld的参数,分隔的逗号会被替换成空格。-e,lib_entry
就指明了入口函数。 编译生成的libservice.so就可以直接执行了:
ubuntu:/tmp/service$ ./libservice.so
Entry point of the service library
当然,这丝毫不会影响libservice.so作为动态链接库的功能。
上面的例子是编译C代码,如果要编译C++代码,基本没有什么不同,除了使用g++外,就是得注意函数名的重构问题。 有两种处理方式,其一,使用extern "C"
迫使函数名保持原样,其二,先使用g++ -c
将代码编译成.o
文件, 再使用readelf -s service.o
来查看重构之后的函数名,在编译时使用重构后的函数名,例如:-Wl,-e,_Z10test_entryv
。
再值得注意一点的是,入口函数及其所调用的函数不能使用C++的输入输出类,即不能使用cout、cin之类,否则就会core。 使用C函数进行输入输出操作是没有问题的,我觉得这个问题是可以解决的,但我目前没有找到问题在哪,先留个TODO在此。
将程序的核心逻辑进行so化是有相当多好处的,比如可以进行多实例部署,可以将词典资源抽离出来,可以加快服务拉起, 可以充分利用机器资源等。而给so文件直接可执行的功能,就可以方便地了解so文件编译信息、版本信息、使用方法等。
P.S.在此感谢此文How to make executable shared libraries。