Skip to content

Commit 5cb9b33

Browse files
committed
Refactor tpbase to 'libc', and add DTV disasm
The DTV variables read from extracted __tls_get_addr can be used to read TLS variables. Functionality should be otherwise unchanged.
1 parent 5443dd2 commit 5cb9b33

File tree

19 files changed

+933
-406
lines changed

19 files changed

+933
-406
lines changed

interpreter/instancestubs.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ package interpreter // import "go.opentelemetry.io/ebpf-profiler/interpreter"
55

66
import (
77
"go.opentelemetry.io/ebpf-profiler/host"
8+
"go.opentelemetry.io/ebpf-profiler/libc"
89
"go.opentelemetry.io/ebpf-profiler/libpf"
910
"go.opentelemetry.io/ebpf-profiler/metrics"
1011
"go.opentelemetry.io/ebpf-profiler/process"
1112
"go.opentelemetry.io/ebpf-profiler/reporter"
12-
"go.opentelemetry.io/ebpf-profiler/tpbase"
1313
)
1414

1515
// InstanceStubs provides empty implementations of Instance hooks that are
@@ -22,7 +22,7 @@ func (is *InstanceStubs) SynchronizeMappings(EbpfHandler, reporter.ExecutableRep
2222
return nil
2323
}
2424

25-
func (is *InstanceStubs) UpdateTSDInfo(EbpfHandler, libpf.PID, tpbase.TSDInfo) error {
25+
func (is *InstanceStubs) UpdateLibcInfo(EbpfHandler, libpf.PID, libc.LibcInfo) error {
2626
return nil
2727
}
2828

interpreter/multi.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import (
88

99
"go.opentelemetry.io/ebpf-profiler/host"
1010
"go.opentelemetry.io/ebpf-profiler/internal/log"
11+
"go.opentelemetry.io/ebpf-profiler/libc"
1112
"go.opentelemetry.io/ebpf-profiler/libpf"
1213
"go.opentelemetry.io/ebpf-profiler/metrics"
1314
"go.opentelemetry.io/ebpf-profiler/process"
1415
"go.opentelemetry.io/ebpf-profiler/remotememory"
1516
"go.opentelemetry.io/ebpf-profiler/reporter"
16-
"go.opentelemetry.io/ebpf-profiler/tpbase"
1717
)
1818

1919
// MultiData implements the Data interface for multiple interpreters.
@@ -104,11 +104,11 @@ func (m *MultiInstance) SynchronizeMappings(ebpf EbpfHandler,
104104
return errors.Join(errs...)
105105
}
106106

107-
// UpdateTSDInfo updates TSD info for all interpreter instances.
108-
func (m *MultiInstance) UpdateTSDInfo(ebpf EbpfHandler, pid libpf.PID, info tpbase.TSDInfo) error {
107+
// UpdateLibcInfo updates libc info for all interpreter instances.
108+
func (m *MultiInstance) UpdateLibcInfo(ebpf EbpfHandler, pid libpf.PID, info libc.LibcInfo) error {
109109
var errs []error
110110
for _, instance := range m.instances {
111-
if err := instance.UpdateTSDInfo(ebpf, pid, info); err != nil {
111+
if err := instance.UpdateLibcInfo(ebpf, pid, info); err != nil {
112112
errs = append(errs, err)
113113
}
114114
}

interpreter/perl/instance.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ import (
1515

1616
"go.opentelemetry.io/ebpf-profiler/host"
1717
"go.opentelemetry.io/ebpf-profiler/interpreter"
18+
"go.opentelemetry.io/ebpf-profiler/libc"
1819
"go.opentelemetry.io/ebpf-profiler/libpf"
1920
"go.opentelemetry.io/ebpf-profiler/libpf/pfunsafe"
2021
"go.opentelemetry.io/ebpf-profiler/metrics"
2122
npsr "go.opentelemetry.io/ebpf-profiler/nopanicslicereader"
2223
"go.opentelemetry.io/ebpf-profiler/remotememory"
2324
"go.opentelemetry.io/ebpf-profiler/successfailurecounter"
2425
"go.opentelemetry.io/ebpf-profiler/support"
25-
"go.opentelemetry.io/ebpf-profiler/tpbase"
2626
"go.opentelemetry.io/ebpf-profiler/util"
2727
)
2828

@@ -73,9 +73,8 @@ func hashCOPKey(k copKey) uint32 {
7373
return uint32(h ^ xxh3.HashString128(k.funcName.String()).Lo)
7474
}
7575

76-
func (i *perlInstance) UpdateTSDInfo(ebpf interpreter.EbpfHandler, pid libpf.PID,
77-
tsdInfo tpbase.TSDInfo,
78-
) error {
76+
func (i *perlInstance) UpdateLibcInfo(ebpf interpreter.EbpfHandler, pid libpf.PID,
77+
libcInfo libc.LibcInfo) error {
7978
d := i.d
8079
stateInTSD := uint8(0)
8180
if d.stateInTSD {
@@ -88,9 +87,9 @@ func (i *perlInstance) UpdateTSDInfo(ebpf interpreter.EbpfHandler, pid libpf.PID
8887
StateInTSD: stateInTSD,
8988

9089
TsdInfo: support.TSDInfo{
91-
Offset: tsdInfo.Offset,
92-
Multiplier: tsdInfo.Multiplier,
93-
Indirect: tsdInfo.Indirect,
90+
Offset: libcInfo.TSDInfo.Offset,
91+
Multiplier: libcInfo.TSDInfo.Multiplier,
92+
Indirect: libcInfo.TSDInfo.Indirect,
9493
},
9594

9695
Interpreter_curcop: uint16(vms.interpreter.curcop),

interpreter/python/python.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ import (
2626

2727
"go.opentelemetry.io/ebpf-profiler/host"
2828
"go.opentelemetry.io/ebpf-profiler/interpreter"
29+
"go.opentelemetry.io/ebpf-profiler/libc"
2930
"go.opentelemetry.io/ebpf-profiler/libpf"
3031
"go.opentelemetry.io/ebpf-profiler/libpf/pfelf"
3132
"go.opentelemetry.io/ebpf-profiler/metrics"
3233
npsr "go.opentelemetry.io/ebpf-profiler/nopanicslicereader"
3334
"go.opentelemetry.io/ebpf-profiler/remotememory"
3435
"go.opentelemetry.io/ebpf-profiler/successfailurecounter"
3536
"go.opentelemetry.io/ebpf-profiler/support"
36-
"go.opentelemetry.io/ebpf-profiler/tpbase"
3737
"go.opentelemetry.io/ebpf-profiler/util"
3838
)
3939

@@ -371,19 +371,18 @@ func (p *pythonInstance) GetAndResetMetrics() ([]metrics.Metric, error) {
371371
}, nil
372372
}
373373

374-
func (p *pythonInstance) UpdateTSDInfo(ebpf interpreter.EbpfHandler, pid libpf.PID,
375-
tsdInfo tpbase.TSDInfo,
376-
) error {
374+
func (p *pythonInstance) UpdateLibcInfo(ebpf interpreter.EbpfHandler, pid libpf.PID,
375+
libcInfo libc.LibcInfo) error {
377376
d := p.d
378377
vm := &d.vmStructs
379378
cdata := support.PyProcInfo{
380379
AutoTLSKeyAddr: uint64(d.autoTLSKey) + uint64(p.bias),
381380
Version: d.version,
382381

383382
TsdInfo: support.TSDInfo{
384-
Offset: tsdInfo.Offset,
385-
Multiplier: tsdInfo.Multiplier,
386-
Indirect: tsdInfo.Indirect,
383+
Offset: libcInfo.TSDInfo.Offset,
384+
Multiplier: libcInfo.TSDInfo.Multiplier,
385+
Indirect: libcInfo.TSDInfo.Indirect,
387386
},
388387

389388
PyThreadState_frame: uint8(vm.PyThreadState.Frame),

interpreter/types.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ import (
88
"unsafe"
99

1010
"go.opentelemetry.io/ebpf-profiler/host"
11+
"go.opentelemetry.io/ebpf-profiler/libc"
1112
"go.opentelemetry.io/ebpf-profiler/libpf"
1213
"go.opentelemetry.io/ebpf-profiler/lpm"
1314
"go.opentelemetry.io/ebpf-profiler/metrics"
1415
"go.opentelemetry.io/ebpf-profiler/process"
1516
"go.opentelemetry.io/ebpf-profiler/remotememory"
1617
"go.opentelemetry.io/ebpf-profiler/reporter"
17-
"go.opentelemetry.io/ebpf-profiler/tpbase"
1818
"go.opentelemetry.io/ebpf-profiler/util"
1919
)
2020

@@ -143,9 +143,9 @@ type Instance interface {
143143
SynchronizeMappings(ebpf EbpfHandler, exeReporter reporter.ExecutableReporter,
144144
pr process.Process, mappings []process.Mapping) error
145145

146-
// UpdateTSDInfo is called when the process C-library Thread Specific Data related
146+
// UpdateLibcInfo is called when the process C-library related
147147
// introspection data has been updated.
148-
UpdateTSDInfo(ebpf EbpfHandler, pid libpf.PID, info tpbase.TSDInfo) error
148+
UpdateLibcInfo(ebpf EbpfHandler, pid libpf.PID, info libc.LibcInfo) error
149149

150150
// Symbolize converts one ebpf frame to one or more (if inlining was expanded) libpf.Frame.
151151
// The resulting libpf.Frame values are appended to frames.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4-
package tpbase // import "go.opentelemetry.io/ebpf-profiler/tpbase"
4+
package libc // import "go.opentelemetry.io/ebpf-profiler/libc"
55

66
import (
77
"errors"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4-
package tpbase
4+
package libc
55

66
import (
77
"debug/elf"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4-
package tpbase // import "go.opentelemetry.io/ebpf-profiler/tpbase"
4+
package libc // import "go.opentelemetry.io/ebpf-profiler/libc"
55

66
import (
77
"bytes"

tpbase/libc.go renamed to libc/libc.go

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,29 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4-
package tpbase // import "go.opentelemetry.io/ebpf-profiler/tpbase"
4+
package libc // import "go.opentelemetry.io/ebpf-profiler/libc"
55

66
import (
77
"debug/elf"
88
"fmt"
99
"regexp"
1010

11+
"go.opentelemetry.io/ebpf-profiler/internal/log"
12+
1113
"go.opentelemetry.io/ebpf-profiler/libpf/pfelf"
1214
)
1315

16+
type LibcInfo struct {
17+
DTVInfo *DTVInfo
18+
TSDInfo *TSDInfo
19+
}
20+
21+
type DTVInfo struct {
22+
Offset int64 // Offset of DTV from FS base (or from thread pointer)
23+
EntryWidth uint32 // Width of each DTV entry in bytes
24+
Indirect uint8 // 0 if DTV is at FS+offset, 1 if at [FS+0]+offset
25+
}
26+
1427
// TSDInfo contains information to access C-library's Thread Specific Data from eBPF
1528
type TSDInfo struct {
1629
// Offset is the pointer difference from "tpbase" pointer to the C-library
@@ -84,8 +97,25 @@ func IsPotentialTSDDSO(filename string) bool {
8497
return libcRegex.MatchString(filename)
8598
}
8699

100+
func ExtractLibcInfo(ef *pfelf.File) (*LibcInfo, error) {
101+
tsdinfo, err := extractTSDInfo(ef)
102+
if err != nil {
103+
return nil, err
104+
}
105+
106+
dtvinfo, err := extractDTVInfo(ef)
107+
if err != nil {
108+
return nil, err
109+
}
110+
111+
return &LibcInfo{
112+
TSDInfo: tsdinfo,
113+
DTVInfo: dtvinfo,
114+
}, nil
115+
}
116+
87117
// ExtractTSDInfo extracts the introspection data for pthread thread specific data.
88-
func ExtractTSDInfo(ef *pfelf.File) (*TSDInfo, error) {
118+
func extractTSDInfo(ef *pfelf.File) (*TSDInfo, error) {
89119
_, code, err := ef.SymbolData("__pthread_getspecific", 2048)
90120
if err != nil {
91121
_, code, err = ef.SymbolData("pthread_getspecific", 2048)
@@ -111,3 +141,33 @@ func ExtractTSDInfo(ef *pfelf.File) (*TSDInfo, error) {
111141
}
112142
return &info, nil
113143
}
144+
145+
// extractDTVInfo extracts the introspection data for the DTV to access TLS vars
146+
func extractDTVInfo(ef *pfelf.File) (*DTVInfo, error) {
147+
var info DTVInfo
148+
_, code, err := ef.SymbolData("__tls_get_addr", 2048)
149+
if err != nil {
150+
// Only error out reading DTV if we have the symbol, but fail to parse it
151+
// if the symbol is not exported, failing to read it is not a critical error
152+
// and empty DTV introspection data is returned
153+
log.Warnf("unable to read '__tls_get_addr': %s, libc DTV introspection data is unavailable", err)
154+
return &info, nil
155+
}
156+
157+
if len(code) < 8 {
158+
return nil, fmt.Errorf("__tls_get_addr function size is %d", len(code))
159+
}
160+
161+
switch ef.Machine {
162+
case elf.EM_AARCH64:
163+
info, err = extractDTVInfoARM(code)
164+
case elf.EM_X86_64:
165+
info, err = extractDTVInfoX86(code)
166+
default:
167+
return &info, fmt.Errorf("unsupported arch %s", ef.Machine.String())
168+
}
169+
if err != nil {
170+
return &info, fmt.Errorf("failed to extract DTV data: %s", err)
171+
}
172+
return &info, nil
173+
}
Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4-
package tpbase // import "go.opentelemetry.io/ebpf-profiler/tpbase"
4+
package libc // import "go.opentelemetry.io/ebpf-profiler/libc"
55

66
import (
77
"errors"
@@ -268,3 +268,72 @@ func extractTSDInfoARM(code []byte) (TSDInfo, error) {
268268
Indirect: indirect,
269269
}, nil
270270
}
271+
272+
func extractDTVInfoARM(code []byte) (DTVInfo, error) {
273+
dtvOffset := int64(0)
274+
entryWidth := uint32(0)
275+
foundThreadPtr := false
276+
foundDTVLoad := false
277+
278+
// Scan entire function
279+
for offs := 0; offs < len(code); offs += 4 {
280+
if offs+4 > len(code) {
281+
break
282+
}
283+
284+
inst, err := aa.Decode(code[offs:])
285+
if err != nil {
286+
return DTVInfo{}, err
287+
}
288+
289+
switch inst.Op {
290+
291+
case aa.MRS:
292+
foundThreadPtr = true
293+
294+
case aa.LDUR:
295+
if len(inst.Args) >= 2 {
296+
if m, ok := inst.Args[1].(aa.MemImmediate); ok {
297+
imm, ok := ah.DecodeImmediate(m)
298+
if ok {
299+
dtvOffset = imm
300+
foundDTVLoad = true
301+
}
302+
}
303+
}
304+
305+
case aa.LDR:
306+
if len(inst.Args) >= 2 {
307+
// Check what type of LDR this is
308+
switch m := inst.Args[1].(type) {
309+
case aa.MemImmediate:
310+
if foundThreadPtr && !foundDTVLoad {
311+
imm, ok := ah.DecodeImmediate(m)
312+
if ok {
313+
dtvOffset = imm
314+
foundDTVLoad = true
315+
}
316+
}
317+
318+
case aa.MemExtend:
319+
if m.Amount > 0 {
320+
entryWidth = uint32(1 << m.Amount)
321+
}
322+
}
323+
}
324+
325+
case aa.LSL:
326+
if len(inst.Args) >= 3 {
327+
if imm, ok := inst.Args[2].(aa.Imm); ok {
328+
entryWidth = uint32(1 << imm.Imm)
329+
}
330+
}
331+
}
332+
}
333+
334+
return DTVInfo{
335+
Offset: dtvOffset,
336+
EntryWidth: entryWidth,
337+
Indirect: 1,
338+
}, nil
339+
}

0 commit comments

Comments
 (0)