苏苏的博客

简约至极

在Android上运行PHP

为安卓交叉编译ARM版的PHP

目前手头上有一个古老的Android平板,系统为Android2.2,CPU 是armv5的架构,可以说是很古老了

想作为一个简易的服务器使用,便开始自己折腾.

本教程操作都在centosdocker容器内.基本工具都类似.

yum update && yum upgrade && yum install wget make perl -y 不用安装gcc

1.下载交叉编译工具 https://pan.baidu.com/s/1micZBWc

直连下载 wget http://share.suconghou.cn/files/arm/crossx86-arm-linux-musleabi-1.1.12.tar.xz

下载后解压,并添加bin到环境变量tar Jxf crossx86-arm-linux-musleabi-1.1.12.tar.xz

export PATH=/arm-linux-musleabi/bin:$PATH

之后,使用arm-linux-musleabi-gcc -v 可以验证

2.修改musl-libc库,目的是修改查找的DNS配置文件.

cd /tmp && wget --no-check-certificate https://www.musl-libc.org/releases/musl-1.1.16.tar.gz

下载并解压tar zxf musl-1.1.16.tar.gz && cd musl-1.1.16

替换1: 替换/etc/resolv.conf

grep -rn "/etc/resolv.conf" . 即可查找到有两个文件包含它.

接下来,我们把它替换掉,把路径替换为安卓安装文件夹可写的路径

sed -i "s{/etc/resolv.conf{/data/data/cn.suconghou.hello/files/resolv.conf{" ./src/network/resolvconf.c
sed -i "s{/etc/resolv.conf{/data/data/cn.suconghou.hello/files/resolv.conf{" ./include/resolv.h

可以再次查找,以验证是否替换成功

替换2: 替换/etc/hosts

grep -rn "/etc/hosts" . 即可查找到有三个文件,其中有一个是注释 下载执行替换,同样替换为一个我们可以写入的路径

sed -i "s{/etc/hosts{/data/data/cn.suconghou.hello/files/hosts{" ./src/network/getnameinfo.c
sed -i "s{/etc/hosts{/data/data/cn.suconghou.hello/files/hosts{" ./src/network/lookup_name.c

再次查找以验证是否替换成功

替换3: 替换/bin/sh,使我们可以使用android的sh

grep -rn "/bin/sh" . 即可查找到较多结果,我们需要修改

./include/paths.h

./src/legacy/getusershell.c

./src/misc/wordexp.c

./src/process/system.c

./src/stdio/popen.c

这5个文件

sed -i "s{/bin/sh{/system/bin/sh{" ./include/paths.h
sed -i "s{/bin/sh{/system/bin/sh{" ./src/legacy/getusershell.c
sed -i "s{/bin/sh{/system/bin/sh{" ./src/misc/wordexp.c
sed -i "s{/bin/sh{/system/bin/sh{" ./src/process/system.c
sed -i "s{/bin/sh{/system/bin/sh{" ./src/stdio/popen.c

同样可以再次查找验证是否替换.

然后开始编译我们修改后的musl

./configure --prefix="/arm-linux-musleabi/arm-linux-musleabi" CC="arm-linux-musleabi-gcc"
make -j4 && make install

编译约需要5分钟,ls -lh /arm-linux-musleabi/arm-linux-musleabi/lib/ 这些文件就是编译后生成的新文件.

注意这里编译使用的是arm-linux-musleabi-gcc而不是gcc,事实上,从头到尾我们都不需要gcc

之后开始第三步,编译PHP

3. 编译PHP

下载PHP最新源码,并解压

cd /tmp && wget http://php.net/distributions/php-7.1.8.tar.xz && tar xJf php-7.1.8.tar.xz && cd php-7.1.8

替换1: 同样,我们需要替换sh的路径, grep -rn "/bin/sh" . 有较多结果,我们需要替换./ext/standard/proc_open.c

sed -i "s{/bin/sh{/system/bin/sh{" ./ext/standard/proc_open.c

执行下面命令,把我们的编译命令都替换为交叉编译的命令

export HOST="arm-linux-musleabi"
export CC="arm-linux-musleabi-gcc"
export CXX="arm-linux-musleabi-g++"
export AR="arm-linux-musleabi-ar"
export LD="arm-linux-musleabi-ld"
export RANLIB="arm-linux-musleabi-ranlib"
export STRIP="arm-linux-musleabi-strip"
export CPPFLAGS="-I/deps/include"
export LDFLAGS="-L/deps/lib"
export SYSROOT_MUSL="/arm-linux-musleabi/arm-linux-musleabi"

我们先最小化编译PHP,生成Makefile

LDFLAGS="-static -L/deps/lib" CFLAGS="--sysroot=/arm-linux-musleabi/arm-linux-musleabi -Os" CXXFLAGS="--sysroot=/arm-linux-musleabi/arm-linux-musleabi -Os" ./configure --host=arm-linux-musleabi --enable-inline-optimization --enable-static=yes --enable-shared=no --without-pear --disable-cgi --disable-opcache --disable-fpm --disable-phpdbg --disable-all --enable-cli --enable-tokenizer

生成Makefile后,我们要修改Makefile使其静态编译.

sed -i "s{-I/usr/include{ {" Makefile
sed -i "s{-export-dynamic{-all-static{" Makefile

然后 make -j4 && make install 可以编译成功

原先的strip 命令,可以使用arm-linux-musleabi-strip代替

添加其他扩展需要我们自己下载编译,下面这个几个是编译CURL所必须的.

1. zlib

cd /tmp && wget http://zlib.net/zlib-1.2.11.tar.gz
tar zxf zlib-1.2.11.tar.gz && cd zlib-1.2.11
CFLAGS="--sysroot=$SYSROOT_MUSL -Os" \
./configure \
--prefix=/deps \
--static
make && make install

2. bz2

cd /tmp && wget http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz
tar zxf bzip2-1.0.6.tar.gz && cd bzip2-1.0.6
# 把 Makefile 里 CFLAGS 里的 -O2 改为 -Os 后面的 -g去掉 , 前面几行gcc部分等写死的直接删除
make install PREFIX=/deps

这些删除

# To assist in cross-compiling
CC=gcc
AR=ar
RANLIB=ranlib
LDFLAGS=

3. openssl

cd /tmp && wget https://www.openssl.org/source/openssl-1.0.2l.tar.gz
tar zxf openssl-1.0.2l.tar.gz && cd openssl-1.0.2l
CFLAGS="--sysroot=$SYSROOT_MUSL -static" \
./Configure \
linux-armv4 \
no-shared \
--prefix="/deps" \
--with-zlib-lib="/deps/lib" \
--with-zlib-include="/deps/include"
# 把 Makefile 里 CFLAG 里的 -O3 改为 -Os (vi 查找,第一个就是)
make && make install

4. curl

cd /tmp && wget https://curl.haxx.se/download/curl-7.54.0.tar.gz
tar zxf curl-7.54.0.tar.gz && cd curl-7.54.0

CC="arm-linux-musleabi-gcc --sysroot=$SYSROOT_MUSL" \
CXX="arm-linux-musleabi-g++ --sysroot=$SYSROOT_MUSL" \
CPPFLAGS="-I/deps/include" \
LDFLAGS="-static -L/deps/lib" \
./configure \
--prefix="/deps" \
--enable-zlib \
--disable-shared \
--with-ssl="/deps" \
--enable-static \
--host=$HOST
# 把 Makefile 里 CFLAGS 里的 -O2 改为 -Os
make -j4 && make install

包含我们需要的一些扩展 , 然后编译PHP(上次编译后,需make clean)

这次我们指定php.ini的文件位置

--with-config-file-scan-dir=/data/data/cn.suconghou.hello/files/conf.d --with-config-file-path=/data/data/cn.suconghou.hello/files
LDFLAGS="-static -L/deps/lib" CFLAGS="--sysroot=/arm-linux-musleabi/arm-linux-musleabi -Os" CXXFLAGS="--sysroot=/arm-linux-musleabi/arm-linux-musleabi -Os" ./configure --with-config-file-scan-dir="/data/data/cn.suconghou.hello/files/conf.d" --with-config-file-path="/data/data/cn.suconghou.hello/files" --host=arm-linux-musleabi --enable-inline-optimization --enable-static=yes --enable-shared=no --without-pear --disable-cgi --disable-opcache --disable-fpm --disable-phpdbg --disable-all --enable-cli --enable-tokenizer --enable-session --enable-json  --enable-mbstring --enable-mbregex --enable-filter --enable-hash --enable-zip --enable-phar --with-sqlite3 --with-openssl=/deps --with-curl=/deps --with-zlib-dir=/deps

添加 --enable-zip --with-zlib-dir=/deps 启用zip和zlib扩展

--enable-pdo --with-pdo-sqlite --enable-sockets --enable-posix --enable-ftp

编译启用越多扩展,PHP进程的内存占用会稍微增加

同上面一样

生成Makefile后,我们要修改Makefile使其静态编译.

sed -i "s{-I/usr/include{ {" Makefile
sed -i "s{-export-dynamic{-all-static{" Makefile

然后 make -j4 编译,生成的文件在sapi/cli文件夹中,编译已经成功,由于这次启用了phar,make install时需要调用php命令Generating phar.php,但php可执行文件在Linux x86_64下无法执行,是arm环境的.因此不能执行make install,但编译是完好的.

使用arm-linux-musleabi-strip命令,可为我们编译的可执行文件瘦身.

最后编译的文件,能够运行于android2.2,服务于项目 https://github.com/suconghou/androidphp

问题

file_get_contents访问https时报错

file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed

这是由于证书引起的问题,使用curl访问可以正常访问.

使用下面语句可以查看openssl的默认证书配置

var_dump(openssl_get_cert_locations());

或者配置 file_get_contents 不验证https证书.

$stream_opts = [
    "ssl" => [
        "verify_peer"=>false,
        "verify_peer_name"=>false,
    ]
];

$response = file_get_contents("https://www.example.com", false, stream_context_create($stream_opts));

或者在 php.ini 中添加 openssl.cafile=/path/to/cacert.pem

证书下载 https://curl.haxx.se/ca/cacert.pem