前言
前不久,为了搞定 OpenRASP for linux aarch64 版本,中间最关键的一个东西就是 Chromium 的开源 v8 引擎(俗称 libv8),通过静态库(libv8_monolith.a)的形式引入,官方提供的预编译版本是 7.8.279.19
,相对较老,尝试从 V8 官方源码编译这个版本,运行总是 OOM(不知道是否编译环境的问题),含泪放弃~~~
以下为过程中的报错信息:
mvn install
gdb ../build64/java/libopenrasp_v8_java.so core.xxxxx # xxxxx 为 coredump 的进程号
后来经过反复尝试(网络上的文章是真简短,但最终都是指向失败,求助 GPT 基本也是一顿胡说,让我走了不少弯路),发现 8.6.395.17
版本的 hello-world 可以正常运行,遂在这个版本基础上进行 OpenRASP 更新的后续工作。
这篇文章就讲一讲我这三周摸索出的三种获取 libv8_monolith.a 静态库的方法,供读者参考。
方法一:使用 Libv8 官方代码
- 优点:原生代码,干净;生成的库体积小、参数配置方便
- 缺点:需要梯子才能访问、工具链庞大,编译环境搭建贼慢
- 参考:https://github.com/tanjelly/openrasp-v8/blob/master/vendors/build-libv8.sh
PS: 刚开始不知道项目中有这个脚本,走了不少弯路!!!
安装系统
- Ubuntu22.04 X86_64
安装必要组件
sed -i 's#/archive.ubuntu.com#/mirrors.ustc.edu.cn#g' /etc/apt/sources.list
sed -i 's#/security.ubuntu.com#/mirrors.ustc.edu.cn#g' /etc/apt/sources.list
apt-get update
apt-get upgrade
apt-get install vim git python2 python3 python3-pip ninja-build clang-14
apt-get install pkg-config libglib2.0-dev
ln -s /usr/bin/python2 /usr/bin/python
export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/share/pkgconfig:/usr/lib/$(uname -p)-linux-gnu/pkgconfig
拉取编译 libv8 相关代码
cd /opt/
# 拉取编译工具
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=/opt/depot_tools:$PATH
# 初始化编译环境(如果有报错需要根据错误处理)
python3 /opt/depot_tools/gclient.py
# 拉取v8代码
fetch v8
# 拉取相关依赖(漫长的等待...)
cd v8
git checkout 8.6.395.17
gclient sync -v
# 或执行 python3 /opt/depot_tools/gclient.py sync -v
安装 aarch64 编译环境
cd /opt/v8/
python build/linux/sysroot_scripts/install-sysroot.py --arch=arm64
构建
cd /opt/v8/
# 构建配置
mkdir -p out/arm64.release
cat > out/arm64.release/args.gn <<EOF
is_debug = false
target_cpu = "arm64"
symbol_level = 0
is_component_build = false
treat_warnings_as_errors = false
use_custom_libcxx = false
libcxx_abi_unstable = false
v8_embedder_string = " <OpenRASP>"
v8_monolithic = true
v8_enable_i18n_support = false
v8_use_snapshot = true
v8_use_external_startup_data = false
v8_enable_shared_ro_heap = true
EOF
# 生成构建相关文件(过程中可能会提示 libcxx_abi_unstable 问题,直接忽略)
gn gen out/arm64.release
# 构建(16为CPU数量)
/usr/bin/ninja -C out/arm64.release -j 16 v8_monolith
# 最终生成的库文件(大小为40M左右)
out/arm64.release/obj/libv8_monolith.a
其它版本 libv8_monolith.a 生成方法与 aarch64 版本基本相同,只需要修改 args.gn
文件中的 target_cpu
参数为 x64
(编译成功) 或 x86
(交叉编译失败,可能需要在 i386 系统环境下编译)即可。
方法二:使用 Nodejs 官方代码
- 优点:使用系统默认的编译环境就可以进行,省却大量拉取工具链的时间
- 缺点:想定制不太容易,怎么调参没研究透,最终生成的文件也比【方法一】大一些(约70M+)
- 参考:https://github.com/rubyjs/libv8-node/blob/master/libexec/build-libv8
安装并启动系统
- CentOS 7.9 Aarch64
安装必要组件
yum update -y
yum -y install cmake git wget vim python2 centos-release-scl
yum makecache
安装编译工具
yum -y install devtoolset-10
scl enable devtoolset-10 bash
拉取 Nodejs 代码
wget https://nodejs.org/dist/v15.14.0/node-v15.14.0.tar.gz
tar -xf node-v15.14.0.tar.xz
编译 Nodejs
cd node-v15.10.0/
CPUS=`cat /proc/cpuinfo | grep processor | wc -l`
# 配置
./configure --openssl-no-asm --without-npm --shared --without-intl --without-siphash
sed -i "56i \ 'v8_embedder_string': ' <OpenRASP>'," config.gypi
# 编译
make -j$CPUS
打包 libv8_monolith.a
首先在 nodejs 源码根目录下创建一个包含下列内容的脚本 build-monolith.sh(参考 node-v8-monolith 项目)
#!/bin/sh
BASEDIR=$(pwd)
BUILDTYPE=${BUILDTYPE:-Release}
cd out/${BUILDTYPE}/obj.target
if [ "$(uname)" = "SunOS" ]; then
/usr/xpg4/bin/find . -path "**/*v8*/**/*.o" | xargs ar cqs libv8_monolith.a
/usr/xpg4/bin/find . -path "**/*icu*/**/*.o" | xargs ar cqs libv8_monolith.a
elif [ "$(uname)" = "Linux" ]; then
for lib in `find . -path './tools/v8_gypfiles/*.a'`;
do ar -t $lib | xargs ar -q libv8_monolith.a;
done;
for lib in `find . -path './tools/icu/*.a'`;
do ar -t $lib | xargs ar -q libv8_monolith.a;
done;
else
echo "Unsupported Platform"
exit
fi
mv libv8_monolith.a $BASEDIR/out/${BUILDTYPE}
然后执行脚本,生成文件:
bash ./build-monolith.sh
# 生成的库文件位置
out/Release/libv8_monolith.a
方法三:使用 libv8-node 预编译库
- 优点:此方法最省事,省却大量折腾 libv8 库的时间
- 缺点:文件有点大,没法定制
下载 libv8-node
# 本来想找一个跟方法二一致的版本,发现官方没提供
wget https://rubygems.org/downloads/libv8-node-15.14.0.1-aarch64-linux.gem
从下载的文件包中使用 7zip 提取下列文件:
data.tar.gz/data.tar/vendor/v8/include
data.tar.gz/data.tar/vendor/v8/out.gn/libv8/obj/libv8_monolith.a
总结
综合对比来看,三种方法条件允许还是使用【方法一】比较好。如果为了节省时间,【方法三】是个不错的选择。【方法二】适合具备一定调参基础的同学。
参考
1: https://github.com/sqreen/node-v8-monolith
2: https://github.com/tanjelly/openrasp-v8
3: https://v8.dev/docs/compile-arm64
4: https://rubygems.org/gems/libv8-node/versions/15.14.0.1-aarch64-linux
5: https://nodejs.org/