Skip to content

Commit f2bede8

Browse files
committed
Add DTV extraction support to libc package
The DTV variables read from extracted __tls_get_addr can be used to read TLS variables. If this symbol is missing, empty introspection data is returned.
1 parent 82f72fe commit f2bede8

File tree

4 files changed

+518
-0
lines changed

4 files changed

+518
-0
lines changed

libc/libc.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ import (
88
"fmt"
99
"regexp"
1010

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

1416
// LibcInfo contains introspection information extracted from the C-library
1517
type LibcInfo struct {
1618
// TSDInfo is the TSDInfo extracted for this C-library
1719
TSDInfo TSDInfo
20+
// TODO comment
21+
DTVInfo DTVInfo
1822
}
1923

2024
// TSDInfo contains information to access C-library's Thread Specific Data from eBPF
@@ -35,6 +39,16 @@ type TSDInfo struct {
3539
Indirect uint8
3640
}
3741

42+
// TODO comment
43+
type DTVInfo struct {
44+
// Offset is the offset of DTV from FS base (or from thread pointer)
45+
Offset int64
46+
// EntryWidth is the size of each DTV entry in bytes
47+
EntryWidth uint32
48+
// Indirect is 0 if DTV is at FS+offset, 1 if at [FS+0]+offset
49+
Indirect uint8
50+
}
51+
3852
// This code analyzes the C-library provided POSIX defined function which is used
3953
// to read thread-specific data (TSD):
4054
// void *pthread_getspecific(pthread_key_t key);
@@ -96,8 +110,14 @@ func ExtractLibcInfo(ef *pfelf.File) (*LibcInfo, error) {
96110
return nil, err
97111
}
98112

113+
dtvinfo, err := extractDTVInfo(ef)
114+
if err != nil {
115+
return &LibcInfo{}, err
116+
}
117+
99118
return &LibcInfo{
100119
TSDInfo: *tsdinfo,
120+
DTVInfo: dtvinfo,
101121
}, nil
102122
}
103123

@@ -128,3 +148,33 @@ func extractTSDInfo(ef *pfelf.File) (*TSDInfo, error) {
128148
}
129149
return &info, nil
130150
}
151+
152+
// extractDTVInfo extracts the introspection data for the DTV to access TLS vars
153+
func extractDTVInfo(ef *pfelf.File) (DTVInfo, error) {
154+
var info DTVInfo
155+
_, code, err := ef.SymbolData("__tls_get_addr", 2048)
156+
if err != nil {
157+
// Only error out reading DTV if we have the symbol, but fail to parse it
158+
// if the symbol is not exported, failing to read it is not a critical error
159+
// and empty DTV introspection data is returned
160+
log.Warnf("unable to read '__tls_get_addr': %s, libc DTV introspection data is unavailable", err)
161+
return info, nil
162+
}
163+
164+
if len(code) < 8 {
165+
return info, fmt.Errorf("__tls_get_addr function size is %d", len(code))
166+
}
167+
168+
switch ef.Machine {
169+
case elf.EM_AARCH64:
170+
info, err = extractDTVInfoARM(code)
171+
case elf.EM_X86_64:
172+
info, err = extractDTVInfoX86(code)
173+
default:
174+
return info, fmt.Errorf("unsupported arch %s", ef.Machine.String())
175+
}
176+
if err != nil {
177+
return info, fmt.Errorf("failed to extract DTV data: %s", err)
178+
}
179+
return info, nil
180+
}

libc/libc_aarch64.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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)