BPF ABI Guide
The C structures on the BPF side are the source of truth for the perf event ABI. Go types are generated automatically from BTF in the BPF objects. Do not maintain equivalent structures manually.
Conventions
Each ABI domain maps to one C header and one generated Go file:
| Item | Convention |
|---|---|
| Domain name | <domain> |
| C header | bpf/include/abi/<domain>_types.h |
| C structure prefix | <domain>_ |
| Generated Go file | internal/bpf/abi/<domain>_types_generated.go |
<domain> must start with a lowercase letter and contain only lowercase
letters, digits, and underscores. Avoid domains with overlapping prefixes,
such as net and net_rx.
Implementation
The following example creates the sample domain.
1. Define the ABI Header
Create bpf/include/abi/sample_types.h:
// Copyright 2026 The HuaTuo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __BPF_ABI_SAMPLE_H__
#define __BPF_ABI_SAMPLE_H__
#include "bpf_abi.h"
#define SAMPLE_DATA_LEN 4
struct sample_detail {
u32 code;
u8 data[SAMPLE_DATA_LEN];
};
struct sample_event {
u64 timestamp;
struct sample_detail detail;
u32 pid;
u8 kind;
u8 pad[3];
};
BPF_ABI_EXPORT(sample_detail);
BPF_ABI_EXPORT(sample_event);
#endif /* __BPF_ABI_SAMPLE_H__ */
Requirements:
- Structure names must use the
<domain>_prefix. - Every structure that requires a generated Go type must call
BPF_ABI_EXPORT(<type>), including nested structures. - The header must contain only ABI types and related constants. Do not add BPF programs, maps, or business logic.
- Array lengths must be compile-time constants and remain consistent across all BPF compilation units.
2. Use the Header in a BPF Program
The ABI header depends on base types such as u8 and u16. Include it after
vmlinux.h and the project base headers:
#include "vmlinux.h"
#include "bpf_common.h"
#include "abi/sample_types.h"
Use the ABI structure directly when emitting a perf event:
struct sample_event event = {};
event.timestamp = bpf_ktime_get_ns();
event.pid = bpf_get_current_pid_tgid() >> 32;
event.kind = kind;
bpf_perf_event_output(ctx, &events, COMPAT_BPF_F_CURRENT_CPU,
&event, sizeof(event));
The event must be zero-initialized to prevent uninitialized padding from being
written to the perf buffer. Any structure passed to bpf_perf_event_output
must be defined in an ABI header.
3. Generate and Use the Go Type
Run the following command from the repository root:
make gen-build
The generated file is located at:
internal/bpf/abi/sample_types_generated.go
Reference the generated type directly from Go code:
import "huatuo-bamai/internal/bpf/abi"
var event abi.SampleEvent
if err := reader.ReadInto(&event); err != nil {
return fmt.Errorf("read sample event: %w", err)
}
The generated file contains the structure, the SampleEventSize size
constant, and layout assertions based on unsafe.Sizeof and
unsafe.Offsetof. Generated files are read-only. Do not edit them manually.
Types and Layout
| Type | Support |
|---|---|
| Fixed-width integers of 1, 2, 4, or 8 bytes | Supported |
| Fixed-length arrays with a nonzero length | Supported |
| Nested structures that meet the same constraints | Supported |
typedef with a supported target type |
Supported |
Pointers, union, enum, and floating-point types |
Unsupported |
Bit fields and _Bool |
Unsupported |
| Zero-length and flexible arrays | Unsupported |
| Recursive structures, overlapping fields, or non-byte-aligned structures | Unsupported |
Layout requirements:
- Use fixed-width integers such as
u8,s16,u32, ands64. - Order fields according to their alignment requirements. Use
u8 pad[N]to make padding explicit when necessary. - Do not use platform-dependent types such as
longorunsigned long. - Do not use
__attribute__((packed))to bypass layout validation. - If a structure with the same name appears in multiple BPF objects, its fields, offsets, and size must match exactly.
C names are converted to exported Go names by splitting on underscores. For
example, sample_event becomes SampleEvent, and pid_tgid becomes
PIDTGID. Avoid C names that map to the same Go name, such as sample_id and
sample_i_d.
Verification
Run the following commands after adding or modifying an ABI:
make gen-build
go test ./build/bpfabi-tool
make check
Add a decoding test for each new perf event. At a minimum, cover:
- Integer boundary values and native byte order.
- Nested structures and the first and last array elements.
- Fields before and after padding.
- The sample size and the generated
<GoType>Sizeconstant.
Common Errors
| Error | Check |
|---|---|
has no "<domain>_" btf anchors |
Confirm that a BPF source includes the header and that the type calls BPF_ABI_EXPORT |
without matching abi header |
Confirm that the header name matches the structure prefix and that domain prefixes do not overlap |
differs between objects |
Check whether conditional compilation, array length macros, or included headers change the layout |
go offset is ... btf offset is ... |
Reorder fields or add explicit padding; do not use packed |
go type name ... collides |
Rename C types or fields that map to the same Go name |