同步: 最近刚来平台, 计划将自己之前在其他当地发的文章搬运 (同步) 过来.

首发日期2024-02-27, 以下为原文内容.


在 GNU/Linux 体系上运转 Android 应用比较容易 (比方 waydroid), 可是反过来就很麻烦了.

Android 尽管也运用 Linux 内核 (kernel), 可是体系环境和一般的 GNU/Linux 体系 (比方 ArchLinux, Debian, Ubuntu, Fedora, NixOS 等) 具有不可疏忽的明显差异, 所认为 GNU/Linux 编译的二进制可执行文件, 不能拿过来直接运转.

想要在 Android 体系运转这些应用, 有几种不同的办法. 功能最高的办法当然是重新编译, 编译后的二进制文件就能够直接在 Android 运转了, Termux 便是这么做的. 可是, 尽管终究一步很简略, 前面的为 Android 编译, 便是很麻烦乃至很困难的了.

假如能把为 GNU/Linux 编译的二进制文件直接拿来, 不加修改的运转, 在许多情况下会简略容易许多. proot 便是一种这样的技术. proot 通过运用 Linux 内核的 ptrace 功能, 对程序的内核调用 (syscall) 进行翻译, 然后完成一个很薄的兼容层, 功能只有很小的损失 (并不是虚拟机办法).

本文以应用程序 deno 举栗, 介绍在 Android 运转的办法. 本文不只介绍终究成果 (详细的完成计划), 还要点阐明探索的进程, 也便是这个成果是怎么获得的.


相关链接:

目录

  • 1 获取所需文件
  • 2 拼装运转环境
  • 3 测验
  • 4 总结与展望
  • 附录 1 proot-distro 简介

1 获取所需文件

本文的目标是构建一个相对独立的, 最小的运转环境. 这个运转环境所需的各种文件, 需求从多个当地分别获取.

  • (1) proot (termux)

    首先在手机上装置 Termux: termux.dev/

    然后在 Termux 中装置 proot:

    > pkg install proot
    

(同步) 在 Android 运转 GNU/Linux 二进制程序 (proot)

  • (2) GNU/Linux 体系文件包 (proot-distro)

    这个需求从 proot-distro 的发布页面下载: github.com/termux/proo…

    下载这个文件: debian-bookworm-aarch64-pd-v4.7.0.tar.xz

  • (3) deno (aarch64 linux)

    这个需求从 deno 的发布页面下载: github.com/denoland/de…

    下载这个文件 (v1.41.0): deno-aarch64-unknown-linux-gnu.zip

2 拼装运转环境

万事俱备, 能够开端拼凑运转环境了 !

  • (1) proot 运转所需文件.

    先来看看 proot 软件包都有哪些文件 (在 termux 中操作):

    > dpkg -L proot
    

(同步) 在 Android 运转 GNU/Linux 二进制程序 (proot)

其间 usr/bin/proot 是 proot 的可执行文件, usr/libexec/proot/loader 这个也是 proot 的重要组成部分.

再来看看 proot 的依靠库:

> ldd /data/data/com.termux/files/usr/bin/proot

(同步) 在 Android 运转 GNU/Linux 二进制程序 (proot)

libtalloc.so.2 这个是 proot 运转所需求的库文件, 需求留意. libc.so, ld-android.so, libdl.so 这些库是 Android 体系本身提供的, 不用管.

  • (2) deno 运转所需文件.

    把压缩包 deno-aarch64-unknown-linux-gnu.zip 解压之后, 把里面的 deno 文件拿出来, 放到 termux 中, 然后:

    > ldd deno
    

(同步) 在 Android 运转 GNU/Linux 二进制程序 (proot)

这儿能够看到一些依靠库文件: libgcc_s.so.1, libpthread.so.0, libm.so.6, libc.so.6 等.

deno 运用 rust 编程言语开发, 尽管 rust 号称是静态链接, 在终究生成的单个二进制可执行文件中打包一切依靠, 但实际上仍是有少数体系库是动态链接的 (比方 glibc).

把压缩包 debian-bookworm-aarch64-pd-v4.7.0.tar.xz 解压之后, 从里面获取此处所需的库文件.

  • (3) 开端拼装:

    > find setup
    setup
    setup/proot
    setup/loader
    setup/libtalloc.so.2
    setup/run.sh
    setup/setup.sh
    setup/debian12_aarch64
    setup/debian12_aarch64/usr
    setup/debian12_aarch64/usr/bin
    setup/debian12_aarch64/usr/bin/env
    setup/debian12_aarch64/usr/bin/deno
    setup/debian12_aarch64/usr/lib
    setup/debian12_aarch64/usr/lib/aarch64-linux-gnu
    setup/debian12_aarch64/usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
    setup/debian12_aarch64/usr/lib/aarch64-linux-gnu/libc.so.6
    setup/debian12_aarch64/usr/lib/aarch64-linux-gnu/libm.so.6
    setup/debian12_aarch64/usr/lib/aarch64-linux-gnu/libpthread.so.0
    setup/debian12_aarch64/usr/lib/aarch64-linux-gnu/libgcc_s.so.1
    setup/debian12_aarch64/usr/lib/aarch64-linux-gnu/libdl.so.2
    

    这是终究拼装好的运转环境的文件清单, 一切文件放在 setup 目录中. 其间 proot, loader, libtalloc.so.2 是从 termux 中获取的. usr/bin/deno 是从压缩包 deno-aarch64-unknown-linux-gnu.zip 中获取的. debian12_aarch64 目录中的文件是从压缩包 debian-bookworm-aarch64-pd-v4.7.0.tar.xz 中获取的.


其间 setup.sh 文件:

#!/system/bin/sh
echo setup.sh $1
#cd $1
chmod +x run.sh
chmod +x proot
chmod +x loader
chmod +x debian12_aarch64/usr/bin/env
chmod +x debian12_aarch64/usr/bin/deno
mkdir -p tmp
mkdir -p debian12_aarch64/tmp
ln -s usr/lib debian12_aarch64/lib
ln -s aarch64-linux-gnu/ld-linux-aarch64.so.1 debian12_aarch64/usr/lib/ld-linux-aarch64.so.1
echo setup ok.

这个脚本用来做一些初始化的事情.

其间 run.sh 文件:

#!/system/bin/sh
export LD_LIBRARY_PATH=$(pwd)
export PROOT_TMP_DIR=$(pwd)/tmp
export PROOT_LOADER=$(pwd)/loader
./proot 
  --bind=debian12_aarch64/tmp:/dev/shm 
  --bind=/sys 
  --bind=/proc/self/fd/2:/dev/stderr 
  --bind=/proc/self/fd/1:/dev/stdout 
  --bind=/proc/self/fd/0:/dev/stdin 
  --bind=/proc/self/fd:/dev/fd 
  --bind=/proc 
  --bind=/dev/urandom:/dev/random 
  --bind=/dev 
  -L 
  --kernel-release=6.2.1-PRoot-Distro 
  --sysvipc 
  --link2symlink 
  --kill-on-exit 
  --cwd=/ 
  --change-id=0:0 
  --rootfs=debian12_aarch64 
  /usr/bin/deno "$@"

这个脚本调用 proot 来运转 deno.

3 测验

测验设备: 手机 Android 11 (MIUI 12.5)

运用 USB 数据线连接 PC 和手机:

> adb devices
List of devices attached
643fa0f6	device

然后:

> adb push setup /data/local/tmp
setup/: 13 files pushed, 0 skipped. 62.6 MB/s (94798346 bytes in 1.444s)
> adb shell
raphael:/ $ cd /data/local/tmp/setup
raphael:/data/local/tmp/setup $ ls -l
total 288
drwxrwxr-x 3 shell shell   4096 2024-02-27 21:15 debian12_aarch64
-rw-rw-rw- 1 shell shell  30504 2024-02-26 19:39 libtalloc.so.2
-rw-rw-rw- 1 shell shell   5736 2024-02-27 01:59 loader
-rwxrwxrwx 1 shell shell 213976 2024-02-26 19:39 proot
-rwxrwxrwx 1 shell shell    590 2024-02-27 03:38 run.sh
-rw-rw-rw- 1 shell shell    356 2024-02-27 03:34 setup.sh
raphael:/data/local/tmp/setup $ chmod +x setup.sh
raphael:/data/local/tmp/setup $ ./setup.sh
setup.sh
setup ok.
raphael:/data/local/tmp/setup $ ls -l
total 296
drwxrwxr-x 4 shell shell   4096 2024-02-27 21:16 debian12_aarch64
-rw-rw-rw- 1 shell shell  30504 2024-02-26 19:39 libtalloc.so.2
-rwxrwxrwx 1 shell shell   5736 2024-02-27 01:59 loader
-rwxrwxrwx 1 shell shell 213976 2024-02-26 19:39 proot
-rwxrwxrwx 1 shell shell    590 2024-02-27 03:38 run.sh
-rwxrwxrwx 1 shell shell    356 2024-02-27 03:34 setup.sh
drwxrwxrwx 2 shell shell   4096 2024-02-27 21:16 tmp
raphael:/data/local/tmp/setup $ ./run.sh --version
deno 1.41.0 (release, aarch64-unknown-linux-gnu)
v8 12.1.285.27
typescript 5.3.3
raphael:/data/local/tmp/setup $ ./run.sh repl -A
Deno 1.41.0
exit using ctrl+d, ctrl+c, or close()
> 0.1 + 0.2
0.30000000000000004
> 

成功运转了 GNU/Linux (aarch64) 版别的 deno.

(同步) 在 Android 运转 GNU/Linux 二进制程序 (proot)

4 总结与展望

在 proot, termux, proot-distro 的帮助下, 咱们总算成功在 Android 运转了最新版 deno. 这个小的运转环境是相对独立的, 能够单独拿出来放在一个当地就能运转.

尽管 proot 的作业原理很简略, 这个计划看起来也很简略, 窝之前认为不用费多大功夫就能轻松搞定. 实际上却是这也不可, 那也不可, 这儿有问题, 那里也有问题, 这儿是坑, 那里也是坑 .. . 折腾了好久, 遭遇了许多困难和挫折, 因为太笨气哭了好几次, 擦干眼睛周围的小小水滴之后, 才有了这个终究计划. 好在终究雨后初霁了, 努力没有白搭.

最好的办法仍是重新编译, 然后直接在 Android 运转. 可是为 Android 编译很麻烦乃至很困难的情况下, 本文的办法便是一个好的替代计划.

附录 1 proot-distro 简介

proot-distro 是 termux 团队开发的一个东西, 用来便利的在 Termux 中装置和运转 GNU/Linux 发行版 (比方 debian).

  • (1) 在 termux 中装置 proot-distro:

    > pkg install proot-distro
    
  • (2) 查看有哪些发行版可用:

    > proot-distro list
    
  • (3) 装置一个发行版 (比方 debian):

    > proot-distro install debian
    
  • (4) 发动一个发行版 (比方 debian):

    > proot-distro login debian
    

然后就获得了一个在 Android 运转的 GNU/Linux 体系环境.

假如拿到一个 GNU/Linux 的二进制程序, 首先在这个环境中测验一下, 能不能正常运转. 这是快速验证的办法.

假如能够运转, 那再用本文的办法. 假如不能运转, 那就不用考虑本文了, 早点洗洗睡吧.

本文之中构建的运转环境, 其实便是 proot-distro 的运转环境简化而来.


本文运用 CC-BY-SA 4.0 许可发布.