bpftime Attach System
The attach system in bpftime provides a modular framework for attaching eBPF programs to various event sources in userspace. It supports multiple attachment types including function probes (uprobes), syscall tracing, and specialized attachments like CUDA kernel instrumentation.
Architecture Overview
Core Design Principles
- Modular Design: Each attach type is implemented as a separate module inheriting from
base_attach_impl
- Unified Interface: All attach implementations follow the same interface pattern for consistency
- Extensibility: New attach types can be easily added by implementing the base interface
- Cross-Process Support: Attach points can be shared across processes using shared memory
Component Structure
attach/
├── base_attach_impl/ # Base interface and common functionality
├── frida_uprobe_attach_impl/ # Function instrumentation using Frida
├── syscall_trace_attach_impl/ # System call tracing
├── nv_attach_impl/ # CUDA/GPU kernel instrumentation
├── simple_attach_impl/ # Simplified attach wrapper
└── text_segment_transformer/ # Binary rewriting for syscall interception
Base Interface
The base_attach_impl
class defines the core interface that all attach implementations must follow:
class base_attach_impl {
public:
// Detach an attachment by ID
virtual int detach_by_id(int id) = 0;
// Create attachment with eBPF callback
virtual int create_attach_with_ebpf_callback(
ebpf_run_callback &&cb,
const attach_private_data &private_data,
int attach_type) = 0;
// Register custom helper functions
virtual void register_custom_helpers(
ebpf_helper_register_callback register_callback);
// Call attach-specific functionality
virtual void *call_attach_specific_function(
const std::string &name, void *data);
};
Key Types
ebpf_run_callback
: Function that executes eBPF program with prepared contextattach_private_data
: Base class for attach-specific configuration dataoverride_return_set_callback
: Thread-local callback for modifying return values
Attach Implementations
1. Frida Uprobe Attach (frida_uprobe_attach_impl
)
Provides dynamic function instrumentation using Frida-gum framework.
Features: - Function entry probes (uprobe) - Function return probes (uretprobe) - Override probes (modify function behavior) - Function replacement (ureplace)
Attach Types:
- ATTACH_UPROBE
(6): Execute at function entry
- ATTACH_URETPROBE
(7): Execute at function return
- ATTACH_UPROBE_OVERRIDE
(1008): Override function behavior
- ATTACH_UREPLACE
(1009): Replace function entirely
Key Components:
- Register state capture via pt_regs
- Multiple callback types for different probe scenarios
- Thread-local CPU context management
2. Syscall Trace Attach (syscall_trace_attach_impl
)
Intercepts and traces system calls without kernel support.
Features: - Trace syscall entry and exit - Per-syscall or global callbacks - Minimal performance overhead
Key Components:
- trace_event_raw_sys_enter/exit
: eBPF-compatible event structures
- Syscall dispatcher with up to 512 syscall slots
- Integration with text segment transformer for interception
Workflow:
1. Text segment transformer hooks syscall instructions
2. Hooks redirect to dispatch_syscall
3. Dispatcher calls registered eBPF programs
4. Original syscall executed if not overridden
3. CUDA Attach (nv_attach_impl
)
Instruments CUDA kernels and GPU operations (Linux only).
Features: - Memory operation capture - Kernel function probes/retprobes - PTX code transformation - eBPF-to-PTX compilation
Attach Types:
- ATTACH_CUDA_PROBE
(8): CUDA kernel entry
- ATTACH_CUDA_RETPROBE
(9): CUDA kernel exit
Implementation: 1. Intercepts CUDA runtime APIs 2. Extracts and modifies PTX code 3. Injects eBPF programs as PTX 4. Recompiles and loads modified kernels
4. Simple Attach (simple_attach_impl
)
Wrapper providing simplified attach interface for custom event sources.
Features: - Single callback model - String-based configuration - Easy integration for new event types
Use Case: Ideal for prototyping new attach types or simple event sources that don't require complex context preparation.
Private Data System
Each attach type defines its own attach_private_data
subclass:
struct attach_private_data {
virtual int initialize_from_string(const std::string_view &sv);
virtual std::string to_string() const;
};
Examples:
- frida_attach_private_data
: Function address, offset, module info
- syscall_trace_private_data
: Syscall number, entry/exit flag
- nv_attach_private_data
: Kernel name, attach type
Helper Functions
Attach implementations can register custom helper functions:
// Global helpers available to all attach types
bpftime_set_retval() // Set function return value
bpftime_override_return() // Override with specific value
// Attach-specific helpers registered via register_custom_helpers()
Thread Safety
- Each attach implementation manages its own synchronization
- Thread-local storage for per-thread state (e.g., override callbacks)
- Shared memory operations are atomic where necessary
Adding New Attach Types
- Create new directory under
attach/
- Inherit from
base_attach_impl
- Implement required virtual methods
- Define custom
attach_private_data
if needed - Add to
CMakeLists.txt
- Register attach type IDs in runtime
Platform Support
- Linux: All attach types supported
- macOS: Limited to Frida-based attachments
- Windows: Not currently supported
Special requirements: - CUDA attach requires NVIDIA GPU and CUDA toolkit - Syscall trace requires text segment modification permissions - Some features may require root/elevated privileges
Performance Considerations
- Frida Uprobe: ~10-100ns overhead per probe
- Syscall Trace: Minimal overhead for untraced syscalls
- CUDA Attach: Overhead varies with kernel complexity
- Simple Attach: Depends on callback implementation
Integration with Runtime
The attach system integrates with the bpftime runtime through: - Handler manager for object lifecycle - Shared memory for cross-process access - VM interface for eBPF program execution - Helper function registration system