Skip to content

Commit

Permalink
link: unify KprobeOptions.Offset and UprobeOptions.Offset
Browse files Browse the repository at this point in the history
Both options currently have subtly different definitions. Kprobe.Offset is counted
from the start of the symbol, while Uprobe.Offset counts from the start of the
address space.

Rename UprobeOptions.Offset to Address and RelativeOffset to Offset. Existing
users of UprobeOptions.Offset will get an error when they first upgrade the
library, so make the error message even more explicit.
  • Loading branch information
lmb committed Jul 7, 2022
1 parent 977fc22 commit 5713a12
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 47 deletions.
71 changes: 35 additions & 36 deletions link/uprobe.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,22 @@ var (
type Executable struct {
// Path of the executable on the filesystem.
path string
// Parsed ELF symbols and dynamic symbols offsets.
offsets map[string]uint64
// Parsed ELF and dynamic symbols' addresses.
addresses map[string]uint64
}

// UprobeOptions defines additional parameters that will be used
// when loading Uprobes.
type UprobeOptions struct {
// Symbol offset. Must be provided in case of external symbols (shared libs).
// If set, overrides the offset eventually parsed from the executable.
Offset uint64
// Symbol address. Must be provided in case of external symbols (shared libs).
// If set, overrides the address eventually parsed from the executable.
Address uint64
// The offset relative to given symbol. Useful when tracing an arbitrary point
// inside the frame of given symbol and eliminates the need of recalculating
// the absolute offset.
RelativeOffset uint64
// inside the frame of given symbol.
//
// Note: this field changed from being an absolute offset to being relative
// to Address.
Offset uint64
// Only set the uprobe on the given process ID. Useful when tracing
// shared library calls or programs that have many running instances.
PID int
Expand Down Expand Up @@ -104,8 +106,8 @@ func OpenExecutable(path string) (*Executable, error) {
}

ex := Executable{
path: path,
offsets: make(map[string]uint64),
path: path,
addresses: make(map[string]uint64),
}

if err := ex.load(se); err != nil {
Expand Down Expand Up @@ -134,7 +136,7 @@ func (ex *Executable) load(f *internal.SafeELFFile) error {
continue
}

off := s.Value
address := s.Value

// Loop over ELF segments.
for _, prog := range f.Progs {
Expand All @@ -150,45 +152,42 @@ func (ex *Executable) load(f *internal.SafeELFFile) error {
// fn symbol offset = fn symbol VA - .text VA + .text offset
//
// stackoverflow.com/a/40249502
off = s.Value - prog.Vaddr + prog.Off
address = s.Value - prog.Vaddr + prog.Off
break
}
}

ex.offsets[s.Name] = off
ex.addresses[s.Name] = address
}

return nil
}

// offset calculates the address of a symbol in the executable.
// address calculates the address of a symbol in the executable.
//
// opts must not be nil.
func (ex *Executable) offset(symbol string, opts *UprobeOptions) (uint64, error) {
var offset uint64
if opts.Offset > 0 {
offset = opts.Offset
} else if off, ok := ex.offsets[symbol]; ok {
// Symbols with location 0 from section undef are shared library calls and
// are relocated before the binary is executed. Dynamic linking is not
// implemented by the library, so mark this as unsupported for now.
//
// Since only offset values are stored and not elf.Symbol, if the value is 0,
// assume it's an external symbol.
if off == 0 {
return 0, fmt.Errorf("cannot resolve %s library call '%s', "+
"consider providing the offset via options: %w", ex.path, symbol, ErrNotSupported)
}
func (ex *Executable) address(symbol string, opts *UprobeOptions) (uint64, error) {
if opts.Address > 0 {
return opts.Address + opts.Offset, nil
}

offset = off
} else {
address, ok := ex.addresses[symbol]
if !ok {
return 0, fmt.Errorf("symbol %s: %w", symbol, ErrNoSymbol)
}

// Always add the relative offset regardless where we got the absolute
// offset from.
offset += opts.RelativeOffset
return offset, nil
// Symbols with location 0 from section undef are shared library calls and
// are relocated before the binary is executed. Dynamic linking is not
// implemented by the library, so mark this as unsupported for now.
//
// Since only offset values are stored and not elf.Symbol, if the value is 0,
// assume it's an external symbol.
if address == 0 {
return 0, fmt.Errorf("cannot resolve %s library call '%s': %w "+
"(consider providing UprobeOptions.Address)", ex.path, symbol, ErrNotSupported)
}

return address + opts.Offset, nil
}

// Uprobe attaches the given eBPF program to a perf event that fires when the
Expand Down Expand Up @@ -273,7 +272,7 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti
opts = &UprobeOptions{}
}

offset, err := ex.offset(symbol, opts)
offset, err := ex.address(symbol, opts)
if err != nil {
return nil, err
}
Expand Down
22 changes: 11 additions & 11 deletions link/uprobe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ func TestExecutable(t *testing.T) {
t.Fatalf("create executable: unexpected path '%s'", bashEx.path)
}

_, err = bashEx.offset(bashSym, &UprobeOptions{})
_, err = bashEx.address(bashSym, &UprobeOptions{})
if err != nil {
t.Fatalf("find offset: %v", err)
}

_, err = bashEx.offset("bogus", &UprobeOptions{})
_, err = bashEx.address("bogus", &UprobeOptions{})
if err == nil {
t.Fatal("find symbol: expected error")
}
Expand All @@ -45,24 +45,24 @@ func TestExecutable(t *testing.T) {
func TestExecutableOffset(t *testing.T) {
c := qt.New(t)

symbolOffset, err := bashEx.offset(bashSym, &UprobeOptions{})
symbolOffset, err := bashEx.address(bashSym, &UprobeOptions{})
if err != nil {
t.Fatal(err)
}

offset, err := bashEx.offset(bashSym, &UprobeOptions{Offset: 0x1})
offset, err := bashEx.address(bashSym, &UprobeOptions{Address: 0x1})
if err != nil {
t.Fatal(err)
}
c.Assert(offset, qt.Equals, uint64(0x1))

offset, err = bashEx.offset(bashSym, &UprobeOptions{RelativeOffset: 0x2})
offset, err = bashEx.address(bashSym, &UprobeOptions{Offset: 0x2})
if err != nil {
t.Fatal(err)
}
c.Assert(offset, qt.Equals, symbolOffset+0x2)

offset, err = bashEx.offset(bashSym, &UprobeOptions{Offset: 0x1, RelativeOffset: 0x2})
offset, err = bashEx.address(bashSym, &UprobeOptions{Address: 0x1, Offset: 0x2})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -96,7 +96,7 @@ func TestUprobeExtWithOpts(t *testing.T) {

// This Uprobe is broken and will not work because the offset is not
// correct. This is expected since the offset is provided by the user.
up, err := bashEx.Uprobe("open", prog, &UprobeOptions{Offset: 0x1})
up, err := bashEx.Uprobe("open", prog, &UprobeOptions{Address: 0x1})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -143,7 +143,7 @@ func TestUprobeCreatePMU(t *testing.T) {
c := qt.New(t)

// Fetch the offset from the /bin/bash Executable already defined.
off, err := bashEx.offset(bashSym, &UprobeOptions{})
off, err := bashEx.address(bashSym, &UprobeOptions{})
c.Assert(err, qt.IsNil)

// Prepare probe args.
Expand Down Expand Up @@ -175,7 +175,7 @@ func TestUprobePMUUnavailable(t *testing.T) {
c := qt.New(t)

// Fetch the offset from the /bin/bash Executable already defined.
off, err := bashEx.offset(bashSym, &UprobeOptions{})
off, err := bashEx.address(bashSym, &UprobeOptions{})
c.Assert(err, qt.IsNil)

// Prepare probe args.
Expand All @@ -201,7 +201,7 @@ func TestUprobeTraceFS(t *testing.T) {
c := qt.New(t)

// Fetch the offset from the /bin/bash Executable already defined.
off, err := bashEx.offset(bashSym, &UprobeOptions{})
off, err := bashEx.address(bashSym, &UprobeOptions{})
c.Assert(err, qt.IsNil)

// Prepare probe args.
Expand Down Expand Up @@ -250,7 +250,7 @@ func TestUprobeCreateTraceFS(t *testing.T) {
c := qt.New(t)

// Fetch the offset from the /bin/bash Executable already defined.
off, err := bashEx.offset(bashSym, &UprobeOptions{})
off, err := bashEx.address(bashSym, &UprobeOptions{})
c.Assert(err, qt.IsNil)

// Sanitize the symbol in order to be used in tracefs API.
Expand Down

0 comments on commit 5713a12

Please sign in to comment.