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

Ho1*_*Ho1 70 linux version executable glibc shared-library

在 32 位 Linux 系统上,调用这个

$ /lib/libc.so.6
Run Code Online (Sandbox Code Playgroud)

而在 64 位系统上,这

$ /lib/x86_64-linux-gnu/libc.so.6
Run Code Online (Sandbox Code Playgroud)

在 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>.
Run Code Online (Sandbox Code Playgroud)

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

我看着/usr/lib寻找可执行文件,我找到了/usr/lib/libvlc.so.5.5.0. 运行它会导致分段错误。:-/

gol*_*cks 65

该库有一个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;                       
}                   
Run Code Online (Sandbox Code Playgroud)

编译:

gcc -fPIC -pie -o libtest.so test.c -Wl,-E
Run Code Online (Sandbox Code Playgroud)

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

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

> ./libtest.so 
./libtest.so: Hello!
Run Code Online (Sandbox Code Playgroud)

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

gcc -fPIC -pie -o libtest.so test.c -Wl,-E
Run Code Online (Sandbox Code Playgroud)

使用使extern我们不必创建标题。现在编译:

gcc program.c -L. -ltest
Run Code Online (Sandbox Code Playgroud)

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

export LD_LIBRARY_PATH=./
Run Code Online (Sandbox Code Playgroud)

现在:

> ./a.out
Test program.
./a.out: Hello!
Run Code Online (Sandbox Code Playgroud)

并且ldd a.out将显示联动libtest.so

请注意,我怀疑这就是 glibc 的实际编译方式,因为它可能不像 glibc 本身那样可移植(参见man gcc关于-fPIC-pie开关),但它演示了基本机制。对于真正的细节,您必须查看源生成文件。


IBr*_*IBr 24

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

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

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

让我们深入了解makefile。在链接器标志中有一个有趣的标志:

# Give libc.so an entry point and make it directly runnable itself.
LDFLAGS-c.so += -e __libc_main
Run Code Online (Sandbox Code Playgroud)

所以这是设置库入口点的链接器标志。在构建库时,您可以为-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. 
Run Code Online (Sandbox Code Playgroud)

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

开始 = 0x2020;

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

开始 = other_symbol ;

(当前文档可以在这里找到)

ld链接实际上做,如果你提供的命令行选项来创建与入口点函数的可执行文件-e(这是最流行的解决方案),提供一个函数符号start,或spcify一个符号地址汇编。

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