Skip to content

Commit

Permalink
feature: add AttachKprobeOffset method to bpf programs.
Browse files Browse the repository at this point in the history
This method allows the kprobe to be attached not only by symbol name (already
supported) but by the kernel symbol offset as well. The offset should be read
from /proc/kallsyms file before method is invoked.
  • Loading branch information
rafaeldtinoco committed Jan 11, 2024
1 parent c7cd7fd commit a879333
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 20 deletions.
24 changes: 24 additions & 0 deletions libbpfgo.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,30 @@ void cgo_bpf_tc_hook_free(struct bpf_tc_hook *hook)
free(hook);
}

struct bpf_kprobe_opts *cgo_bpf_kprobe_opts_new(__u64 bpf_cookie,
size_t offset,
bool retprobe,
int attach_mode)
{
struct bpf_kprobe_opts *opts;
opts = calloc(1, sizeof(*opts));
if (!opts)
return NULL;

opts->sz = sizeof(*opts);
opts->bpf_cookie = bpf_cookie;
opts->offset = offset;
opts->retprobe = retprobe;
opts->attach_mode = attach_mode;

return opts;
}

void cgo_bpf_kprobe_opts_free(struct bpf_kprobe_opts *opts)
{
free(opts);
}

//
// struct getters
//
Expand Down
6 changes: 6 additions & 0 deletions libbpfgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ void cgo_bpf_tc_opts_free(struct bpf_tc_opts *opts);
struct bpf_tc_hook *cgo_bpf_tc_hook_new();
void cgo_bpf_tc_hook_free(struct bpf_tc_hook *hook);

struct bpf_kprobe_opts *cgo_bpf_kprobe_opts_new(__u64 bpf_cookie,
size_t offset,
bool retprobe,
int attach_mode);
void cgo_bpf_kprobe_opts_free(struct bpf_kprobe_opts *opts);

//
// struct getters
//
Expand Down
100 changes: 80 additions & 20 deletions prog.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,41 +370,101 @@ func (p *BPFProg) AttachPerfEvent(fd int) (*BPFLink, error) {
return bpfLink, nil
}

// this API should be used for kernels > 4.17
func (p *BPFProg) AttachKprobe(kp string) (*BPFLink, error) {
return doAttachKprobe(p, kp, false)
}
//
// Kprobe and Kretprobe
//

// this API should be used for kernels > 4.17
func (p *BPFProg) AttachKretprobe(kp string) (*BPFLink, error) {
return doAttachKprobe(p, kp, true)
type attachTo struct {
symName string
symAddr uint64
isRet bool
}

func doAttachKprobe(prog *BPFProg, kp string, isKretprobe bool) (*BPFLink, error) {
kpC := C.CString(kp)
defer C.free(unsafe.Pointer(kpC))
// attachKprobeCommon is a common function for attaching kprobe and kretprobe.
func (p *BPFProg) attachKprobeCommon(a attachTo) (*BPFLink, error) {
// Create kprobe_opts.
optsC, errno := C.cgo_bpf_kprobe_opts_new(
C.ulonglong(0), // bpf cookie (not used)
C.size_t(a.symAddr), // might be 0 if attaching using symbol name
C.bool(a.isRet), // is kretprobe or kprobe
C.int(0), // attach mode (default)
)
if optsC == nil {
return nil, fmt.Errorf("failed to create kprobe_opts of %v: %v", a, errno)
}
defer C.cgo_bpf_kprobe_opts_free(optsC)

// Create kprobe symbol name.
symNameC := C.CString(a.symName)
defer C.free(unsafe.Pointer(symNameC))

linkC, errno := C.bpf_program__attach_kprobe(prog.prog, C.bool(isKretprobe), kpC)
// Create kprobe link.
var linkC *C.struct_bpf_link
linkC, errno = C.bpf_program__attach_kprobe_opts(p.prog, symNameC, optsC)
if linkC == nil {
return nil, fmt.Errorf("failed to attach %s k(ret)probe to program %s: %w", kp, prog.Name(), errno)
return nil, fmt.Errorf("failed to attach to %v: %v", a, errno)
}

kpType := Kprobe
if isKretprobe {
kpType = Kretprobe
linkType := Kprobe
if a.isRet {
linkType = Kretprobe
}

eventName := a.symName
if eventName == "" {
eventName = fmt.Sprintf("%d", a.symAddr)
}

// Create bpfLink and append it to the module.
bpfLink := &BPFLink{
link: linkC,
prog: prog,
linkType: kpType,
eventName: kp,
link: linkC, // linkC is a pointer to a struct bpf_link
prog: p, // p is a pointer to the related BPFProg
linkType: linkType, // linkType is a BPFLinkType
eventName: eventName, // eventName is a string
}
prog.module.links = append(prog.module.links, bpfLink)
p.module.links = append(p.module.links, bpfLink)

return bpfLink, nil
}

// AttachKprobe attaches the BPFProgram to the given symbol name.
func (p *BPFProg) AttachKprobe(symbol string) (*BPFLink, error) {
a := attachTo{
symName: symbol,
isRet: false,
}
return p.attachKprobeCommon(a)
}

// AttachKretprobe attaches the BPFProgram to the given symbol name (for return).
func (p *BPFProg) AttachKretprobe(symbol string) (*BPFLink, error) {
a := attachTo{
symName: symbol,
isRet: true,
}
return p.attachKprobeCommon(a)
}

// AttachKprobeOnOffset attaches the BPFProgram to the given offset.
func (p *BPFProg) AttachKprobeOffset(offset uint64) (*BPFLink, error) {
a := attachTo{
symAddr: offset,
isRet: false,
}
return p.attachKprobeCommon(a)
}

// AttachKretprobeOnOffset attaches the BPFProgram to the given offset (for return).
func (p *BPFProg) AttachKretprobeOnOffset(offset uint64) (*BPFLink, error) {
a := attachTo{
symAddr: offset,
isRet: true,
}
return p.attachKprobeCommon(a)
}

// End of Kprobe and Kretprobe

func (p *BPFProg) AttachNetns(networkNamespacePath string) (*BPFLink, error) {
fd, err := syscall.Open(networkNamespacePath, syscall.O_RDONLY, 0)
if fd < 0 {
Expand Down

0 comments on commit a879333

Please sign in to comment.