为什么某些共享库可以运行以及如何运行,就好像它们是可执行文件一样?

为什么某些共享库可以运行以及如何运行,就好像它们是可执行文件一样?

在 32 位 Linux 系统上,调用此

$ /lib/libc.so.6

在 64 位系统上

$ /lib/x86_64-linux-gnu/libc.so.6

在 shell 中,提供如下输出:

GNU C Library stable release version 2.10.1, by Roland McGrath et al.
Copyright (C) 2009 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.4.0 20090506 (Red Hat 4.4.0-4).
Compiled on a Linux >>2.6.18-128.4.1.el5<< system on 2009-08-19.
Available extensions:
    The C stubs add-on version 2.1.2.
    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
    RT using linux kernel aio
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.

为什么会发生这种情况以及如何发生,以及如何在其他共享库中执行相同的操作?

我寻找/usr/lib可执行文件,发现了/usr/lib/libvlc.so.5.5.0.运行它导致了分段故障。 :-/

答案1

该库有一个main()函数或等效的入口点,并且以既可用作可执行文件又可用作共享对象的方式进行编译。

这是一个建议关于如何做到这一点,尽管它对我不起作用。

这是另一个在SO 上类似问题的答案,我会无耻地抄袭、调整并添加一些解释。

首先,我们的示例库的来源test.c

#include <stdio.h>                  

void sayHello (char *tag) {         
    printf("%s: Hello!\n", tag);    
}                                   

int main (int argc, char *argv[]) { 
    sayHello(argv[0]);              
    return 0;                       
}                   

编译一下:

gcc -fPIC -pie -o libtest.so test.c -Wl,-E

在这里,我们正在编译一个共享库 ( -fPIC),但告诉链接器它是一个常规可执行文件 ( -pie),并使其符号表可导出 ( -Wl,-E),以便可以有效地链接它。

而且,虽然file会说它是一个共享对象,但它确实可以作为可执行文件工作:

> ./libtest.so 
./libtest.so: Hello!

现在我们需要看看是否真的可以动态链接。一个示例程序program.c

#include <stdio.h>

extern void sayHello (char*);

int main (int argc, char *argv[]) {
    puts("Test program.");
    sayHello(argv[0]);
    return 0;
}

使用extern它可以让我们不必创建标头。现在编译一下:

gcc program.c -L. -ltest

在执行之前,我们需要添加libtest.so动态加载器的路径:

export LD_LIBRARY_PATH=./

现在:

> ./a.out
Test program.
./a.out: Hello!

并将ldd a.out显示到 的链接libtest.so

请注意,我怀疑这就是 glibc 的实际编译方式,因为它可能不如 glibc 本身那么可移植(请参阅man gcc-fPIC开关-pie),但它演示了基本机制。要了解真正的细节,您必须查看源 makefile。

答案2

让我们在 GitHub 上的随机 glibc 存储库中寻找答案。此版本在文件中提供了“横幅”version.c

在同一个文件中,有一些有趣的点:__libc_print_version将文本打印到标准输出的函数和__libc_main (void)记录为入口点的符号。所以运行库时会调用这个符号。

那么链接器或编译器如何准确地知道这是入口点函数呢?

让我们深入探讨生成文件。在链接器标志中有一个有趣的标志:

# Give libc.so an entry point and make it directly runnable itself.
LDFLAGS-c.so += -e __libc_main

这是用于设置库入口点的链接器标志。构建库时,您可以-e function_name为链接器提供标志以启用可执行行为。它到底有什么作用?让我们看看手册(有些过时但仍然有效):

链接器命令语言包括专门用于定义输出文件中的第一可执行指令(其入口点)的命令。它的参数是一个符号名称:

输入(符号)

与符号分配一样,ENTRY 命令可以作为独立命令放置在命令文件中,也可以放置在 SECTIONS 命令内的节定义中 - 无论什么对您的布局最有意义。

ENTRY 只是选择切入点的几种方法之一。您可以通过以下任何一种方式来指示它(按优先级降序显示:列表中较高的方法覆盖较低的方法)。

the `-e' entry command-line option;
the ENTRY(symbol) command in a linker control script;
the value of the symbol start, if present;
the address of the first byte of the .text section, if present;
The address 0. 

例如,您可以使用这些规则生成带有赋值语句的入口点:如果您的输入文件中没有定义符号开始,您可以简单地定义它,为其分配适当的值——

开始= 0x2020;

该示例显示了绝对地址,但您可以使用任何表达式。例如,如果您的输入目标文件使用某些其他符号名称约定作为入口点,您可以只分配包含要开始的起始地址的任何符号的值:

开始=其他符号;

(当前文档可以找到这里

ld如果您提供命令行选项-e(这是最流行的解决方案)、提供函数符号start或为汇编器指定符号地址,链接器实际上会创建带有入口点函数的可执行文件。

但请注意,它不能保证与其他链接器一起工作(我不知道 llvm 的 lld 是否具有相同的标志)。我不知道为什么这除了提供有关 SO 文件的信息之外还有其他用途。

相关内容