Недавно Motorola выпустила Android-телефон на базе x86. Я немного смущен тем, как на этом телефоне могут запускаться собственные приложения/библиотеки, написанные для ARM (например, netflix).
Буду признателен, если кто-нибудь сможет объяснить.
Недавно Motorola выпустила Android-телефон на базе x86. Я немного смущен тем, как на этом телефоне могут запускаться собственные приложения/библиотеки, написанные для ARM (например, netflix).
Буду признателен, если кто-нибудь сможет объяснить.
Да, встроенный код ARM работает на Intel x86 с использованием функции эмуляции с именем Houdini
Что делает эта библиотека, она читает инструкции ARM "на лету" и преобразует их в эквивалентные инструкции x86. Именно по этой причине многие приложения могут работать как на x86, не имея при этом необходимости создавать эквивалентную библиотеку.
Фактически вы можете включить другой собственный код для другой архитектуры, не знаете, как работает Netflix, но если вы открываете apk, вы можете видеть /lib/armeabi-v7a/
, поэтому я предполагаю, что может быть папка, похожая на /lib/x86/
Изменить: я просто проверял приложение для покупок Amazon, у него есть собственный код для руки и x86. Так может быть, Thats, как netflix тоже это делает.
Эмулятор Android Studio 3 использует QEMU в качестве бэкэнда
https://en.wikipedia.org/wiki/QEMU
QEMU, пожалуй, ведущий эмулятор кросс-арки с открытым исходным кодом. Это программное обеспечение GPL, и в дополнение к x86 и ARM поддерживает множество других арок.
Затем Android просто добавляет немного магии пользовательского интерфейса поверх QEMU и, возможно, некоторые патчи, но ядро определенно находится в QEMU upstream.
QEMU использует технику, называемую бинарным переводом, для достижения достаточно быстрой эмуляции: https://en.wikipedia.org/wiki/Binary_translation
Двоичный перевод в основном переводит инструкции ARM в эквивалентные инструкции x86.
Поэтому, чтобы понять детали, лучше всего:
теория
Поэтому ясно, что любой процессор может эмулировать любой процессор, если ему достаточно памяти.
Сложный вопрос, как сделать это быстро.
Практика: симуляция пользовательского режима QEMU
QEMU имеет режим пользовательского пространства, который позволяет очень легко играть с пользовательским кодом ARM на вашем компьютере с архитектурой x86, чтобы увидеть, что происходит, если ваш гость и хост находятся в одной ОС.
В этом режиме происходит то, что двоичный перевод выполняет основные инструкции, а системные вызовы просто перенаправляются на системные вызовы хоста.
Например, для Linux на Linux с автономным Linux (без glibc) привет мир:
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
Затем собрать и запустить как:
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
Вы даже можете запускать программы ARM, скомпилированные со стандартной библиотекой C, и GDB шаг отлаживает программу! Посмотрите этот конкретный пример: Как выполнить пошаговую сборку ARM в GDB на QEMU?
Поскольку мы говорим о двоичном переводе, мы также можем включить некоторую регистрацию, чтобы увидеть точный перевод, который делает QEMU:
qemu-aarch64 -d in_asm,out_asm main.out
Вот:
in_asm
относится к сборке гостевого ввода ARMout_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 полная эмуляция системы
Однако, когда вы загружаете Android в QEMU, он, конечно, не запускает бинарный файл пользователя, а скорее выполняет полную симуляцию системы, где он запускает реальное ядро Linux и все устройства в симуляции.
Полная симуляция системы более точна, но немного медленнее, и вам нужно передать ядро и образ диска в QEMU.
Чтобы попробовать это, взгляните на следующие настройки:
KVM
Если вы запустите Android X86 на QEMU, вы заметите, что он намного быстрее.
Причина в том, что QEMU использует KVM, которая является функцией ядра Linux, которая может выполнять гостевые инструкции прямо на хосте!
Если у вас есть мощный компьютер ARM (но редкий по состоянию на 2019 год), вы также можете запустить ARM на ARM с KVM намного быстрее.
По этой причине я рекомендую придерживаться X86-симуляции AOSP, если вы работаете на хосте X86, как указано в разделе: Как скомпилировать ядро Android AOSP и протестировать его с помощью эмулятора Android? Если только вам действительно не нужно прикасаться к чему-то низкому уровню.
В Trend Micro Safe Mobile Workforce у нас есть время выполнения ARM (не Intel houdini) для собственной библиотеки в приложениях Android. Чтобы мы могли поддерживать APK только с ARM lib на мощном сервере x86.