为ARM编写的原生android代码如何在x86上运行?

时间:2012-10-22 05:15:53

标签: android android-ndk arm native android-x86

摩托罗拉刚刚发布了基于x86的Android手机。关于为ARM编写的本机应用程序/库(例如netflix)如何在这款手机上运行,​​我感到有点困惑。

如果有人能够解释,我会感激不尽。

3 个答案:

答案 0 :(得分:52)

是的,ARM本机代码使用名为 Houdini

的仿真功能在Intel x86上运行

这个库的作用是动态读取ARM指令并将它们转换为等效的x86指令。这就是为什么许多应用程序可以在x86上运行而不必实际构建等效库的原因。

enter image description here

答案 1 :(得分:9)

您实际上可以为不同的架构包含不同的本机代码,不确定Netflix是如何运行的,但如果您打开apk,则可以看到/lib/armeabi-v7a/,因此我假设可以有一个类似/lib/x86/ <的文件夹/ p>

编辑:我刚检查了亚马逊购物应用程序,它有arm和x86的本机代码。所以也许这就是netflix如何做到的。

答案 2 :(得分:3)

Android Studio 3模拟器使用QEMU作为后端

https://en.wikipedia.org/wiki/QEMU

QEMU可以说是领先的开源跨拱模拟器。它是GPL软件,除x86和ARM外还支持许多更多的arch。

Android然后在QEMU和可能的补丁之上添加了一些UI魔法,但核心肯定在QEMU上游。

QEMU使用一种称为二进制翻译的技术来实现相当快速的仿真:https://en.wikipedia.org/wiki/Binary_translation

二进制转换基本上将ARM指令转换为等效的x86指令。

因此,要了解细节,最好的方法是:

<强>理论

  • CPU为“Turing complete”(最高内存限制)
  • CPU具有简单的确定性行为,可以使用有限内存图灵机进行模拟

因此,很明显任何CPU都可以模拟任何给定足够内存的CPU。

难题是如何快速

练习:QEMU用户模式模拟

QEMU具有用户态模式,只要您的来宾和主机是相同的操作系统,就可以非常轻松地在x86计算机上使用用户态ARM代码来查看发生的情况。

在这种模式下,二进制转换会处理基本指令,系统调用只会转发给主机系统调用。

,对于Linux on Linux with Linux独立(无glibc)的hello world:

main.S

.text
.global _start
_start:
asm_main_after_prologue:
    /* write */
    mov x0, 1
    adr x1, msg
    ldr x2, =len
    mov x8, 64
    svc 0

    /* exit */
    mov x0, 0
    mov x8, 93
    svc 0
msg:
    .ascii "hello syscall v8\n"
len = . - msg

GitHub upstream

然后汇编并运行为:

sudo apt-get install qemu-user gcc-aarch64-linux-gnu
aarch64-linux-gnu-as -o main.o main.S
aarch64-linux-gnu-ld -o main.out main.o
qemu-aarch64 main.out 

并输出预期的:

hello syscall v8

您甚至可以运行针对C标准库编译的ARM程序,并且GDB步骤调试程序!请参阅此具体示例:How to single step ARM assembly in GDB on QEMU?

由于我们讨论的是二进制翻译,我们还可以启用一些日志记录来查看QEMU正在进行的确切翻译:

qemu-aarch64 -d in_asm,out_asm main.out

下面:

  • in_asm指的是ARM来宾输入程序集
  • out_asm指的是运行的X86主机生成的程序集

输出包含:

----------------
IN: 
0x0000000000400078:  d2800020      mov x0, #0x1
0x000000000040007c:  100000e1      adr x1, #+0x1c (addr 0x400098)
0x0000000000400080:  58000182      ldr x2, pc+48 (addr 0x4000b0)
0x0000000000400084:  d2800808      mov x8, #0x40
0x0000000000400088:  d4000001      svc #0x0

OUT: [size=105]
0x5578d016b428:  mov    -0x8(%r14),%ebp
0x5578d016b42c:  test   %ebp,%ebp
0x5578d016b42e:  jne    0x5578d016b482
0x5578d016b434:  mov    $0x1,%ebp
0x5578d016b439:  mov    %rbp,0x40(%r14)
0x5578d016b43d:  mov    $0x400098,%ebp
0x5578d016b442:  mov    %rbp,0x48(%r14)
0x5578d016b446:  mov    $0x4000b0,%ebp
0x5578d016b44b:  mov    0x0(%rbp),%rbp
0x5578d016b44f:  mov    %rbp,0x50(%r14)
0x5578d016b453:  mov    $0x40,%ebp
0x5578d016b458:  mov    %rbp,0x80(%r14)
0x5578d016b45f:  mov    $0x40008c,%ebp
0x5578d016b464:  mov    %rbp,0x140(%r14)
0x5578d016b46b:  mov    %r14,%rdi
0x5578d016b46e:  mov    $0x2,%esi
0x5578d016b473:  mov    $0x56000000,%edx
0x5578d016b478:  mov    $0x1,%ecx
0x5578d016b47d:  callq  0x5578cfdfe130
0x5578d016b482:  mov    $0x7f8af0565013,%rax
0x5578d016b48c:  jmpq   0x5578d016b416 

所以在IN部分,我们看到了手工编写的ARM汇编代码,在OUT部分,我们看到了生成的x86汇编。

在Ubuntu 16.04 amd64,QEMU 2.5.0,binutils 2.26.1中测试。

QEMU完整系统仿真

然而,当您在QEMU中启动Android时,它当然没有运行用户空间二进制文件,而是进行完整的系统仿真,它运行实际的Linux内核和模拟中的所有设备。

完整的系统仿真更准确,但速度稍慢,您需要将内核和磁盘映像提供给QEMU。

要试一试,请查看以下设置:

<强> KVM

如果你在QEMU上运行Android X86,你会发现它的速度要快得多。

原因是QEMU使用KVM,这是一个Linux内核功能,可以直接在主机上运行访客指令!

如果你碰巧拥有一台功能强大的ARM机器(从2019年开始很少见),你也可以用更快的速度在ARM上运行ARM。

出于这个原因,如果您在X86主机上,我建议您坚持使用A86P的X86模拟:How to compile the Android AOSP kernel and test it with the Android Emulator?,除非您确实需要触摸低级别的东西。