DEV Community

Cover image for Hello world from a WASM module in a static binary
Dario Castañé
Dario Castañé

Posted on

Hello world from a WASM module in a static binary

These are some quick notes documenting a Saturday morning's experiment. All the steps were executed on Linux.

TL;DR: What?

Compiling a static binary that runs a compiled Ahead-of-Time (AOT) WASM module.

Ideally it'd have been WASM bytecode compiled to native code, but I couldn't find a way that didn't rely on the WASM runtime, invoking it from the command line or embedded in a C program.

Why?

I've been thinking and learning this week about WebAssembly and its potential since I read "WASM will replace containers".

I decided initially to use Wasmer and ended filing a question on their repository because their own native binary build command doesn't work as expected.

Finally, I landed on Bytecode Alliance's WebAssembly Micro Runtime (WAMR) wamrc compiler.

How?

The code

(module
  ;; Import the required WASI functions
  (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)))

  ;; Define a buffer to store the message
  (memory (export "memory") 1)
  (data (i32.const 8) "Hello, World!\n")

  ;; Define the _start function
  (func $main (export "_start")
    ;; Setup the iovec array
    (i32.store (i32.const 0) (i32.const 8))    ;; pointer to the message
    (i32.store (i32.const 4) (i32.const 14))   ;; length of the message

    ;; Call fd_write
    (call $fd_write
      (i32.const 1)  ;; file_descriptor - 1 for stdout
      (i32.const 0)  ;; *iovs - pointer to the iovec array
      (i32.const 1)  ;; iovs_len - number of iovec entries
      (i32.const 20) ;; nwritten - where to store the number of bytes written
    )
    drop ;; Discard the result
  )
)
Enter fullscreen mode Exit fullscreen mode

This is WebAssembly Text. Learn more here.

The tools

Everything had to be compiled from scratch.

wasm-opt isn't really required, so I'll skip it. I learnt about it while looking at these benchmarks, and I want these notes to keep track of the interesting tidbits I discovered.

The process

First, let's generate WASM bytecode from our WebAssembly Text code:

wat2wasm ./hello_world.wat -o ./hello_world.wasm
Enter fullscreen mode Exit fullscreen mode

Next, compile it:

wamrc --format=aot -o ./hello_world.aot ./hello_world.wasm
Enter fullscreen mode Exit fullscreen mode

For the last step, we need some C glue to embed the WAMR runtime:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wasm_export.h"  // Now points to WAMR's core/iwasm/include/
#include "hello_world.h"

// Define a static pool buffer with proper alignment
#define POOL_SIZE (256 * 1024)  // 256 KB pool
static uint8_t global_pool_buf[POOL_SIZE] __attribute__((aligned(8)));  // 8-byte alignment

int main() {
    RuntimeInitArgs init_args;
    memset(&init_args, 0, sizeof(RuntimeInitArgs));

    // Configure memory pool
    init_args.mem_alloc_type = Alloc_With_Pool;  // Use pool allocator
    init_args.mem_alloc_option.pool.heap_buf = global_pool_buf;
    init_args.mem_alloc_option.pool.heap_size = sizeof(global_pool_buf);

    // Initialize the WAMR runtime
    if (!wasm_runtime_full_init(&init_args)) {
        printf("Runtime initialization failed.\n");
        return -1;
    }

    // Load the AOT module from the embedded blob
    char error_buf[128];
    wasm_module_t module = wasm_runtime_load(hello_world_aot, hello_world_aot_len, error_buf, sizeof(error_buf));
    if (!module) {
        printf("Load failed: %s\n", error_buf);
        return -1;
    }

    // Instantiate the module
    wasm_module_inst_t inst = wasm_runtime_instantiate(module, 65536, 65536, error_buf, sizeof(error_buf));
    if (!inst) {
        printf("Failed to instantiate: %s\n", error_buf);
        return -1;
    }

    // Call a function named "main" exported from the WASM module
    wasm_function_inst_t func = wasm_runtime_lookup_function(inst, "_start");
    if (!func) {
        printf("Function '_start' not found.\n");
        return -1;
    }

    wasm_exec_env_t env = wasm_runtime_create_exec_env(inst, 65536);
    if (!env) {
        printf("Failed to create environment.\n");
        return -1;
    }

    // Execute the function
    wasm_runtime_call_wasm(env, func, 0, NULL);

    // Cleanup
    wasm_runtime_deinstantiate(inst);
    wasm_runtime_unload(module);
    wasm_runtime_destroy();
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

The sharp reader may be asking "Where are the wasm_export.h and hello_world.h includes? The former is in wasm-micro-runtime/core/iwasm/include, and the later needs to be generated:

xxd -i hello_world.aot > hello_world.h
Enter fullscreen mode Exit fullscreen mode

And we can compile and run it:

gcc -static $HOME/Code/hello-wasm/main.c $HOME/Code/wasm-micro-runtime/product-mini/platforms/linux/build/libiwasm.a -lm -I $HOME/Code/wasm-micro-runtime/core/iwasm/include -I $HOME/Code/hello-wasm -o $HOME/Code/hello-wasm/hello_world

$HOME/Code/hello-wasm/hello_world
Hello, World!
Enter fullscreen mode Exit fullscreen mode

Note: it's not a perfect statically-linked binary. It depends on getaddrinfo, but this is better left out of scope right now.

Image by Duncan Cumming.

Image of Timescale

PostgreSQL for Agentic AI — Build Autonomous Apps on One Stack ☝️

pgai turns PostgreSQL into an AI-native database for building RAG pipelines and intelligent agents. Run vector search, embeddings, and LLMs—all in SQL

Build Today

Top comments (0)

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay