Skip to content

Commit b00d794

Browse files
authored
Merge pull request #86 from parca-dev/upstream-update
upstream update
2 parents 670e84e + 06c40dc commit b00d794

36 files changed

+1280
-219
lines changed

.github/workflows/codeql.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ permissions: read-all
1212

1313
jobs:
1414
analyze:
15+
permissions:
16+
security-events: write # for github/codeql-action/analyze to upload SARIF results
1517
name: Analyze Go (${{ matrix.target_arch }})
1618
if: ${{ github.actor != 'dependabot[bot]' && github.repository == 'open-telemetry/opentelemetry-ebpf-profiler' }}
1719
runs-on: ubuntu-24.04

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,10 @@ For more information about the maintainer role, see the [community repository](h
145145

146146
### Approvers
147147

148+
- [Damien Mathieu](https://github.com/dmathieu), Elastic
148149
- [Florian Lehner](https://github.com/florianl), Elastic
149150
- [Joel Höner](https://github.com/athre0z)
150151
- [Tim Rühsen](https://github.com/rockdaboot), Elastic
151-
- [Damien Mathieu](https://github.com/dmathieu), Elastic
152152

153153
For more information about the approver role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#approver).
154154

asm/amd/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
testdata/fuzz

asm/amd/interpreter.go

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package amd // import "go.opentelemetry.io/ebpf-profiler/asm/amd"
5+
6+
import (
7+
"fmt"
8+
"io"
9+
"math"
10+
11+
"go.opentelemetry.io/ebpf-profiler/asm/expression"
12+
"golang.org/x/arch/x86/x86asm"
13+
)
14+
15+
type CodeBlock struct {
16+
Address expression.Expression
17+
Code []byte
18+
}
19+
20+
type Interpreter struct {
21+
Regs Registers
22+
code []byte
23+
CodeAddress expression.Expression
24+
pc int
25+
}
26+
27+
func NewInterpreter() *Interpreter {
28+
it := &Interpreter{}
29+
it.initRegs()
30+
return it
31+
}
32+
33+
func NewInterpreterWithCode(code []byte) *Interpreter {
34+
it := &Interpreter{code: code, CodeAddress: expression.Named("code address")}
35+
it.initRegs()
36+
return it
37+
}
38+
39+
func (i *Interpreter) ResetCode(code []byte, address expression.Expression) {
40+
i.code = code
41+
i.CodeAddress = address
42+
i.pc = 0
43+
}
44+
45+
func (i *Interpreter) Loop() (x86asm.Inst, error) {
46+
return i.LoopWithBreak(func(x86asm.Inst) bool { return false })
47+
}
48+
49+
func (i *Interpreter) LoopWithBreak(breakLoop func(op x86asm.Inst) bool) (x86asm.Inst, error) {
50+
prev := x86asm.Inst{}
51+
for {
52+
op, err := i.Step()
53+
if err != nil {
54+
return prev, err
55+
}
56+
if breakLoop(op) {
57+
return op, nil
58+
}
59+
prev = op
60+
}
61+
}
62+
63+
func (i *Interpreter) Step() (x86asm.Inst, error) {
64+
if len(i.code) == 0 {
65+
return x86asm.Inst{}, io.EOF
66+
}
67+
var inst x86asm.Inst
68+
var err error
69+
if ok, instLen := DecodeSkippable(i.code); ok {
70+
inst = x86asm.Inst{Op: x86asm.NOP, Len: instLen}
71+
} else {
72+
inst, err = x86asm.Decode(i.code, 64)
73+
if err != nil {
74+
return inst, fmt.Errorf("at 0x%x : %v", i.pc, err)
75+
}
76+
}
77+
i.pc += inst.Len
78+
i.code = i.code[inst.Len:]
79+
i.Regs.setX86asm(x86asm.RIP, expression.Add(i.CodeAddress, expression.Imm(uint64(i.pc))))
80+
switch inst.Op {
81+
case x86asm.ADD:
82+
if dst, ok := inst.Args[0].(x86asm.Reg); ok {
83+
left := i.Regs.getX86asm(dst)
84+
switch src := inst.Args[1].(type) {
85+
case x86asm.Imm:
86+
right := expression.Imm(uint64(src))
87+
i.Regs.setX86asm(dst, expression.Add(left, right))
88+
case x86asm.Reg:
89+
right := i.Regs.getX86asm(src)
90+
i.Regs.setX86asm(dst, expression.Add(left, right))
91+
case x86asm.Mem:
92+
right := i.MemArg(src)
93+
right = expression.MemWithSegment(src.Segment, right, inst.MemBytes)
94+
i.Regs.setX86asm(dst, expression.Add(left, right))
95+
}
96+
}
97+
case x86asm.SHL:
98+
if dst, ok := inst.Args[0].(x86asm.Reg); ok {
99+
if src, imm := inst.Args[1].(x86asm.Imm); imm {
100+
v := expression.Multiply(
101+
i.Regs.getX86asm(dst),
102+
expression.Imm(uint64(math.Pow(2, float64(src)))),
103+
)
104+
i.Regs.setX86asm(dst, v)
105+
}
106+
}
107+
case x86asm.MOV, x86asm.MOVZX, x86asm.MOVSXD, x86asm.MOVSX:
108+
if dst, ok := inst.Args[0].(x86asm.Reg); ok {
109+
switch src := inst.Args[1].(type) {
110+
case x86asm.Imm:
111+
i.Regs.setX86asm(dst, expression.Imm(uint64(src)))
112+
case x86asm.Reg:
113+
i.Regs.setX86asm(dst, i.Regs.getX86asm(src))
114+
case x86asm.Mem:
115+
v := i.MemArg(src)
116+
117+
dataSizeBits := inst.DataSize
118+
119+
v = expression.MemWithSegment(src.Segment, v, inst.MemBytes)
120+
if inst.Op == x86asm.MOVSXD || inst.Op == x86asm.MOVSX {
121+
v = expression.SignExtend(v, dataSizeBits)
122+
} else {
123+
v = expression.ZeroExtend(v, dataSizeBits)
124+
}
125+
i.Regs.setX86asm(dst, v)
126+
}
127+
}
128+
case x86asm.XOR:
129+
if dst, ok := inst.Args[0].(x86asm.Reg); ok {
130+
if src, reg := inst.Args[1].(x86asm.Reg); reg {
131+
if src == dst {
132+
i.Regs.setX86asm(dst, expression.Imm(0))
133+
}
134+
}
135+
}
136+
case x86asm.AND:
137+
if dst, ok := inst.Args[0].(x86asm.Reg); ok {
138+
if src, imm := inst.Args[1].(x86asm.Imm); imm {
139+
if src == 3 { // todo other cases
140+
i.Regs.setX86asm(dst, expression.ZeroExtend(i.Regs.getX86asm(dst), 2))
141+
}
142+
}
143+
}
144+
case x86asm.LEA:
145+
if dst, ok := inst.Args[0].(x86asm.Reg); ok {
146+
if src, mem := inst.Args[1].(x86asm.Mem); mem {
147+
v := i.MemArg(src)
148+
i.Regs.setX86asm(dst, v)
149+
}
150+
}
151+
default:
152+
}
153+
return inst, nil
154+
}
155+
156+
func (i *Interpreter) MemArg(src x86asm.Mem) expression.Expression {
157+
vs := make([]expression.Expression, 0, 3)
158+
if src.Disp != 0 {
159+
vs = append(vs, expression.Imm(uint64(src.Disp)))
160+
}
161+
if src.Base != 0 {
162+
vs = append(vs, i.Regs.getX86asm(src.Base))
163+
}
164+
if src.Index != 0 {
165+
v := expression.Multiply(
166+
i.Regs.getX86asm(src.Index),
167+
expression.Imm(uint64(src.Scale)),
168+
)
169+
vs = append(vs, v)
170+
}
171+
v := expression.Add(vs...)
172+
return v
173+
}
174+
175+
func (i *Interpreter) initRegs() {
176+
for j := 0; j < len(i.Regs.regs); j++ {
177+
i.Regs.regs[j] = expression.Named(Reg(j).String())
178+
}
179+
}

asm/amd/interpreter_test.go

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package amd
5+
6+
import (
7+
"io"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
"go.opentelemetry.io/ebpf-profiler/asm/expression"
13+
)
14+
15+
func BenchmarkPythonInterpreter(b *testing.B) {
16+
for i := 0; i < b.N; i++ {
17+
testPythonInterpreter(b)
18+
}
19+
}
20+
21+
func TestPythonInterpreter(t *testing.T) {
22+
testPythonInterpreter(t)
23+
}
24+
25+
func testPythonInterpreter(t testing.TB) {
26+
// 00010000 4D 89 F2 mov r10, r14
27+
// 00010003 45 0F B6 36 movzx r14d, byte ptr [r14]
28+
// 00010007 48 8D 05 2D B3 35 00 lea rax, [rip + 0x35b32d]
29+
// 0001000E 4C 8B 6C 24 08 mov r13, qword ptr [rsp + 8]
30+
// 00010013 48 89 C1 mov rcx, rax
31+
// 00010016 48 89 44 24 10 mov qword ptr [rsp + 0x10], rax
32+
// 0001001B 45 0F B6 5A 01 movzx r11d, byte ptr [r10 + 1]
33+
// 00010020 41 0F B6 C6 movzx eax, r14b
34+
// 00010024 48 8B 04 C1 mov rax, qword ptr [rcx + rax*8]
35+
// 00010028 FF E0 jmp rax
36+
code := []byte{
37+
0x4d, 0x89, 0xf2, 0x45, 0x0f, 0xb6, 0x36, 0x48, 0x8d, 0x05, 0x2d, 0xb3, 0x35,
38+
0x00, 0x4c, 0x8b, 0x6c, 0x24, 0x08, 0x48, 0x89, 0xc1, 0x48, 0x89, 0x44, 0x24,
39+
0x10, 0x45, 0x0f, 0xb6, 0x5a, 0x01, 0x41, 0x0f, 0xb6, 0xc6, 0x48, 0x8b, 0x04,
40+
0xc1, 0xff, 0xe0,
41+
}
42+
it := NewInterpreterWithCode(code)
43+
it.CodeAddress = expression.Imm(0x8AF05)
44+
r14 := it.Regs.Get(R14)
45+
_, err := it.Loop()
46+
if err == nil || err != io.EOF {
47+
t.Fatal(err)
48+
}
49+
actual := it.Regs.Get(RAX)
50+
expected := expression.Mem(
51+
expression.Add(
52+
expression.Multiply(
53+
expression.ZeroExtend8(expression.Mem1(r14)),
54+
expression.Imm(8),
55+
),
56+
expression.NewImmediateCapture("switch table"),
57+
),
58+
8,
59+
)
60+
if !actual.Match(expected) {
61+
t.Fatal()
62+
}
63+
}
64+
65+
func TestRecoverSwitchCase(t *testing.T) {
66+
blocks := []CodeBlock{
67+
{
68+
Address: expression.Imm(0x3310E3),
69+
// 003310E3 48 8B 44 24 20 mov rax, qword ptr [rsp + 0x20]
70+
// 003310E8 48 89 18 mov qword ptr [rax], rbx
71+
// 003310EB 49 83 C2 02 add r10, 2
72+
// 003310EF 44 89 E0 mov eax, r12d
73+
// 003310F2 83 E0 03 and eax, 3
74+
// 003310F5 31 DB xor ebx, ebx
75+
// 003310F7 41 F6 C4 04 test r12b, 4
76+
// 003310FB 4C 89 74 24 10 mov qword ptr [rsp + 0x10], r14
77+
// 00331100 74 08 je 0x33110a
78+
Code: []byte{0x48, 0x8b, 0x44, 0x24, 0x20, 0x48, 0x89, 0x18, 0x49,
79+
0x83, 0xc2, 0x02, 0x44, 0x89, 0xe0, 0x83, 0xe0, 0x03, 0x31, 0xdb,
80+
0x41, 0xf6, 0xc4, 0x04, 0x4c, 0x89, 0x74, 0x24, 0x10, 0x74, 0x08},
81+
},
82+
{
83+
Address: expression.Imm(0x33110a),
84+
// 0033110A 4D 89 DC mov r12, r11
85+
// 0033110D 4D 8D 47 F8 lea r8, [r15 - 8]
86+
// 00331111 4C 89 7C 24 60 mov qword ptr [rsp + 0x60], r15
87+
// 00331116 4D 8B 7F F8 mov r15, qword ptr [r15 - 8]
88+
// 0033111A 48 8B 0D 87 06 17 01 mov rcx, qword ptr [rip + 0x1170687]
89+
// 00331121 89 C0 mov eax, eax
90+
// 00331123 48 8D 15 02 E7 C0 00 lea rdx, [rip + 0xc0e702]
91+
// 0033112A 48 63 04 82 movsxd rax, dword ptr [rdx + rax*4]
92+
// 0033112E 48 01 D0 add rax, rdx
93+
// 00331131 4C 89 D5 mov rbp, r10
94+
// 00331134 4D 89 C5 mov r13, r8
95+
// 00331137 FF E0 jmp rax
96+
Code: []byte{
97+
0x4d, 0x89, 0xdc, 0x4d, 0x8d, 0x47, 0xf8, 0x4c, 0x89, 0x7c, 0x24,
98+
0x60, 0x4d, 0x8b, 0x7f, 0xf8, 0x48, 0x8b, 0x0d, 0x87, 0x06, 0x17,
99+
0x01, 0x89, 0xc0, 0x48, 0x8d, 0x15, 0x02, 0xe7, 0xc0, 0x00, 0x48,
100+
0x63, 0x04, 0x82, 0x48, 0x01, 0xd0, 0x4c, 0x89, 0xd5, 0x4d, 0x89,
101+
0xc5, 0xff, 0xe0,
102+
},
103+
},
104+
}
105+
it := NewInterpreter()
106+
initR12 := it.Regs.Get(R12)
107+
it.ResetCode(blocks[0].Code, blocks[0].Address)
108+
_, err := it.Loop()
109+
require.ErrorIs(t, err, io.EOF)
110+
111+
expected := expression.ZeroExtend(initR12, 2)
112+
assertEval(t, it.Regs.Get(RAX), expected)
113+
it.ResetCode(blocks[1].Code, blocks[1].Address)
114+
_, err = it.Loop()
115+
require.ErrorIs(t, err, io.EOF)
116+
table := expression.NewImmediateCapture("table")
117+
base := expression.NewImmediateCapture("base")
118+
expected = expression.Add(
119+
expression.SignExtend(
120+
expression.Mem(
121+
expression.Add(
122+
expression.Multiply(
123+
expression.ZeroExtend(initR12, 2),
124+
expression.Imm(4),
125+
),
126+
table,
127+
),
128+
4,
129+
),
130+
64,
131+
),
132+
base,
133+
)
134+
assertEval(t, it.Regs.Get(RAX), expected)
135+
assert.EqualValues(t, 0xf3f82c, table.CapturedValue())
136+
assert.EqualValues(t, 0xf3f82c, base.CapturedValue())
137+
}
138+
139+
func assertEval(t *testing.T, left, right expression.Expression) {
140+
if !left.Match(right) {
141+
assert.Failf(t, "failed to eval %s to %s", left.DebugString(), right.DebugString())
142+
t.Logf("left %s", left.DebugString())
143+
t.Logf("right %s", right.DebugString())
144+
}
145+
}
146+
147+
func FuzzInterpreter(f *testing.F) {
148+
f.Fuzz(func(_ *testing.T, code []byte) {
149+
i := NewInterpreterWithCode(code)
150+
_, _ = i.Loop()
151+
})
152+
}
153+
154+
func TestMoveSignExtend(t *testing.T) {
155+
i := NewInterpreterWithCode([]byte{
156+
// 00000000 B8 01 00 00 00 mov eax, 1
157+
// 00000005 8B 40 04 mov eax, dword ptr [rax + 4]
158+
// 00000008 B8 02 00 00 00 mov eax, 2
159+
// 0000000D 48 0F B6 40 04 movzx rax, byte ptr [rax + 4]
160+
// 00000012 B8 03 00 00 00 mov eax, 3
161+
// 00000017 48 0F BF 40 04 movsx rax, word ptr [rax + 4]
162+
0xB8, 0x01, 0x00, 0x00, 0x00, 0x8B, 0x40, 0x04,
163+
0xB8, 0x02, 0x00, 0x00, 0x00, 0x48, 0x0F, 0xB6,
164+
0x40, 0x04, 0xB8, 0x03, 0x00, 0x00, 0x00, 0x48,
165+
0x0F, 0xBF, 0x40, 0x04,
166+
})
167+
_, err := i.Loop()
168+
require.ErrorIs(t, err, io.EOF)
169+
pattern := expression.SignExtend(expression.Mem(expression.Imm(7), 2), 64)
170+
require.True(t, i.Regs.Get(RAX).Match(pattern))
171+
}

0 commit comments

Comments
 (0)