在 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 文件的信息之外还有其他用途。