1 package asm
2
3 import (
4 "bytes"
5 "encoding/binary"
6 "encoding/hex"
7 "errors"
8 "fmt"
9 "io"
10 "math"
11 "testing"
12
13 qt "github.com/frankban/quicktest"
14 )
15
16 var test64bitImmProg = []byte{
17
18 0x18, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f,
19 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
20 }
21
22 func TestRead64bitImmediate(t *testing.T) {
23 var ins Instruction
24 n, err := ins.Unmarshal(bytes.NewReader(test64bitImmProg), binary.LittleEndian)
25 if err != nil {
26 t.Fatal(err)
27 }
28 if want := uint64(InstructionSize * 2); n != want {
29 t.Errorf("Expected %d bytes to be read, got %d", want, n)
30 }
31
32 if c := ins.Constant; c != math.MinInt32-1 {
33 t.Errorf("Expected immediate to be %v, got %v", int64(math.MinInt32)-1, c)
34 }
35 }
36
37 func BenchmarkRead64bitImmediate(b *testing.B) {
38 r := &bytes.Reader{}
39 for i := 0; i < b.N; i++ {
40 r.Reset(test64bitImmProg)
41
42 var ins Instruction
43 if _, err := ins.Unmarshal(r, binary.LittleEndian); err != nil {
44 b.Fatal(err)
45 }
46 }
47 }
48
49 func TestWrite64bitImmediate(t *testing.T) {
50 insns := Instructions{
51 LoadImm(R0, math.MinInt32-1, DWord),
52 }
53
54 var buf bytes.Buffer
55 if err := insns.Marshal(&buf, binary.LittleEndian); err != nil {
56 t.Fatal(err)
57 }
58
59 if prog := buf.Bytes(); !bytes.Equal(prog, test64bitImmProg) {
60 t.Errorf("Marshalled program does not match:\n%s", hex.Dump(prog))
61 }
62 }
63
64 func BenchmarkWrite64BitImmediate(b *testing.B) {
65 ins := LoadImm(R0, math.MinInt32-1, DWord)
66
67 var buf bytes.Buffer
68 for i := 0; i < b.N; i++ {
69 buf.Reset()
70
71 if _, err := ins.Marshal(&buf, binary.LittleEndian); err != nil {
72 b.Fatal(err)
73 }
74 }
75 }
76
77 func TestUnmarshalInstructions(t *testing.T) {
78 r := bytes.NewReader(test64bitImmProg)
79
80 var insns Instructions
81 if err := insns.Unmarshal(r, binary.LittleEndian); err != nil {
82 t.Fatal(err)
83 }
84
85
86
87 r.Reset(test64bitImmProg)
88 if err := insns.Unmarshal(r, binary.LittleEndian); err != nil {
89 t.Fatal(err)
90 }
91
92 if len(insns) != 1 {
93 t.Fatalf("Expected one instruction, got %d", len(insns))
94 }
95 }
96
97 func TestSignedJump(t *testing.T) {
98 insns := Instructions{
99 JSGT.Imm(R0, -1, "foo"),
100 }
101
102 insns[0].Offset = 1
103
104 err := insns.Marshal(io.Discard, binary.LittleEndian)
105 if err != nil {
106 t.Error("Can't marshal signed jump:", err)
107 }
108 }
109
110 func TestInstructionRewriteMapConstant(t *testing.T) {
111 ins := LoadMapValue(R0, 123, 321)
112
113 qt.Assert(t, ins.MapPtr(), qt.Equals, 123)
114 qt.Assert(t, ins.mapOffset(), qt.Equals, uint32(321))
115
116 qt.Assert(t, ins.RewriteMapPtr(-1), qt.IsNil)
117 qt.Assert(t, ins.MapPtr(), qt.Equals, -1)
118
119 qt.Assert(t, ins.RewriteMapPtr(1), qt.IsNil)
120 qt.Assert(t, ins.MapPtr(), qt.Equals, 1)
121
122
123 qt.Assert(t, ins.mapOffset(), qt.Equals, uint32(321))
124
125 qt.Assert(t, ins.RewriteMapOffset(123), qt.IsNil)
126 qt.Assert(t, ins.mapOffset(), qt.Equals, uint32(123))
127
128
129 qt.Assert(t, ins.MapPtr(), qt.Equals, 1)
130
131 ins = Mov.Imm(R1, 32)
132 if err := ins.RewriteMapPtr(1); err == nil {
133 t.Error("RewriteMapPtr rewriting bogus instruction")
134 }
135 if err := ins.RewriteMapOffset(1); err == nil {
136 t.Error("RewriteMapOffset rewriting bogus instruction")
137 }
138 }
139
140 func TestInstructionLoadMapValue(t *testing.T) {
141 ins := LoadMapValue(R0, 1, 123)
142 if !ins.IsLoadFromMap() {
143 t.Error("isLoadFromMap returns false")
144 }
145 if fd := ins.mapFd(); fd != 1 {
146 t.Error("Expected map fd to be 1, got", fd)
147 }
148 if off := ins.mapOffset(); off != 123 {
149 t.Fatal("Expected map offset to be 123 after changin the pointer, got", off)
150 }
151 }
152
153 func TestInstructionsRewriteMapPtr(t *testing.T) {
154 insns := Instructions{
155 LoadMapPtr(R1, 0).WithReference("good"),
156 Return(),
157 }
158
159 if err := insns.RewriteMapPtr("good", 1); err != nil {
160 t.Fatal(err)
161 }
162
163 if insns[0].Constant != 1 {
164 t.Error("Constant should be 1, have", insns[0].Constant)
165 }
166
167 if err := insns.RewriteMapPtr("good", 2); err != nil {
168 t.Fatal(err)
169 }
170
171 if insns[0].Constant != 2 {
172 t.Error("Constant should be 2, have", insns[0].Constant)
173 }
174
175 if err := insns.RewriteMapPtr("bad", 1); !errors.Is(err, ErrUnreferencedSymbol) {
176 t.Error("Rewriting unreferenced map doesn't return appropriate error")
177 }
178 }
179
180
181
182 func ExampleInstructions_Format() {
183
184 insns := Instructions{
185 FnMapLookupElem.Call().WithSymbol("my_func").WithSource(Comment("bpf_map_lookup_elem()")),
186 LoadImm(R0, 42, DWord).WithSource(Comment("abc = 42")),
187 Return(),
188 }
189
190 fmt.Println("Default format:")
191 fmt.Printf("%v\n", insns)
192
193 fmt.Println("Don't indent instructions:")
194 fmt.Printf("%.0v\n", insns)
195
196 fmt.Println("Indent using spaces:")
197 fmt.Printf("% v\n", insns)
198
199 fmt.Println("Control symbol indentation:")
200 fmt.Printf("%2v\n", insns)
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233 }
234
235 func TestReadSrcDst(t *testing.T) {
236 testSrcDstProg := []byte{
237
238
239 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240 }
241
242 testcases := []struct {
243 bo binary.ByteOrder
244 dst, src Register
245 }{
246 {binary.BigEndian, R1, R0},
247 {binary.LittleEndian, R0, R1},
248 }
249
250 for _, tc := range testcases {
251 t.Run(tc.bo.String(), func(t *testing.T) {
252 var ins Instruction
253 _, err := ins.Unmarshal(bytes.NewReader(testSrcDstProg), tc.bo)
254 if err != nil {
255 t.Fatal(err)
256 }
257 if ins.Dst != tc.dst {
258 t.Errorf("Expected destination to be %v, got %v", tc.dst, ins.Dst)
259 }
260 if ins.Src != tc.src {
261 t.Errorf("Expected source to be %v, got %v", tc.src, ins.Src)
262 }
263 })
264 }
265 }
266
267 func TestInstructionIterator(t *testing.T) {
268 insns := Instructions{
269 LoadImm(R0, 0, Word),
270 LoadImm(R0, 0, DWord),
271 Return(),
272 }
273 offsets := []RawInstructionOffset{0, 1, 3}
274
275 iter := insns.Iterate()
276 for i := 0; i < len(insns); i++ {
277 if !iter.Next() {
278 t.Fatalf("Expected %dth call to Next to return true", i)
279 }
280
281 if iter.Ins == nil {
282 t.Errorf("Expected iter.Ins to be non-nil")
283 }
284 if iter.Index != i {
285 t.Errorf("Expected iter.Index to be %d, got %d", i, iter.Index)
286 }
287 if iter.Offset != offsets[i] {
288 t.Errorf("Expected iter.Offset to be %d, got %d", offsets[i], iter.Offset)
289 }
290 }
291 }
292
293 func TestMetadataCopyOnWrite(t *testing.T) {
294 c := qt.New(t)
295
296
297
298
299
300 ins := Ja.Label("my_func")
301 ins2 := ins.WithReference("my_func2")
302
303 c.Assert(ins.Reference(), qt.Equals, "my_func", qt.Commentf("WithReference updated ins"))
304 c.Assert(ins2.Reference(), qt.Equals, "my_func2", qt.Commentf("WithReference didn't update ins2"))
305
306
307 ins = Ja.Label("").WithSymbol("my_sym")
308 ins2 = ins.WithSymbol("my_sym2")
309
310 c.Assert(ins.Symbol(), qt.Equals, "my_sym", qt.Commentf("WithSymbol updated ins"))
311 c.Assert(ins2.Symbol(), qt.Equals, "my_sym2", qt.Commentf("WithSymbol didn't update ins2"))
312
313
314 ins = LoadMapPtr(R1, 0)
315 ins2 = ins
316
317 testMap := testFDer(1)
318 c.Assert(ins2.AssociateMap(testMap), qt.IsNil, qt.Commentf("failed to associate map with ins2"))
319
320 c.Assert(ins.Map(), qt.IsNil, qt.Commentf("AssociateMap updated ins"))
321 c.Assert(ins2.Map(), qt.Equals, testMap, qt.Commentf("AssociateMap didn't update ins2"))
322 }
323
324 type testFDer int
325
326 func (t testFDer) FD() int {
327 return int(t)
328 }
329
View as plain text