Build System
Relevant source files
The following files were used as context for generating this wiki page:
Purpose and Scope
This document describes the eCapture build system, including Makefile targets, compilation processes, cross-compilation support, asset embedding, and CI/CD workflows. The build system handles both CO-RE (Compile Once - Run Everywhere) and non-CO-RE eBPF bytecode compilation, produces multiple package formats, and supports cross-platform builds for x86_64 and arm64 architectures.
For information about eBPF program development and structure, see eBPF Program Development. For release and deployment details, see CI/CD and Release Process.
Build System Architecture
The build system is orchestrated through a main Makefile that delegates to specialized makefiles for different build targets. The overall architecture follows a multi-stage process: eBPF compilation, asset embedding, and Go binary compilation.
Build Flow Overview
Sources: Makefile:1-245, functions.mk:1-76
Primary Build Targets
The Makefile provides several top-level targets for different build scenarios. Each target represents a complete build pipeline from source to binary.
| Target | Description | eBPF Mode | Use Case |
|---|---|---|---|
all | Full build with CO-RE and non-CO-RE bytecode | Both | Development and testing on systems with BTF support |
nocore / noncore | Build with non-CO-RE bytecode only | Non-CO-RE only | Production builds for maximum compatibility |
ebpf | Compile CO-RE eBPF programs only | CO-RE | Testing eBPF changes without full rebuild |
ebpf_noncore | Compile non-CO-RE eBPF programs only | Non-CO-RE | Testing non-CO-RE eBPF changes |
assets | Embed all bytecode files into Go | Both | Manual asset generation |
assets_noncore | Embed non-CO-RE bytecode only | Non-CO-RE | Manual asset generation for non-CO-RE builds |
build | Compile Go binary with CO-RE assets | CO-RE | Manual binary compilation |
build_noncore | Compile Go binary with non-CO-RE assets | Non-CO-RE | Manual binary compilation |
clean | Remove build artifacts | N/A | Clean slate for fresh builds |
env | Display build environment variables | N/A | Debugging build configuration |
Sources: Makefile:4-11, Makefile:106-245
Target Dependency Graph
Sources: Makefile:6-11, Makefile:134-201
CO-RE vs Non-CO-RE Compilation
eCapture supports two eBPF compilation modes: CO-RE (Compile Once - Run Everywhere) and non-CO-RE. The build system handles both modes with different compilation flags and kernel header requirements.
CO-RE Compilation
CO-RE bytecode is compiled with BTF (BPF Type Format) support, allowing a single binary to work across different kernel versions without recompilation.
Compilation Command:
clang -D__TARGET_ARCH_$(LINUX_ARCH) \
-target bpfel \
-O2 -g -Wall -Werror \
-c kern/source.c \
-o user/bytecode/source_core.oKey characteristics:
- Uses
-target bpfelfor BPF ELF format - Requires BTF support in the kernel (Linux 5.2+)
- Generates
*_core.ofiles inuser/bytecode/ - Includes CO-RE relocations for structure field offsets
- Requires
vmlinux.hgenerated bybpftool
Sources: Makefile:117-127
Non-CO-RE Compilation
Non-CO-RE bytecode is compiled against specific kernel headers, requiring kernel source to be available at build time. This mode provides broader compatibility with older kernels.
Compilation Command:
clang \
-I $(KERN_SRC_PATH)/arch/$(LINUX_ARCH)/include \
-I $(KERN_BUILD_PATH)/arch/$(LINUX_ARCH)/include/generated \
-I $(KERN_SRC_PATH)/include \
-c kern/source.c \
-o - | llc -march=bpf -filetype=obj \
-o user/bytecode/source_noncore.oKey characteristics:
- Two-stage compilation:
clang→ LLVM IR →llc→ BPF object - Requires kernel source at build time
- Generates
*_noncore.ofiles inuser/bytecode/ - No CO-RE relocations; structure offsets are hardcoded
- Works on kernels without BTF support
Sources: Makefile:139-159
Compilation Comparison
| Aspect | CO-RE | Non-CO-RE |
|---|---|---|
| Kernel Headers | vmlinux.h only | Full kernel source tree |
| Portability | Single binary for all kernels | Kernel-specific binary |
| Kernel Requirement | Linux 5.2+ with BTF | Linux 4.15+ |
| Build Output | *_core.o | *_noncore.o |
| Compilation Tool | clang only | clang + llc |
| Structure Offsets | CO-RE relocations | Hardcoded at compile time |
Sources: Makefile:117-159
Cross-Compilation Support
The build system supports cross-compilation for arm64 and amd64 architectures. Cross-compilation is controlled by the CROSS_ARCH environment variable.
Cross-Compilation Architecture
Sources: Makefile:99-104, .github/workflows/go-c-cpp.yml:56-65
Cross-Compilation Examples
Build for arm64 on x86_64 host:
CROSS_ARCH=arm64 make allBuild for amd64 on arm64 host:
CROSS_ARCH=amd64 make allAndroid build for arm64:
ANDROID=1 CROSS_ARCH=arm64 make nocoreSources: Makefile:92-96, .github/workflows/go-c-cpp.yml:56-65
Kernel Source Requirements
For non-CO-RE cross-compilation, the build system requires prepared kernel headers for the target architecture. The prepare target handles this:
prepare:
if [ -d "$(LINUX_SOURCE_PATH)" ]; then \
$(CMD_CD) $(LINUX_SOURCE_PATH) && $(KERNEL_HEADER_GEN) || exit 1; \
elif [ -n "$(CROSS_ARCH)" ]; then \
echo "linux source not found"; exit 1; \
fiThe kernel headers are prepared with architecture-specific commands:
- For arm64:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- prepare - For x86_64:
make ARCH=x86 CROSS_COMPILE=x86_64-linux-gnu- prepare
Sources: Makefile:98-104, .github/workflows/go-c-cpp.yml:25-32, .github/workflows/go-c-cpp.yml:86-92
Asset Embedding with go-bindata
The build system uses go-bindata to embed all compiled eBPF bytecode files into the Go binary. This allows eCapture to be distributed as a single executable without external dependencies on bytecode files.
Asset Embedding Process
Sources: Makefile:162-171
Asset Generation Commands
Full asset generation (CO-RE + non-CO-RE):
assets: ebpf ebpf_noncore
$(CMD_GO) run github.com/shuLhan/go-bindata/cmd/go-bindata \
$(IGNORE_LESS52) -pkg assets -o "assets/ebpf_probe.go" \
$(wildcard ./user/bytecode/*.o)Non-CO-RE only asset generation:
assets_noncore: ebpf_noncore
$(CMD_GO) run github.com/shuLhan/go-bindata/cmd/go-bindata \
$(IGNORE_LESS52) -pkg assets -o "assets/ebpf_probe.go" \
$(wildcard ./user/bytecode/*.o)The IGNORE_LESS52 variable excludes older OpenSSL 1.0.2 and 1.1.0 versions to reduce binary size for builds targeting newer systems.
Sources: Makefile:162-171
Asset Usage in Code
The generated assets/ebpf_probe.go file provides an Asset() function that modules use to retrieve embedded bytecode:
// Example usage in module code
bytecode, err := assets.Asset("openssl_3_0_0_kern_core.o")This allows the module system to select and load the appropriate eBPF bytecode based on detected library versions at runtime.
Sources: Makefile:162-171
libpcap Static Library Compilation
eCapture requires libpcap for packet capture functionality. The build system compiles libpcap as a static library to avoid runtime dependencies.
libpcap Build Configuration
$(TARGET_LIBPCAP):
test -f ./lib/libpcap/configure || git submodule update --init
cd lib/libpcap && \
CC=$(CMD_CC_PREFIX)$(CMD_CC) AR=$(CMD_AR_PREFIX)$(CMD_AR) \
CFLAGS="-O2 -g -gdwarf-4 -static -Wno-unused-result" \
./configure \
--disable-rdma \
--disable-shared \
--disable-usb \
--disable-netmap \
--disable-bluetooth \
--disable-dbus \
--without-libnl \
--without-dpdk \
--without-dag \
--without-septel \
--without-snf \
--without-gcc \
--with-pcap=linux \
--without-turbocap \
--host=$(LIBPCAP_ARCH) && \
CC=$(CMD_CC_PREFIX)$(CMD_CC) AR=$(CMD_AR_PREFIX)$(CMD_AR) makeKey configuration points:
--disable-shared: Build only static library (.a)--with-pcap=linux: Use Linux native packet capture--host=$(LIBPCAP_ARCH): Cross-compilation target tripletCFLAGSincludes-staticand-gdwarf-4for debugging
Sources: Makefile:175-184
CGO Integration
The compiled libpcap static library is linked into the Go binary via CGO flags:
CGO_ENABLED=1
CGO_CFLAGS='-O2 -g -I$(CURDIR)/lib/libpcap/'
CGO_LDFLAGS='-O2 -g -L$(CURDIR)/lib/libpcap/ -lpcap -static'This produces a fully statically-linked binary with no external library dependencies.
Sources: functions.mk:47-54
Build Variables and Configuration
The build system uses numerous variables defined in variables.mk and can be overridden via environment variables or command-line arguments.
Key Build Variables
| Variable | Default / Detection | Purpose |
|---|---|---|
CROSS_ARCH | Empty (native) | Target architecture: arm64 or amd64 |
ANDROID | 0 | Enable Android-specific build flags |
DEBUG | 0 | Enable debug symbols and verbose output |
SNAPSHOT_VERSION | Git describe | Version string for release builds |
CLANG_VERSION | Detected from clang --version | Clang major version number |
GO_VERSION | Detected from go version | Go version (must be 1.24+) |
HOST_ARCH | uname -m | Host machine architecture |
TARGET_ARCH | Derived from CROSS_ARCH | Target architecture for compilation |
GOARCH | Derived from TARGET_ARCH | Go architecture: amd64 or arm64 |
LINUX_ARCH | Derived from TARGET_ARCH | Linux kernel architecture: x86 or arm64 |
KERN_RELEASE | uname -r | Kernel version for native builds |
LINUX_SOURCE_PATH | Auto-detected in /usr/src | Path to kernel source for non-CO-RE |
BPF_NOCORE_TAG | Kernel version tag | Tag for non-CO-RE bytecode versioning |
Sources: Makefile:1-2, Makefile:20-63
Environment Display
The env target displays all build configuration:
make envExample output:
eCapture Makefile Environment:
---------------------------------------
PARALLEL -j8
----------------[ from args ]---------------
CROSS_ARCH arm64
ANDROID 0
DEBUG 1
---------------------------------------
HOST_ARCH x86_64
CLANG_VERSION 14
GO_VERSION 1.24.6
TARGET_ARCH arm64
GOARCH arm64
LINUX_ARCH arm64
---------------------------------------Sources: Makefile:19-63
Makefile Functions
The functions.mk file provides reusable functions for build logic.
Version Check Functions
.checkver_$(CMD_CLANG):
@if [ ${CLANG_VERSION} -lt 9 ]; then
echo "you MUST use clang 9 or newer"
exit 1
fi
.checkver_$(CMD_GO):
@if [ ${GO_VERSION_MAJ} -eq 1 ]; then
if [ ${GO_VERSION_MIN} -lt 24 ]; then
echo "you MUST use golang 1.24 or newer"
exit 1
fi
fiThese check functions ensure minimum tool versions:
- Clang 9+
- Go 1.24+
- bpftool (for CO-RE vmlinux.h generation)
Sources: functions.mk:12-40
Go Build Function
define gobuild
CGO_ENABLED=1 \
CGO_CFLAGS='-O2 -g -gdwarf-4 -I$(CURDIR)/lib/libpcap/' \
CGO_LDFLAGS='-O2 -g -L$(CURDIR)/lib/libpcap/ -lpcap -static' \
GOOS=linux GOARCH=$(GOARCH) CC=$(CMD_CC_PREFIX)$(CMD_CC) \
$(CMD_GO) build -trimpath -buildmode=pie -mod=readonly \
-tags '$(TARGET_TAG),netgo' \
-ldflags "-w -s \
-X 'github.com/gojue/ecapture/cli/cmd.GitVersion=$(TARGET_TAG)_$(GOARCH):$(VERSION_NUM):$(VERSION_FLAG)' \
-X 'github.com/gojue/ecapture/cli/cmd.ByteCodeFiles=$(BYTECODE_FILES)' \
-linkmode=external -extldflags -static" \
-o $(OUT_BIN)
endefThis function compiles the Go binary with:
- Static linking (
-linkmode=external -extldflags -static) - PIE (Position Independent Executable) for security
- Version information embedded via
-ldflags -X - CGO enabled with libpcap linkage
Sources: functions.mk:47-54
Release Tar Function
define release_tar
$(call allow-override,TAR_DIR,ecapture-$(DEB_VERSION)-$(1)-$(GOARCH)$(CORE_PREFIX))
$(call allow-override,OUT_ARCHIVE,$(OUTPUT_DIR)/$(TAR_DIR).tar.gz)
$(CMD_MAKE) clean
ANDROID=$(ANDROID) $(CMD_MAKE) $(2)
$(CMD_MKDIR) -p $(TAR_DIR)
$(CMD_CP) LICENSE $(TAR_DIR)/
$(CMD_CP) CHANGELOG.md $(TAR_DIR)/
$(CMD_CP) README.md $(TAR_DIR)/
$(CMD_CP) $(OUTPUT_DIR)/ecapture $(TAR_DIR)/
$(CMD_TAR) -czf $(OUT_ARCHIVE) $(TAR_DIR)
endefThis function creates release archives with documentation and the binary.
Sources: functions.mk:62-76
CI/CD Pipeline
The CI/CD system is implemented using GitHub Actions with two primary workflows: continuous integration and release.
Continuous Integration Workflow
Sources: .github/workflows/go-c-cpp.yml:1-128
Key CI Steps
- Compiler Installation: Installs clang-14, llvm-14, and cross-compilation toolchains
- Kernel Header Preparation: Extracts and prepares Linux kernel headers for both native and cross-compilation
- CO-RE Build: Compiles with full CO-RE support and runs linter
- Non-CO-RE Build: Compiles kernel-specific bytecode
- Cross-Compilation: Builds for opposite architecture (x86→arm64, arm64→x86)
- Android Build: Creates Android-compatible non-CO-RE binaries
- Testing: Runs unit tests with race detector
Sources: .github/workflows/go-c-cpp.yml:12-67, .github/workflows/go-c-cpp.yml:69-127
Release Process
The release workflow is triggered by version tags and produces multiple artifact types.
Release Workflow
Sources: .github/workflows/release.yml:1-129
Release Makefile Targets
The builder/Makefile.release provides specialized targets for creating release artifacts:
| Target | Description | Output |
|---|---|---|
snapshot | Build Linux non-CO-RE release | ecapture-*-linux-*.tar.gz |
snapshot_android | Build Android non-CO-RE release | ecapture-*-android-*.tar.gz |
build_deb | Create Debian package | ecapture_*_*.deb |
publish | Upload all artifacts to GitHub | GitHub Release with all files |
Sources: builder/Makefile.release:1-151
Release Archive Structure
Each release archive includes:
ecapturebinary (statically linked)LICENSEfileCHANGELOG.mdREADME.mdREADME_CN.md
Sources: builder/Makefile.release:69-75
Package Formats
DEB Package
The build system creates Debian packages with the following structure:
ecapture_<version>_<arch>.deb
├── DEBIAN/
│ └── control (metadata)
└── usr/local/bin/
└── ecapture (binary)Control file fields:
- Package: ecapture
- Version: Semantic version (e.g., 1.0.0)
- Architecture: amd64 or arm64
- Maintainer: Package maintainer info
- Description: eCapture description
- Homepage: https://ecapture.cc
Sources: builder/Makefile.release:134-151
RPM Package
RPM packages can be built using the rpm target with a spec file:
make rpm VERSION=0.0.0 RELEASE=1The spec file is located at builder/rpmBuild.spec.
Sources: Makefile:65-71
Docker Image
Multi-architecture Docker images are built using a two-stage Dockerfile:
Stage 1: Builder
- Base:
ubuntu:22.04 - Installs compilers and Go
- Builds eCapture with
make all
Stage 2: Runtime
- Base:
alpine:latest - Contains only the
ecapturebinary - Entrypoint:
/ecapture
Sources: builder/Dockerfile:1-39
Build Environment Setup
For developers setting up a build environment, the builder/init_env.sh script automates installation of all dependencies.
Automated Setup Script
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/gojue/ecapture/master/builder/init_env.sh)"This script:
- Detects Ubuntu version and selects appropriate Clang version
- Detects host architecture (x86_64 or aarch64)
- Installs build dependencies via apt-get
- Installs cross-compilation toolchain
- Downloads and extracts Linux kernel source
- Prepares kernel headers for native and cross-compilation
- Downloads and installs Go 1.24.6
- Clones the eCapture repository
Supported Ubuntu versions:
- 20.04 (Clang 10)
- 20.10 (Clang 10)
- 21.04 (Clang 11)
- 21.10, 22.04, 22.10 (Clang 12)
- 23.04, 23.10 (Clang 15)
- 24.04 (Clang 18)
Sources: builder/init_env.sh:1-106
Build Verification
Version Checks
The build system enforces minimum tool versions through .checkver_* targets that must pass before compilation begins:
.checkver_$(CMD_CLANG): | .check_$(CMD_CLANG)
@if [ ${CLANG_VERSION} -lt 9 ]; then
echo "you MUST use clang 9 or newer"
exit 1
fi
.checkver_$(CMD_GO): | .check_$(CMD_GO)
@if [ ${GO_VERSION_MIN} -lt 24 ]; then
echo "you MUST use golang 1.24 or newer"
exit 1
fiSources: functions.mk:13-35
Build Testing
The CI system runs comprehensive tests:
# Unit tests with race detector
go test -v -race ./... -coverprofile=coverage.out
# E2E tests for specific modules
bash ./test/e2e/tls_e2e_test.sh
bash ./test/e2e/gnutls_e2e_test.sh
bash ./test/e2e/gotls_e2e_test.shSources: Makefile:216-244, .github/workflows/go-c-cpp.yml:66-67
Common Build Scenarios
Native Build (Development)
# Full build with CO-RE and non-CO-RE
make clean
make all
# Non-CO-RE only (faster, smaller binary)
make clean
make nocoreCross-Compilation
# Build for arm64 on x86_64
make clean
CROSS_ARCH=arm64 make all
# Build for amd64 on arm64
make clean
CROSS_ARCH=amd64 make allAndroid Build
# Android arm64
make clean
ANDROID=1 CROSS_ARCH=arm64 make nocore
# Android amd64
make clean
ANDROID=1 CROSS_ARCH=amd64 make nocoreDebug Build
# Enable debug symbols and verbose output
make clean
DEBUG=1 make allRelease Build
# Create versioned release archives
make clean
SNAPSHOT_VERSION=v1.0.0 make -f builder/Makefile.release release
# Publish to GitHub
SNAPSHOT_VERSION=v1.0.0 make -f builder/Makefile.release publishSources: Makefile:1-245, builder/Makefile.release:1-151