# How to Build the QEMU CXL PoC with build.sh

> Easily build the QEMU CXL PoC with build.sh. Automate compilation of bootloader and payload, creating a bootable poc.img for QEMU.

- Repository: [bikini/exploitarium](https://github.com/bikini/exploitarium)
- Tags: how-to-guide
- Published: 2026-09-07

---

**Run `sh build.sh` inside the `qemu-cxl-type3-mailbox-escape-poc` directory to automate the compilation of the 16-bit bootloader, 32-bit stage-2 payload, and the creation of a bootable `poc.img` floppy image ready for QEMU.**

The `bikini/exploitarium` repository hosts a proof-of-concept that demonstrates a **CXL Type-3 mailbox escape** vulnerability in QEMU. To build the QEMU CXL PoC with [`build.sh`](https://github.com/bikini/exploitarium/blob/main/build.sh), you need only standard development toolchains and a single shell command that orchestrates assembly, compilation, linking, and image padding into one deterministic pipeline.

## Prerequisites for Building the QEMU CXL PoC

Before executing the build script, ensure your system provides the following tools as specified in the repository’s [`README.md`](https://github.com/bikini/exploitarium/blob/main/README.md):

- **NASM** – Assembles the real-mode bootloader (`boot.asm`) into raw machine code.
- **GCC with 32-bit support** – Compiles [`stage2.c`](https://github.com/bikini/exploitarium/blob/main/stage2.c) as a freestanding 32-bit object using flags like `-m32` and `-ffreestanding`.
- **GNU ld** – Links the stage-2 object using the custom linker script `stage2.ld`.
- **Python 3** – Executes the inline padding script that concatenates binaries and pads the final image to a 1.44 MiB floppy size (`1474560` bytes).

On Debian-based distributions, you can typically install these with:

```bash
sudo apt-get install nasm gcc-multilib python3

```

## Architecture of the Build Process

The [`build.sh`](https://github.com/bikini/exploitarium/blob/main/build.sh) script (located at [`qemu-cxl-type3-mailbox-escape-poc/build.sh`](https://github.com/bikini/exploitarium/blob/main/qemu-cxl-type3-mailbox-escape-poc/build.sh)) transforms three source components into a single bootable disk image through four distinct stages.

### Bootloader Assembly (boot.asm)

The 16-bit bootloader is assembled by **NASM** into a raw binary. This 512-byte sector loads the protected-mode stage from the floppy into memory and jumps to it.

```bash
nasm -f bin boot.asm -o boot.bin

```

### Stage-2 Payload Compilation (stage2.c)

The freestanding 32-bit C program ([`stage2.c`](https://github.com/bikini/exploitarium/blob/main/stage2.c)) contains the exploit logic that interacts with PCI configuration space and the CXL mailbox. It is compiled without standard libraries or position-independent code:

```bash
gcc -m32 -ffreestanding -nostdlib -fno-builtin -fno-pic -fno-pie -fno-stack-protector -Wall -Wextra -c stage2.c -o stage2.o

```

### Linking with stage2.ld

The linker script `stage2.ld` defines the memory layout for the flat binary output. The linker produces a raw binary blob that can be concatenated directly after the bootloader:

```bash
ld -m elf_i386 -T stage2.ld --oformat binary stage2.o -o stage2.bin

```

### Image Assembly and Padding

An embedded Python fragment pads `stage2.bin` to the correct alignment, concatenates it with `boot.bin`, and pads the entire image to exactly **1,474,560 bytes** (standard 1.44 MiB floppy geometry). The result is written to `poc.img`.

## Step-by-Step Build Instructions

Follow these steps to produce the bootable exploit image:

1. **Navigate to the PoC directory**

   ```bash
   cd qemu-cxl-type3-mailbox-escape-poc
   ```

2. **Execute the build script**

   ```bash
   sh build.sh
   ```

   This command runs the full pipeline defined in lines 3–14 of [`build.sh`](https://github.com/bikini/exploitarium/blob/main/build.sh): assembling `boot.asm`, compiling [`stage2.c`](https://github.com/bikini/exploitarium/blob/main/stage2.c), linking with `stage2.ld`, and invoking the Python padding logic. Finally, `sha256sum` prints checksums for the generated artifacts to verify integrity.

3. **Verify the output**

   Upon successful completion, the terminal displays size metrics confirming correct assembly:

   ```

   boot=512 stage=4560 image=1474560
   ```

   You can also inspect the final image:

   ```bash
   ls -lh poc.img
   # -rw-r--r-- 1 user user 1.4M Jul 12 10:23 poc.img

   ```

## Build Verification and Artifacts

After the script finishes, the directory contains three key files:

- **`boot.bin`** – The 512-byte master boot record.
- **`stage2.bin`** – The flat-binary payload (size varies by compilation, typically ~4–5 KB).
- **`poc.img`** – The final 1.44 MiB floppy image containing the concatenated bootloader and padded stage-2 binary.

The `sha256sum` output at the end of the build provides cryptographic hashes for these files, allowing you to verify reproducible builds across environments.

## Running the PoC After Building

With `poc.img` generated, you can launch the exploit using the companion [`run.sh`](https://github.com/bikini/exploitarium/blob/main/run.sh) script. Supply the path to a QEMU binary that includes CXL Type-3 device emulation:

```bash
sh run.sh /usr/bin/qemu-system-x86_64

```

If the exploit succeeds, the script will display confirmation that the host-side marker file `/tmp/qemu_cxl_escape_marker` was created, proving the mailbox escape from guest to host.

## Summary

- **Single command build**: Execute `sh build.sh` in the `qemu-cxl-type3-mailbox-escape-poc` directory to trigger the complete pipeline.
- **Multi-stage compilation**: The script orchestrates NASM for assembly, GCC 32-bit freestanding compilation, custom LD linking, and Python image padding.
- **Deterministic output**: The process always produces a 1.44 MiB `poc.img` ready for QEMU, with checksums provided for verification.
- **No manual intervention**: All steps—from `boot.asm` to the final floppy geometry—are automated in [`build.sh`](https://github.com/bikini/exploitarium/blob/main/build.sh) lines 3–14.

## Frequently Asked Questions

### What does the build.sh script automate?

The [`build.sh`](https://github.com/bikini/exploitarium/blob/main/build.sh) script automates the entire toolchain invocation sequence. It assembles the 16-bit bootloader with NASM, compiles the 32-bit C payload with GCC freestanding flags, links it using `stage2.ld`, pads the binary to floppy geometry with Python, and generates SHA-256 checksums for the resulting artifacts.

### Why does the build require 32-bit GCC support?

The guest payload in [`stage2.c`](https://github.com/bikini/exploitarium/blob/main/stage2.c) runs as a freestanding 32-bit protected-mode program inside the QEMU guest. The `-m32` flag ensures the compiler targets the i386 instruction set, while `-ffreestanding -nostdlib` prevents linkage against the host’s standard library, producing a bare-metal binary suitable for the custom bootloader.

### How can I verify the build succeeded?

Check the terminal output for the size verification line (`boot=512 stage=4560 image=1474560`) and confirm that `poc.img` exists with a size of exactly 1,474,560 bytes. The `sha256sum` output printed by [`build.sh`](https://github.com/bikini/exploitarium/blob/main/build.sh) also allows you to compare hashes against known-good values to ensure the binaries were constructed correctly.

### Where is the final bootable image located?

The script writes the completed floppy image to **`poc.img`** in the current working directory (`qemu-cxl-type3-mailbox-escape-poc/`). This file isReady to be mounted by QEMU as a floppy disk using the `-fda` parameter or the configuration provided in [`run.sh`](https://github.com/bikini/exploitarium/blob/main/run.sh).