In my post Graviton2: ARM comes to Lambda I showed that it is very easy to cross-compile Go code to run on ARM64 AWS Lambda functions. That’s true as long as your code is 100% Go - as soon as there’s any C code involved it feels impossible. I mean, who wants to deal with this error message?

cgo error message

It turns out there’s a solution: to use Zig to compile and link your C code. Zig is an extremely interesting new programming language that seeks to replace C. It also happens to be able to compile C via zig cc. It can also cross-compile C. Check out this amazing blog post by its creator. Anyway, here’s how to do it:

SQLite and Go on ARM64 Lambda functions via Zig

Here’s a useless Lambda function:

package main

import (
	"context"
	"crawshaw.io/sqlite/sqlitex"
	"encoding/json"
	"github.com/aws/aws-lambda-go/lambda"
)

var dbpool *sqlitex.Pool

func main() {
	dbpool, _ = sqlitex.Open("file:memory:?mode=memory", 0, 1)
	lambda.Start(handle)
}

func handle(ctx context.Context, input json.RawMessage) (string, error) {
	conn := dbpool.Get(ctx)
	defer dbpool.Put(conn)

	// please excuse my complete lack of error handling
	stmt, _, _ := conn.PrepareTransient("SELECT 123")
	defer stmt.Finalize()

	stmt.Step()
	return stmt.ColumnText(0), nil
}

And here’s how you can compile it for ARM64 (and x86_64) from your laptop:

# build.sh
set -eux
export GOOS=linux
export CGO_ENABLED=1
export CC=$(pwd)/zcc.sh
export CXX=$(pwd)/zxx.sh

GOARCH=arm64 \
ZTARGET=aarch64-linux-musl \
go build -ldflags="-linkmode external" -o arm64/bootstrap

GOARCH=amd64 \
ZTARGET=x86_64-linux-musl \
go build -ldflags="-linkmode external" -o amd64/bootstrap

Here’s what those zcc.sh and zxx.sh files should look like:

# zcc.sh
#!/bin/sh
set -eu
export ZIG_SYSTEM_LINKER_HACK=1
export ZIG_LOCAL_CACHE_DIR="$HOME/.zigcache/"
zig cc -target $ZTARGET "$@"

# zxx.sh
#!/bin/sh
set -eu
export ZIG_SYSTEM_LINKER_HACK=1
export ZIG_LOCAL_CACHE_DIR="$HOME/.zigcache/"
zig c++ -target $ZTARGET "$@"

Finally, for completeness, a potential CloudFormation template for your functions:

Transform: AWS::Serverless-2016-10-31

Resources:
  Arm64:
    Type: AWS::Serverless::Function
    Properties:
      Handler: hello
      CodeUri: ./arm64/bootstrap
      Runtime: provided.al2
      Architectures: [arm64]

  Amd64:
    Type: AWS::Serverless::Function
    Properties:
      Handler: hello
      CodeUri: ./amd64/bootstrap
      Runtime: provided.al2
      Architectures: [x86_64]