Skip to content

Commit 4bdc15b

Browse files
author
Florian Lehner
committed
python: get the Thread State from a Thread-local
This change originates from python/cpython#103323. Signed-off-by: Florian Lehner <florian.lehner@elastic.co>
1 parent 846941a commit 4bdc15b

10 files changed

Lines changed: 536 additions & 8 deletions

File tree

‎interpreter/python/amd64_decode.go‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,47 @@ func evaluateStubAnswerAMD64(res e.Expression, memBase uint64) (uint64, error) {
6363
}
6464
return 0, fmt.Errorf("not found %s", res.DebugString())
6565
}
66+
67+
// extractTLSOffsetFromCodeAMD64 extracts the TLS offset by analyzing x86_64 assembly code.
68+
// It looks for MOV instructions with FS segment prefix (e.g., MOV rax, FS:[offset]).
69+
func extractTLSOffsetFromCodeAMD64(code []byte, baseAddr uint64) (int64, error) {
70+
it := amd.NewInterpreterWithCode(code)
71+
it.CodeAddress = e.Imm(baseAddr)
72+
73+
offset := e.NewImmediateCapture("tls_offset")
74+
expected := e.MemWithSegment8(x86asm.FS, offset)
75+
76+
for {
77+
op, err := it.Step()
78+
if err != nil {
79+
break
80+
}
81+
82+
if op.Op != x86asm.MOV {
83+
continue
84+
}
85+
86+
// Check if the source argument (Args[1]) uses FS segment
87+
mem, ok := op.Args[1].(x86asm.Mem)
88+
if !ok || mem.Segment != x86asm.FS {
89+
continue
90+
}
91+
92+
// Get the destination register's expression
93+
dst, ok := op.Args[0].(x86asm.Reg)
94+
if !ok {
95+
continue
96+
}
97+
98+
actual := it.Regs.GetX86(dst)
99+
if actual.Match(expected) {
100+
capturedValue := offset.CapturedValue()
101+
signedOffset := int64(int32(capturedValue))
102+
if (signedOffset < 0 && signedOffset > -4096) || (signedOffset > 0 && signedOffset < 4096) {
103+
return signedOffset, nil
104+
}
105+
}
106+
}
107+
108+
return 0, fmt.Errorf("could not find FS-relative MOV instruction")
109+
}

‎interpreter/python/arm64_decode.go‎

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
package python // import "go.opentelemetry.io/ebpf-profiler/interpreter/python"
55

66
import (
7+
"fmt"
8+
79
ah "go.opentelemetry.io/ebpf-profiler/armhelpers"
810
"go.opentelemetry.io/ebpf-profiler/libpf"
11+
"go.opentelemetry.io/ebpf-profiler/libpf/pfelf"
912
aa "golang.org/x/arch/arm64/arm64asm"
1013
)
1114

@@ -100,3 +103,98 @@ func decodeStubArgumentARM64(code []byte,
100103

101104
return libpf.SymbolValueInvalid
102105
}
106+
107+
// extractTLSOffsetFromCodeARM64 extracts the TLS offset by analyzing ARM64 assembly code.
108+
// It looks for the pattern: MRS Xn, TPIDR_EL0 followed by ADD Xn, Xn, #offset or LDR [Xn, #offset].
109+
func extractTLSOffsetFromCodeARM64(code []byte, baseAddr uint64, visited map[uint64]bool, depth int, ef *pfelf.File) (int64, error) {
110+
// Prevent infinite recursion
111+
const maxDepth = 5
112+
if depth > maxDepth {
113+
return 0, fmt.Errorf("max recursion depth exceeded")
114+
}
115+
116+
if visited[baseAddr] {
117+
return 0, fmt.Errorf("already visited address 0x%x", baseAddr)
118+
}
119+
visited[baseAddr] = true
120+
121+
foundMRS := false
122+
var tpReg int
123+
124+
for offs := 0; offs < len(code)-4; offs += 4 {
125+
inst, err := aa.Decode(code[offs:])
126+
if err != nil {
127+
continue
128+
}
129+
130+
// Check for MRS Xn, TPIDR_EL0 (system register S3_3_C13_C0_2)
131+
if inst.Op == aa.MRS && inst.Args[1].String() == "S3_3_C13_C0_2" {
132+
reg, ok := ah.Xreg2num(inst.Args[0])
133+
if !ok {
134+
continue
135+
}
136+
tpReg = reg
137+
foundMRS = true
138+
139+
// Look ahead for ADD or LDR using this register
140+
for j := offs + 4; j < len(code)-4 && j < offs+64; j += 4 {
141+
nextInst, err := aa.Decode(code[j:])
142+
if err != nil {
143+
continue
144+
}
145+
146+
// Check for ADD Xd, Xn, #imm
147+
if nextInst.Op == aa.ADD {
148+
destReg, destOk := ah.Xreg2num(nextInst.Args[0])
149+
srcReg, srcOk := ah.Xreg2num(nextInst.Args[1])
150+
imm, immOk := ah.DecodeImmediate(nextInst.Args[2])
151+
152+
if destOk && srcOk && immOk && srcReg == tpReg {
153+
if imm > 0 && imm < 0x1000 {
154+
return int64(imm), nil
155+
}
156+
// Track the new register holding TP+offset
157+
tpReg = destReg
158+
}
159+
}
160+
161+
// Check for LDR Xm, [Xn, #imm]
162+
if nextInst.Op == aa.LDR {
163+
// Args[1] is MemImmediate
164+
if mem, ok := nextInst.Args[1].(aa.MemImmediate); ok {
165+
baseReg, regOk := ah.Xreg2num(mem.Base)
166+
imm, immOk := ah.DecodeImmediate(mem)
167+
168+
if regOk && immOk && baseReg == tpReg {
169+
if imm > 0 && imm < 0x1000 {
170+
return int64(imm), nil
171+
}
172+
}
173+
}
174+
}
175+
}
176+
}
177+
178+
// Check for unconditional branch
179+
if inst.Op == aa.B {
180+
if pcrel, ok := inst.Args[0].(aa.PCRel); ok {
181+
targetAddr := int64(baseAddr) + int64(offs) + int64(pcrel)
182+
183+
if targetAddr > 0 && targetAddr < 0x100000000 && !visited[uint64(targetAddr)] {
184+
targetCode := make([]byte, 256)
185+
err := ef.GetRemoteMemory().Read(libpf.Address(targetAddr), targetCode)
186+
if err == nil {
187+
if result, err := extractTLSOffsetFromCodeARM64(targetCode, uint64(targetAddr), visited, depth+1, ef); err == nil {
188+
return result, nil
189+
}
190+
}
191+
}
192+
}
193+
}
194+
}
195+
196+
if !foundMRS {
197+
return 0, fmt.Errorf("could not find MRS TPIDR_EL0 instruction")
198+
}
199+
return 0, fmt.Errorf("found MRS TPIDR_EL0 but no matching ADD/LDR with TLS offset")
200+
}

0 commit comments

Comments
 (0)