为什么某些共享库是可执行文件,为什么以及如何运行?


55

在32位Linux系统上,调用此

$ /lib/libc.so.6

在64位系统上

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

在外壳中,提供如下输出:

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。运行它导致分段错误。:-/


除了以下所有答案之外,我还记得,如果您在共享库上设置了x位,即使r位被清除,也可以(也许仍然)从可执行文件中加载它。曾经被认为是一种很好的安全措施,它可以将世界上的系统可执行文件和库放逐。由于开源的广泛存在,这实际上仅适用于匿名ftp目录的/ bin / ls。对我而言,将x位置保留似乎是旧做法的面貌。
约书亚

Answers:


52

该库具有一个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。


1
好答案,谢谢!:-)我试图nm在共享库上使用,但是它不是调试版本。那么,为什么libvlc和其他崩溃呢?
2015年

1
因为大多数共享库都不是可执行的,所以GNU libc是一个例外。
goldilocks 2015年

我发现了另外两个:ldlibpthread
Ho1 2015年

@ Ho1 ld.so在其他方面很特殊。在某种程度上,它比普通的动态链接的可执行文件更像是一个真正的可执行文件。
Random832

1
以上选项即使创建了可执行文件共享库,但从某种意义上说是不完整的,当某些可执行文件尝试链接到该库时,它会标记错误。在此处添加了详细的示例参考:unix.stackexchange.com/a/479334/152034
parasrish '18

21

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

在同一个文件中,有几个有趣的地方: __libc_print_version该功能提供了到stdin的相同文本和符号的打印,__libc_main (void)这些文本和符号被记录为入口点。因此,在运行库时将调用此符号。

那么链接器/编译器如何知道这正是入口点功能?

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

# 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;

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

开始= other_symbol;

(当前文档可在此处找到)

真正ld链接程序,如果你为它提供的命令行选项创建入口点函数的可执行文件-e(最实用的解决方案),提供函数符号start,或汇编注入符号地址。

但是请注意,显然不能保证与其他链接程序一起使用(我不知道llvm的lld是否具有相同的标志)。我不知道为什么除了提供有关此文件的信息外,这还应该用于其他目的。


1
如果是python,它将提供单元测试。
埃里克·阿隆斯蒂
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.