8

在 OpenHarmony 开发板上运行 WasmEdge(WebAssembly Runtime)

 2 years ago
source link: https://my.oschina.net/u/4532842/blog/5392261
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在 OpenHarmony 开发板上运行 WasmEdge(WebAssembly Runtime)

作者:翁纯仪,WasmEdge Contributor

在 OpenHarmony OS 上运行 WasmEdge,能够改善开发者的开体验。

移动与 IoT 设备的特点是资源受限,软硬件不统一,用户体验却要求很高。设备要能安全,跨平台地运行第三方开发者提供的软件应用(例如,应用商店),因而直接原生编译的软件应用(Native Client, or NaCl)并不主流。鸿蒙与安卓这样的主流操作系统一般提供基于 Java 或者 JavaScript 的软件执行沙盒,来支持第三方应用。但是这样的软件执行沙盒有几个大问题:

  • 支持的编程语言很有限
  • 支持的语言有 IP 与法律风险
  • 资源开销大
  • 无法支持实时系统
  • 安全性一般(所以应用商店需要审查制)

而 WebAssembly 作为一个多语言、跨平台、高性能、轻量级、安全的软件执行环境,能让开发者兼得性能、可移植性与安全性。WebAssembly 是移动与 IoT 设备上系统中间件的最佳选择。

WasmEdge 是由 CNCF 托管的轻量级、高性能和可扩展的 WebAssembly runtime,适用于云原生、边缘和去中心化应用程序。WasmEdge 可以运行 C/C++、Rust、Swift、AssemblyScript 或 Kotlin 等语言编译的标准 WebAssembly 字节码程序。

OpenHarmony 是由开放原子开源基金会(OpenAtom Foundation)孵化及运营的开源项目,目标是面向全场景、全连接、全智能时代,基于开源的方式,搭建一个智能终端设备操作系统的框架和平台,促进万物互联产业的繁荣发展。

WasmEdge 为 OpenHarmony 提供了一个与 JVM 与 JS engine 同级的 runtime,但是比 JVM、JS engine 更安全、更快、更小、更易于管理。通过 WasmEdge,可以在设备上安全地运行第三方开发者用 C、C++、Rust 等语言编写的 Wasm 程序,扩大 OpenHarmony 的开发者群体。 WasmEdge 相当于 OpenHarmony 的一个完全开源的开发执行环境。社区开发者可以方便地运行编译好的 WebAssembly 程序,降低门槛。

WasmEdge 目前已经支持了 Linux、macOS、Windows 与 实时操作系统 seL4。添加 OpenHarmony 的支持,将丰富 WasmEdge 的生态。

介绍完毕 ,下面进入编程时间。请参考下面的教程从源码在 OpenHarmony 开发板中构建和测试 WasmEdge。

  1. 全量编译 OpenHarmony OS
  2. 获取 WasmEdge 源码
  3. 修改 OpenHarmony 标准系统配置文件
  4. 构建 WasmEdge 与 OpenHarmony
  5. 烧录到开发板
  6. 运行 WasmEdge 提供的测试用例

配合视频观看,效果更佳。

OpenHarmony 标准系统

OpenHarmony 标准系统为开发者提供的 Docker 环境封装了对应的编译工具链,本文档主要介绍在 Docker 环境下构建 WasmEdge 的步骤.

OpenHarmony 源码的获取与编译可以参考 Open Harmony 提供的文档 搭建Ubuntu环境-Docker方式

请注意,在构建 WasmEdge 前需要将 Openharmony 进行一次全量编译以便后续 WasmEdge 的交叉编译过程.

# 获取到 docker 镜像后
$ docker run -it -v $(pwd):/home/openharmony openharmony-docker-standard:0.0.5
$ ./build.sh --product-name Hi3516DV300

获取 WasmEdge 源码

OpenHarmony 将第三方库项目放在了 third_party 文件夹下,因此我们需要在 third_party 文件夹下获取 WasmEdge 源码。

这之后,用户可以根据需要更改路径并修改相关配置文件中的路径。

$ docker run -it -v $(pwd):/home/openharmony openharmony-docker-standard:0.0.5
$ cd third_party
$ git clone https://github.com/WasmEdge/WasmEdge.git
$ cd WasmEdge

修改 OpenHarmony 标准系统配置文件

添加 WasmEdge 子系统配置

修改 OpenHarmony 的 build 目录下的 subsystem_config.json 文件,如下添加 wasmedge 子系统。

{
  ...
  
  "wasmedge": {
    "path": "third_party/WasmEdge",
    "name": "wasmedge"
  },
  
  ...
}

将组件添加到产品配置中

修改 OpenHarmony 产品配置文件,标准系统对应的配置文件为:productdefine/common/products/Hi3516DV300.json。 在该配置文件中添加 "wasmedge:wasmedge":{},表示该产品会编译并打包 wasmedge 子系统下的 wasmedge 模块到版本中。

{
  ...
  "parts":{
    ...
    "wasmedge:wasmedge":{}
  }
}

构建 WasmEdge 与 OpenHarmony

在 OpenHarmony 中构建的 WasmEdge 目前仅支持 wasmedge,即 wasm 的通用运行时。

  • wasmedge 可以在解释器模式下执行一个 WASM 文件, 也可以执行从 WASM 文件 AOT 预编译产生的机器码二进制格式文件。但目前还不支持在 OpenHarmony 中对 WASM 文件进行 AOT 预编译 。

执行构建脚本

执行 WasmEdge 源码下的 utils/build_for_ohos.sh 命令行脚本,将自动执行以下工作:

  1. .gn 等 OpenHarmony 需要的构建配置文件移动到 WasmEdge 项目根目录;
  2. 使用 OpenHarmony 的编译工具链进行交叉编译构建 WasmEdge;
  3. 运行 OpenHarmony 的构建脚本 build.sh 进行全量编译,该步骤将 wasmedge 添加进 OpenHarmony OS;
$ docker run -it -v $(pwd):/home/openharmony openharmony-docker-standard:0.0.5
$ cd third_party/WasmEdge/utils/ohos
$ ./build_for_ohos.sh /home/openharmony

当 terminal 显示以下信息时,表明编译完成.

...

post_process
=====build Hi3516DV300 successful.
2021-12-15 03:18:50
++++++++++++++++++++++++++++++++++++++++

检查 wasmedge 是否编译打包进 OpenHarmony OS。

$ cd /home/openharmony/out/ohos-arm-release/packages/phone/system/bin
$ ls 

当输出的文件名中存在 wasmedge 时,就表明 WasmEdge 已经成功导入到 OpenHarmony OS。

将重新编译后的 OpenHarmony 标准系统镜像烧录进开发板,具体见 OpenHarmony 提供的文档 Hi3516DV300 开发板烧录

WasmEdge 在 tools/wasmedge/examples/ 文件夹提供了测试样例。在 OpenHarmony 标准系统中,这些样例写入了 system 镜像中,依然可以进行测试。通过串口工具连接上开发板并启动OpenHarmony 标准系统后,我们就可以进行以下的测试。

# cd /system/usr/wasmedge_example
# wasmedge hello.wasm 1 2 3
hello
1
2
3
# wasmedge --reactor add.wasm add 2 2
4
# wasmedge --reactor fibonacci.wasm fib 8
34
# wasmedge --reactor factorial.wasm fac 12
479001600
#
# cd js
# wasmedge --dir .:. qjs.wasm hello.js 1 2 3
Hello 1 2 3

接下来,你可以参考 WasmEdge Book 在 OpenHarmony 标准系统中使用 WasmEdge Runtime 来运行你自己的 WebAssembly 应用。

移植过程踩过的坑

最后和大家分享一下,在移植 WasmEdge 到 OpenHarmony OS 过程出现的一些问题与值得注意的地方。

cmake 项目进行交叉编译需要配置工具链,官方的交叉编译配置给出了参考,但需要在此基础上细化,如指明 Clang 及 Clang++ 的位置。此外,标准版 sysroot 的路径也有所不同,具体可以参考 WasmEdge 中的配置:

  set(TOOLSCHAIN_PATH "${OHOS_DIR_PATH}/prebuilts/clang/ohos/linux-x86_64/llvm")
  set(TOOLCHAIN_HOST "${TOOLSCHAIN_PATH}/bin")
  set(OHOS_SYSROOT_PATH "${OHOS_DIR_PATH}/out/ohos-arm-release/obj/third_party/musl")
  set(CMAKE_SYSROOT ${OHOS_SYSROOT_PATH})
  set(CMAKE_CROSSCOMPILING TRUE)
  set(CMAKE_SYSTEM_NAME "Generic")
  set(CMAKE_CXX_COMPILER_ID Clang)
  set(CMAKE_TOOLCHAIN_PREFIX llvm-)
  set(LLVM_PATH "${OHOS_DIR_PATH}/prebuilts/clang/ohos/linux-x86_64/llvm")
  include_directories(${LLVM_PATH}/include/c++/v1)
  include_directories(${OHOS_SYSROOT_PATH}/usr/include/arm-linux-ohosmusl)
  link_directories(${OHOS_SYSROOT_PATH}/usr/lib/arm-linux-ohosmusl)
  set(TOOLCHAIN_CC "${TOOLCHAIN_HOST}/clang")
  set(TOOLCHAIN_CXX "${TOOLCHAIN_HOST}/clang++")
  set(CMAKE_C_COMPILER ${TOOLCHAIN_CC})
  set(CMAKE_C_FLAGS "--target=arm-linux-ohosmusl -D__clang__ -march=armv7-a -mfloat-abi=softfp -mtune=generic-armv7-a -mfpu=neon -mthumb -w --sysroot=${OHOS_SYSROOT_PATH}")
  set(CMAKE_CXX_COMPILER ${TOOLCHAIN_CXX})
  set(CMAKE_CXX_FLAGS "--target=arm-linux-ohosmusl -D__clang__ -march=armv7-a -mfloat-abi=softfp -mtune=generic-armv7-a -mfpu=neon -mthumb -w --sysroot=${OHOS_SYSROOT_PATH}")
  set(MY_LINK_FLAGS "--target=arm-linux-ohosmusl --sysroot=${OHOS_SYSROOT_PATH}")
  set(CMAKE_LINKER clang)
  set(CMAKE_CXX_LINKER clang++)
  set(CMAKE_C_LINKER clang)
  set(CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINKER} ${MY_LINK_FLAGS} <FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
  set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINKER} ${MY_LINK_FLAGS} <FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")

编译工具链

OpenHarmony OS 使用 gn+ninja 进行编译。对于 cmake 组织编译的项目来说,官方提供的 cmake 项目移植文档给出的案例是基于轻量级系统的,并不完全适用于标准版系统上的移植。要想将项目写入 OpenHarmony OS 编译生成的镜像烧录到开发板上,需要编写 gn 脚本参与进 OpenHarmony OS 的编译过程。

在 WasmEdge 编译过程中,需要使用到 spqlog 项目,构建过程中存在 spdlog 项目拉取及编译生成 sqdlog 静态库的动作,这意味着只将 WasmEdge 相关库的编译过程改写为 gn 脚本是不够的,还需要对 spdlog 的编译过程进行改写,使得工作量急剧增加。

那么对于项目所依赖但并不属于 OpenHarmony OS 中的静态库模块,编译过程中要如何将这一模块引入 OpenHarmony OS 呢?

OpenHarmony OS 提供的 gn 编写模板中有 ohos_copy ,它可以将生成的静态库移至生成的目标文件夹。这样在真正编译需要链接时,就能将这一静态库视为 OpenHarmony OS 的原生模块而不是查无此库。在 WasmEdge 的移植过程中,所执行的编译脚本便是事先进行一遍交叉编译,生成需要 copy 的 spdlog 静态库,然后再执行 OpenHarmony OS 的编译脚本,从而按照项目目录下的 BUILD.gn 内的定义组织编译。

在 WasmEdge 的 BUILD.gn 中,关于 spdlog 静态库的描述如下:

ohos_copy("spdlog"){
    sources = [
        "$WASMEDGE_ROOT_DIR/build/_deps/spdlog-build/libspdlog.a",
    ]
    outputs = [
        target_out_dir + "/lib/libspdlog.a"
    ]
    module_install_name = ""
}

平时我们常用的标准 C 库是 GNU 发布的 libc 库,而 OpenHarmony 中使用的是 Musl-libc,因此如果需要移植的项目代码中使用了 glibc 的宏变量的代码,那么需要进行修改或者在开头重新定义为 Musl-libc 中的宏变量。

../../third_party/WasmEdge/lib/system/allocator.cpp:64:40: error: unused variable 'k4G' [-Werror,-Wunused-const-variable]
static inline constexpr const uint64_t k4G = UINT64_C(0x100000000);
                                       ^
../../third_party/WasmEdge/lib/system/allocator.cpp:65:40: error: unused variable 'k12G' [-Werror,-Wunused-const-variable]
static inline constexpr const uint64_t k12G = UINT64_C(0x300000000);
                                       ^
1 warning and 2 errors generated.

诸如这类报错,在 BUILD.gn 中使用到该源码的模块中添加 cflags.例如,对上面的报错,可以添加如下的 cflags

cflags = [
  ...
  "-Wno-unused-const-variable",
  ...
]

如果出现下面的 C++ 的链接编译报错,

../../third_party/WasmEdge/lib/host/wasi/inode-linux.cpp:745:3: error: cannot use 'try' with exceptions disabled
  try {
  ^

则添加 cflags_cc:

# BUILD.gn 使用到该源码的相应模块
{
  ...
  cflags_cc = [ 
    ...
    "-fexceptions",
    ...
  ]
}

移植过程中还有许多诸如此类的编译报错,在此不进行一一列举,有兴趣的朋友欢迎在下面留言,一起讨论。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK