让一个动态链接库既可以执行,又可以被动态链接,下面是linux x86_64示例代码
#define OUT_STR "hello world\n"
long _write(long fd, char *buf, unsigned long len)
{
long ret;
__asm__ volatile(
"mov %0, %%rdi\n"
"mov %1, %%rsi\n"
"mov %2, %%rdx\n"
"mov $1, %%rax\n"
"syscall" : : "g"(fd), "g"(buf), "g"(len)
);
asm("mov %%rax, %0" : "=r"(ret));
return ret;
}
void Exit(long status)
{
__asm__ volatile("mov %0, %%rdi\n"
"mov $60, %%rax\n"
"syscall" : : "r"(status)
);
}
int entry_point()
{
_write(1, OUT_STR, sizeof(OUT_STR));
Exit(-1);
}
[root@dldl tmp]# gcc main.c -fPIC -pie -nostdlib -o main -Wl,-e,entry_point
[root@dldl tmp]# ./main
hello world
-fPIC 表示位置无关,-pie 表示生成elf类型为DYN但是可以执行的二进制,-nostdlib 表示不链接glibc库,-Wl,-e,entry_point (-e entry_point) 表示将entry_point函数设置为入口点
这里解释下为啥正常编译出来的动态库不能执行
gcc main.c -fPIC -shared -nostdlib -o main.so
上面是动态库的常规编译方法,通过这样编译出来的动态库 main.so 有两个不同点
所以,如果我们不加上 -pie 选项,我们也可以自己手动指定入口点和解释器
#define OUT_STR "hello world\n"
const char my_interp[] __attribute__((section(".interp"))) = "/lib64/ld-linux-x86-64.so.2";
void Exit(long status);
long _write(long fd, char *buf, unsigned long len);
int entry_point()
{
_write(1, OUT_STR, sizeof(OUT_STR));
Exit(-1);
}
long _write(long fd, char *buf, unsigned long len)
{
long ret;
__asm__ volatile(
"mov %0, %%rdi\n"
"mov %1, %%rsi\n"
"mov %2, %%rdx\n"
"mov $1, %%rax\n"
"syscall" : : "g"(fd), "g"(buf), "g"(len)
);
asm("mov %%rax, %0" : "=r"(ret));
return ret;
}
void Exit(long status)
{
__asm__ volatile("mov %0, %%rdi\n"
"mov $60, %%rax\n"
"syscall" : : "r"(status)
);
}
上面代码我们手动指定了解释器,然后把入口点 entry_point 函数放在另外两个函数的前面,那么它即位于 .text 的起始位置,为默认入口点位置
此时,我们正常编译,就可以正常运行
[root@dldl tmp]# gcc main.c -fPIC -shared -nostdlib -o main.so
[root@dldl tmp]# ./main.so
hello world
上面的做法,只是让一个类型为DYN的动态库可以被执行,但是里面的符号在链接时却找不到比如:(下面去掉 -nostdlib,即使用了glibc)
#include <stdio.h>
int add(int x, int y)
{
return x+y;
}
int main(int argc, char **argv)
{
printf("hello world\n");
return 0;
}
[root@dldl tmp]# gcc main.c -fPIC -pie -o main.so
[root@dldl tmp]# ./main.so
hello world
此时main.so已经为可执行的动态库,但是当我们其他的代码想要链接main.so时,发现找不到 add 符号,我们通过 readelf -s main.so 查看符号表,发现 .dynsym 节里面没有符号 add
所以按照上述方法,只能执行,但是不能被链接,解决办法很简单,编译时加上 -Wl,-E 即链接时,把所有的符号都导入到 .dynsym 动态符号表
gcc main.c -fPIC -pie -o main.so -Wl,-E
此时我们通过 readelf -s main.so 就能看到 .dynsym 里面有 add 符号了,就可以被正常链接了