【问题标题】:Executing share object file执行共享对象文件
【发布时间】:2015-07-20 02:10:42
【问题描述】:

所有讨论都针对 x86。

如果我写了一个简单的 hello 程序,如下所示:

#include <stdio.h>

int main(){
  printf("Hello\n");
  return 0;
}

然后在我的电脑上用 ubuntu 编译它

$gcc -shared -mPIC -o hello_new hello.c

然后当我尝试执行hello_new 时它会给我分段错误。当我将此二进制文件移动到安卓手机时,同样的错误。 (但我可以用静态链接的 libc 将其编译为二进制文件并在 android 手机上运行)

是的,我想直接执行一个共享对象。

原因如下: 我最近得到了一个别人编译的 linux 文件。当我使用 linux 命令filereadelf 分析文件时。它说它是一个共享对象(32 位,用 -m32 编译)。但是我可以像在手机上的 android 中的可执行文件一样执行共享对象:

$./hello

这真的让我很困惑。此共享对象文件包含 printf 函数调用,不确定是静态链接还是动态链接。但由于它可以通过 ADB 在 Android 上运行,我假设它与 libc 静态链接。

什么样的编译技术可以让我们直接执行共享对象?

【问题讨论】:

  • 您的gcc 是否设置为针对ARM 进行编译?我不认为您可以将 x86 可执行文件移植到 ARMv7a 设备上并期望它能够正常工作。在 x86 android 设备(例如模拟器)上尝试,或者尝试针对目标处理器的架构进行编译。
  • @CPUTerminator,都是 x86。
  • 好吧,我没有太多坚实的基础可以继续,也许 gcc 正在使用不受支持的指令/扩展,而不是在 Android 上。看看这个link 虽然方法 1 似乎与您的方法相同,但它针对的是 ARM 架构。也许尝试方法 2 和/或 3?
  • @CPUTerminator,你还需要什么额外的信息?
  • 对不起,我措辞不好。我的意思是说我脑子里没有更多的东西(因为我缺乏这个特定方面/主题的知识)。您提供的信息非常完整。查看我发送的链接,它准确地描述了(我的想法)你想要做什么(但对于 ARM,对于 x86 应该是相同的)。

标签: android c linux


【解决方案1】:

碰巧我目前正在做这类事情。 Linux 下可执行文件和共享对象之间的主要区别之一是可执行文件具有解释器和(有效)入口点。 例如,在一个最小的程序上:

$ echo 'int main;' | gcc -xc -

如果你看它是精灵程序头:

$ 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 初始化步骤,具体取决于您的需要。

【讨论】:

  • 谢谢你的例子,很有启发性!
【解决方案2】:

我认为你的 android 和 pc 同时是 x86 或 arm,否则可执行文件不应该在两个平台上运行。现在要同时使共享库可执行,您可以使用 gcc 的 -pie 命令行选项。详情可以在这个answer找到。

【讨论】:

  • 我不是在问如何生成共享对象。我要求编译共享对象以便它可以直接执行。
猜你喜欢
  • 2022-09-28
  • 2013-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多