Skip to content

Commit 5f0a645

Browse files
committed
feat: add reverse lookup for uprobe symbols
Signed-off-by: Max Altgelt <max.altgelt@nextron-systems.com>
1 parent b2926b6 commit 5f0a645

File tree

4 files changed

+92
-14
lines changed

4 files changed

+92
-14
lines changed

link/link_other.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,26 @@ func (umi *UprobeMultiInfo) Offsets() ([]UprobeMultiOffset, bool) {
272272
return adresses, true
273273
}
274274

275+
// Symbols returns the symbols that the uprobe was attached to.
276+
func (umi *UprobeMultiInfo) Symbols() ([]UprobeSymbol, error) {
277+
if umi.offsets == nil {
278+
return nil, fmt.Errorf("no offsets available")
279+
}
280+
ex, err := OpenExecutable(umi.path)
281+
if err != nil {
282+
return nil, err
283+
}
284+
var symbols []UprobeSymbol
285+
for i := range umi.offsets {
286+
symbol, err := ex.Symbol(umi.offsets[i])
287+
if err != nil {
288+
return nil, err
289+
}
290+
symbols = append(symbols, symbol)
291+
}
292+
return symbols, nil
293+
}
294+
275295
func (umi *UprobeMultiInfo) Path() string {
276296
return umi.path
277297
}
@@ -340,6 +360,19 @@ type UprobeInfo struct {
340360
RefCtrOffset uint64
341361
}
342362

363+
type UprobeSymbol struct {
364+
Symbol string
365+
Offset uint64
366+
}
367+
368+
func (u *UprobeInfo) Symbol() (UprobeSymbol, error) {
369+
ex, err := OpenExecutable(u.File)
370+
if err != nil {
371+
return UprobeSymbol{}, err
372+
}
373+
return ex.Symbol(uint64(u.Offset))
374+
}
375+
343376
type TracepointInfo struct {
344377
Tracepoint string
345378
Cookie uint64

link/uprobe.go

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,16 @@ type Executable struct {
3939
// Path of the executable on the filesystem.
4040
path string
4141
// Parsed ELF and dynamic symbols' cachedAddresses.
42-
cachedAddresses map[string]uint64
42+
cachedAddresses map[string]symbolAddress
4343
// Keep track of symbol table lazy load.
4444
cacheAddressesOnce sync.Once
4545
}
4646

47+
type symbolAddress struct {
48+
addr uint64
49+
size uint64
50+
}
51+
4752
// UprobeOptions defines additional parameters that will be used
4853
// when loading Uprobes.
4954
type UprobeOptions struct {
@@ -111,7 +116,7 @@ func OpenExecutable(path string) (*Executable, error) {
111116

112117
return &Executable{
113118
path: path,
114-
cachedAddresses: make(map[string]uint64),
119+
cachedAddresses: make(map[string]symbolAddress),
115120
}, nil
116121
}
117122

@@ -155,20 +160,16 @@ func (ex *Executable) load(f *internal.SafeELFFile) error {
155160
}
156161
}
157162

158-
ex.cachedAddresses[s.Name] = address
163+
ex.cachedAddresses[s.Name] = symbolAddress{
164+
addr: address,
165+
size: s.Size,
166+
}
159167
}
160168

161169
return nil
162170
}
163171

164-
// address calculates the address of a symbol in the executable.
165-
//
166-
// opts must not be nil.
167-
func (ex *Executable) address(symbol string, address, offset uint64) (uint64, error) {
168-
if address > 0 {
169-
return address + offset, nil
170-
}
171-
172+
func (ex *Executable) lazyLoadSymbols() error {
172173
var err error
173174
ex.cacheAddressesOnce.Do(func() {
174175
var f *internal.SafeELFFile
@@ -181,11 +182,23 @@ func (ex *Executable) address(symbol string, address, offset uint64) (uint64, er
181182

182183
err = ex.load(f)
183184
})
185+
return err
186+
}
187+
188+
// address calculates the address of a symbol in the executable.
189+
//
190+
// opts must not be nil.
191+
func (ex *Executable) address(symbol string, address, offset uint64) (uint64, error) {
192+
if address > 0 {
193+
return address + offset, nil
194+
}
195+
196+
err := ex.lazyLoadSymbols()
184197
if err != nil {
185198
return 0, fmt.Errorf("lazy load symbols: %w", err)
186199
}
187200

188-
address, ok := ex.cachedAddresses[symbol]
201+
symaddress, ok := ex.cachedAddresses[symbol]
189202
if !ok {
190203
return 0, fmt.Errorf("symbol %s: %w", symbol, ErrNoSymbol)
191204
}
@@ -196,12 +209,30 @@ func (ex *Executable) address(symbol string, address, offset uint64) (uint64, er
196209
//
197210
// Since only offset values are stored and not elf.Symbol, if the value is 0,
198211
// assume it's an external symbol.
199-
if address == 0 {
212+
if symaddress.addr == 0 {
200213
return 0, fmt.Errorf("cannot resolve %s library call '%s': %w "+
201214
"(consider providing UprobeOptions.Address)", ex.path, symbol, ErrNotSupported)
202215
}
203216

204-
return address + offset, nil
217+
if offset >= symaddress.size {
218+
return 0, fmt.Errorf("offset %d is out of range of symbol %s", offset, symbol)
219+
}
220+
221+
return symaddress.addr + offset, nil
222+
}
223+
224+
// Symbol calculates the symbol and offset for an address in the executable.
225+
func (ex *Executable) Symbol(address uint64) (UprobeSymbol, error) {
226+
if err := ex.lazyLoadSymbols(); err != nil {
227+
return UprobeSymbol{}, fmt.Errorf("lazy load symbols: %w", err)
228+
}
229+
230+
for name, symaddress := range ex.cachedAddresses {
231+
if symaddress.addr <= address && symaddress.addr+symaddress.size > address {
232+
return UprobeSymbol{name, address - symaddress.addr}, nil
233+
}
234+
}
235+
return UprobeSymbol{}, ErrNoSymbol
205236
}
206237

207238
// Uprobe attaches the given eBPF program to a perf event that fires when the

link/uprobe_multi_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ func TestUprobeMultiInfo(t *testing.T) {
6161
uprobeOffsets, ok := uprobeDetails.Offsets()
6262
qt.Assert(t, qt.IsTrue(ok))
6363
qt.Assert(t, qt.HasLen(uprobeOffsets, len(bashSyms)))
64+
syms, err := uprobeDetails.Symbols()
65+
if err != nil {
66+
t.Fatal(err)
67+
}
68+
var symnames = make([]string, len(syms))
69+
for i, sym := range syms {
70+
qt.Assert(t, qt.Equals(sym.Offset, 0))
71+
symnames[i] = sym.Symbol
72+
}
73+
qt.Assert(t, qt.ContentEquals(symnames, bashSyms))
6474
}
6575

6676
func TestUprobeMultiInput(t *testing.T) {

link/uprobe_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ func TestUprobeInfo(t *testing.T) {
124124
qt.Assert(t, qt.Equals(eventInfo.Type, PerfEventUprobe))
125125
uprobeInfo := eventInfo.Uprobe()
126126
qt.Assert(t, qt.StringContains(uprobeInfo.File, bashEx.path))
127+
sym, err := uprobeInfo.Symbol()
128+
qt.Assert(t, qt.IsNil(err))
129+
qt.Assert(t, qt.Equals(sym.Symbol, bashSym))
130+
qt.Assert(t, qt.Equals(sym.Offset, 0))
127131
}
128132

129133
func TestUprobeExtNotFound(t *testing.T) {

0 commit comments

Comments
 (0)