Our bof-launcher project allows you to write, build, debug and execute BOFs using Zig, C and/or assembly language on Windows (x86/x64) and Linux (x86/x64/ARM/AARCH64).

Prerequisites

The only dependency is Zig compiler package (version 0.13.0 is required) which can be downloaded from here. This package contains Zig/C/C++ compilers in one executable and supports cross-compilation out of the box (single zig executable can produce binaries for any supported target).

First steps

Add zig executable from the package to your PATH environment variable and run:

git clone https://github.com/The-Z-Labs/bof-launcher
cd bof-launcher
zig build

The last command will build bof-luncher library, sample BOFs and example programs for all supported platforms in ReleaseSmall configuration. Check zig-out directory for build artifacts.

To execute one of the included BOFs run:

(Windows)

.\zig-out\bin\integration_with_c_win_x64.exe .\zig-out\bin\test_obj0.coff.x64.o

(Linux)

./zig-out/bin/integration_with_c_lin_x64 zig-out/bin/test_obj0.elf.x64.o

You should get output similar to the below:

<bof-filename>: .\zig-out\bin\test_obj0.coff.x64.o
File size is: 7129
Running BOF from command line C application...

--- test_obj0.zig ---
Hello, go!
func() it

Writting and building your first BOFs

Using Zig:

const beacon = @import("bof_api").beacon;

pub export fn go(_: ?[*]u8, _: i32) callconv(.C) u8 {
    _ = beacon.printf(0, "hello, bof!\n");
    return 0;
}

Using C:

#include "beacon.h"

unsigned char go(unsigned char* arg_data, int arg_len) {
    BeaconPrintf(0, "hello, bof!\n");
    return 0;
}

Save above files as:

bofs/src/firstBof.zig
bofs/src/firstBofC.c

We also support writing BOFs in assembly langauge (fasm) but this is a bit more advanced topic and will be described in one of the future posts. For now, take a look at sample BOFs if you are interested:

To add BOFs to the build system you need to edit bofs/build.zig file. Add below lines to the definition of bofs_my_custom array in this file. It should look like this:

const bofs_my_custom = [_]Bof{
    .{ .name = "firstBof", .formats = &.{ .elf, .coff }, .archs = &.{ .x64, .x86, .aarch64, .arm } },
    .{ .name = "firstBofC", .formats = &.{ .elf, .coff }, .archs = &.{ .x64, .x86, .aarch64, .arm } },
};

This will instruct the build system to build both BOFs for all supported platforms. From the root directory of the bof-launcher tree run:

zig build

BOFs will show up in zig-out/bin directory as:

firstBof.coff.x86.o
firstBof.coff.x64.o
firstBof.elf.x86.o
firstBof.elf.x64.o
firstBof.elf.arm.o
firstBof.elf.aarch64.o

firstBofC.coff.x86.o
firstBofC.coff.x64.o
firstBofC.elf.x86.o
firstBofC.elf.x64.o
firstBofC.elf.arm.o
firstBofC.elf.aarch64.o

To run your BOFs you can use integration_with_c example program:

.\zig-out\bin\integration_with_c_win_x64.exe .\zig-out\bin\firstBof.coff.x64.o
.\zig-out\bin\integration_with_c_win_x64.exe .\zig-out\bin\firstBofC.coff.x64.o

The source code of this program shows how to use bof-launcher library C API. Take a look at: examples/integration-with-c/main.c if you are interested.

Debugging BOFs

So far we have been building BOFs in ReleaseSmall configuration which is a default mode when you run:

zig build

This is equivalent to:

zig build -Doptimize=ReleaseSmall

In this mode all the BOFs are compiled to object files (COFF or ELF) with binary size optimizations and speed optimizations enabled.

You can also build BOFs as standalone executables with full debug symbols. To do so, run:

zig build -Doptimize=Debug

You can now run any BOF directly, for example:

.\zig-out\bin\test_obj0.coff.x64.exe

You can debug your BOFs in Visual Studio, WinDbg, gdb or any other debugger.

For example, to debug in WinDbg you can use below command:

WinDbgX.exe .\zig-out\bin\helloBof.coff.x64.exe

And then, inside WinDbg:

bp go
g

To debug in Visual Studio open BOF executable (File->Open->Project/Solution…), add new Function Breakpoint (CTRL+K, B) at go() and press F5.

If you want to debug on Linux in gdb, do:

gdb ./zig-out/bin/helloBof.elf.x64

And then inside gdb:

b go
run

Summary

This is all I wanted to show in the first part. We now know how to build the project, how to add our own custom BOFs to the build system and how to run/debug BOFs.

In the next part we will see how to pass arguments to the BOF, how to use bof-launcher library in an external project and we will also look at some unique features/extensions that bof-launcher project provides. Thanks for reading and stay tuned!