在此之前,已经有人尝试在Android设备上运行Java版Minecraft并且取得成功, 项目名为Boardwalk

该项目在前段时间开源: Boardwalk的Github仓库

目前Boardwalk总共有3个形态:

  • 1.2 :使用Android的Dalvik VM来运行
  • 1.9 :使用Oracle提供的JDK arm来运行
  • 2.0 :使用自行编译的OpenJdk9来运行

基本上每个版本都存在局限性

  1. 仅支持Arm 32位
  2. 1.2仅支持原版启动 并且兼容性差
  3. 2.0使用OpenJdk9 仅支持1.13+
  4. 使用的gl2es版本过老 仅支持opengl1.1 性能较差

所以说以1.9版本为学习对象 1.9的最后一次Commit

尝试

在Android环境下直接使用从官网下载的JDK应该会出现以下情况:

# $PWD/jdk1.8.0_221/bin/java -version
/system/bin/sh: /data/data/java/jdk1.8.0_221/bin/java: can't execute: Permission denied
# chmod 755 $PWD/jdk1.8.0_221/bin/java
# $PWD/jdk1.8.0_221/bin/java -version
/system/bin/sh: /data/data/java/jdk1.8.0_221/bin/java: No such file or directory

No such file or directory并不是因为文件/bin/java不存在, 而是因为linux和Android的差异导致的

在Unix/Linux及其子系统中,所有可执行文件都携带一个ELF数据头

ELF数据头里头存放了这个可执行文件所需的动态链接库和LD动态链接器的路径

系统内核先从ELF数据头中读取LD动态链接器的路径,再用链接器来加载支持库与该文件的代码块

动态链接器

在Android中使用的不是Linux使用的glibc而是精简版的bionic, 在设计之初bionic就不打算兼容glibc

如上图所示/lib/ld-linux-aarch64.so.1就是Linux的动态链接器路径

而Android的动态链接器为/system/bin/linker, 所以说No such file or directory指的是链接器不存在

要是直接把这个路径改为Android的动态链接库, 也会因为bionic与glibc的差异而导致无法运行

虽然直接把文件放到指定的位置可以实现我们需要的功能, 但是目前绝大部Android设备是没有Root权限的

好在, 这个动态连接器只是看上去是个SO文件, 它本质上是一个不需要其他链接器的可执行文件

清华仓库: https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/pool/main/g/glibc/

从上方链接下载glibc, 注意!!!我们需要的内容是大概2mb大小的libc6_2.XX-0ubuntuX_arm64.deb!!!

将deb文件解压, 在data.tar.xz/lib/aarch64-linux-gnu里的就是aarch64的glibc了

给予动态链接器可执行权限并且设置LD_LIBRARY_PATH环境变量 扩展阅读:'什么是 LD_LIBRARY_PATH '

# chmod 0755 ./lib/ld-2.29.so
# export export LD_LIBRARY_PATH=$PWD/lib:$PWD

小部分的程序,在以上操作后使用动态链接器启动,来测试测试

# ./lib/ld-2.29.so ./jdk1.8.0_221/bin/java -version
Error: could not find libjava.so
Error: Could not find Java SE Runtime Environment.

不出所料,Java还是无法启动,但是输出的错误已经是Java的内容了

在错误中,说是无法找到libjava.so文件 这时候看向OpenJDK的源码 java_md_solinux.c

错误输出

很明显的就是GetApplicationHome返回了false

GetApplicationHome

猜测是GetExecName返回了Null 这时候来看这里的execname是怎么赋值

SetExecName

在这里调用了readlink这个函数

在我一番查找下,发现在boardwalk中有个libboardwalk_preload.so是没有源码的

在boardwalk的源码中这个文件是被这样调用的 LoadMe.java

setenv("LD_PRELOAD", runtimePath + "/libboardwalk_preload.so:" +
                runtimePath + "/jvm/jdk1.8.0_33/jre/lib/arm/libjsig.so");

从文章LD_PRELOAD替换进程底层函数中得知LD_PRELOAD的作用是在动态链接中优先链接 能直接替换系统函数

用IDA反汇编libboardwalk_preload.so,果不其然的看到了readlink,按F5生成以下伪代码

readlink

setenv("OVERRIDE_PROC_SELF_EXE", runtimePath + "/jvm/jdk1.8.0_33/jre/bin/java");

结合上述代码,基本上确定了这个错误的解决方法

未完待续

鸽了

标签: 安卓, Minecraft, Java