如何使linux共享对象(库)自行运行?

15 c linux

注意到,gcc -shared创建一个可执行文件,我刚刚得到了奇怪的想法来检查时,我尝试运行它......好结果是发生了什么段错误我自己的库.因此,对此感到好奇,我试图"运行"glibc(/lib/x86_64-linux-gnu/libc.so.6在我的系统上).果然,它没有崩溃,但为我提供了一些输出:

GNU C Library (Debian GLIBC 2.19-18) stable release version 2.19, by Roland McGrath et al.
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.8.4.
Compiled on a Linux 3.16.7 system on 2015-04-14.
Available extensions:
    crypt add-on version 2.1 by Michael Glad and others
    GNU Libidn by Simon Josefsson
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<http://www.debian.org/Bugs/>.
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:这背后的魔力是什么?我不能只main在图书馆中定义一个符号 - 或者我可以吗?

jac*_*wah 17

我写了一篇关于这个主题的博客文章,我更深入地了解它,因为我觉得它很有趣.您可以在下面找到我的原始答案.


您可以使用-Wl,-e,entry_pointgcc选项为链接器指定自定义入口点,其中entry_point是库的"main"函数的名称.

void entry_point()
{
    printf("Hello, world!\n");
}
Run Code Online (Sandbox Code Playgroud)

链接器不希望链接的东西-shared作为可执行文件运行,并且必须为程序提供更多信息才能运行.如果您现在尝试运行库,则会遇到分段错误.

.interp部分是操作系统运行应用程序所需的结果二进制文件的一部分.如果-shared未使用,它将由链接器自动设置.如果要构建要自行执行的共享库,则必须在C代码中手动设置此部分.看到这个问题.

解释器的工作是查找并加载程序所需的共享库,准备程序运行,然后运行它.对于Linux上的ELF格式(普遍适用于现代*nix),使用该ld-linux.so程序.有关详细信息,请参阅手册页.

下面的行使用GCC属性在.interp部分中放置一个字符串.将它放在库的全局范围内,以明确告诉链接器您希望在二进制文件中包含动态链接器路径.

const char interp_section[] __attribute__((section(".interp"))) = "/path/to/ld-linux";
Run Code Online (Sandbox Code Playgroud)

找到路径的最简单方法ld-linux.soldd在任何普通应用程序上运行.我系统的示例输出:

jacwah@jacob-mint17 ~ $ ldd $(which gcc)
    linux-vdso.so.1 =>  (0x00007fff259fe000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007faec5939000)
    /lib64/ld-linux-x86-64.so.2 (0x00007faec5d23000)
Run Code Online (Sandbox Code Playgroud)

一旦你指定了解释器,你的库应该是可执行的!只有一个轻微的缺陷:它会在entry_point返回时发生段错误.

使用时编译程序时main,它不是执行时调用的第一个函数.main实际上是被另一个叫做的函数调用_start.此功能是负责建立argvargc等初始化.然后它打电话main.当main返回时,_start调用exit与返回值main.

堆栈中没有返回地址,_start因为它是第一个被调用的函数.如果它尝试返回,则会发生无效读取(最终导致分段错误).这正是我们的入口点函数中发生的事情.添加一个调用exit作为输入函数的最后一行,以正确清理而不是崩溃.

example.c

#include <stdio.h>
#include <stdlib.h>

const char interp_section[] __attribute__((section(".interp"))) = "/path/to/ld-linux";

void entry_point()
{
    printf("Hello, world!\n");
    exit(0);
}
Run Code Online (Sandbox Code Playgroud)

编译gcc example.c -shared -fPIC -Wl,-e,entry_point.