所有讨论均针对 x86。
如果我写了一个简单的 hello 程序,如下所示:
#include <stdio.h>
int main(){
printf("Hello\n");
return 0;
}
并在我的电脑上用ubuntu编译它
gcc -shared -mPIC -o hello_new hello.c
然后,当我尝试执行
hello_new
时,它会给我分段错误。当我将此二进制文件移动到 Android 手机时出现同样的错误。 不过,我可以将其编译为带有静态链接 libc 的二进制文件,并在 Android 手机上运行。
是的,我想直接执行共享对象。
原因是我最近收到了一个别人编译的Linux文件。当我使用
file
和 readelf
分析文件时,他们说它是一个共享对象(32 位,使用 -m32 编译)。但我可以像 Android 中的可执行文件一样执行共享对象:
./hello
这真的让我很困惑。共享对象文件包含
printf
函数调用(不确定是静态链接还是动态链接)。但由于它可以通过 adb 在 Android 上运行,我假设它与 libc 静态链接。
什么样的编译技术可以直接执行共享对象?
恰巧我目前正在做这类事情。 Linux 下可执行文件和共享对象之间的主要区别之一是可执行文件具有解释器和(有效)入口点。 例如,在一个最小的程序上:
$ echo 'int main;' | gcc -xc -
如果你看一下它的 elf 程序头:
$ readelf --program-headers a.out
...
INTERP 0x0000000000000200 0x0000000000400200 0x0000000000400200
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
...
解释器程序负责程序的执行,为了实现这一点,它将执行一些初始化,例如加载所需的共享对象。事实上,它与脚本 shebang 非常相似,只不过是针对 elf 文件。 在本例中,/lib64/ld-linux-x86-64.so.2 是 amd64 的加载程序。您可以拥有多个加载器:例如,一个用于 32 位,一个用于 64。
现在是切入点:
$ readelf --file-header a.out
ELF Header:
...
Entry point address: 0x4003c0
...
$ readelf -a a.out | grep -w _start
57: 00000000004003c0 0 FUNC GLOBAL DEFAULT 13 _start
默认情况下,您可以看到_start被定义为入口点。
因此,如果您考虑以下最小示例:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef INTERPRETER
const char interp[] __attribute__((section(".interp"))) = INTERPRETER;
#endif /* INTERPRETER */
void entry_point(void) {
fprintf(stderr, "hello executable shared object world !\n");
_exit(EXIT_SUCCESS);
}
如果将其编译为“普通”共享对象并执行它:
$ gcc libexecutable.c -Wall -Wextra -fPIC -shared -o libexecutable.so
$ ./libexecutable.so
Erreur de segmentation
你可以看到它存在段错误。但是现在,如果您定义一个解释器(将其路径调整为 readelf --program-headers 之前给您的路径)并告诉链接器您的入口点是什么:
$ gcc libexecutable.c -Wall -Wextra -fPIC -shared -o libexecutable.so -DINTERPRETER=\"/lib64/ld-linux-x86-64.so.2\" -Wl,-e,entry_point
$ ./libexecutable.so hello executable shared object world !
现在可以了。请注意,必须调用 _exit() 以避免执行结束时出现段错误。
但最后,请记住,因为您指定了自定义入口点,所以您将绕过 libc 初始化步骤,这可能需要或不需要,具体取决于您的需要。
我认为你的android和pc同时都是x86或arm,否则可执行文件不应该在两个平台上运行。现在,要同时使共享库可执行,您可以使用 gcc 的 -pie 命令行选项。详细信息可以在这个answer中找到。