1
2
3
4
5
6
7 package x86asm
8
9 import (
10 "bufio"
11 "bytes"
12 "encoding/hex"
13 "flag"
14 "fmt"
15 "io"
16 "io/ioutil"
17 "log"
18 "math/rand"
19 "os"
20 "os/exec"
21 "regexp"
22 "runtime"
23 "strings"
24 "testing"
25 "time"
26 )
27
28 var (
29 printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths")
30 dumpTest = flag.Bool("dump", false, "dump all encodings")
31 mismatch = flag.Bool("mismatch", false, "log allowed mismatches")
32 longTest = flag.Bool("long", false, "long test")
33 keep = flag.Bool("keep", false, "keep object files around")
34 debug = false
35 )
36
37
38
39 type ExtInst struct {
40 addr uint32
41 enc [32]byte
42 nenc int
43 text string
44 }
45
46 func (r ExtInst) String() string {
47 return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
48 }
49
50
51 type ExtDis struct {
52 Arch int
53 Dec chan ExtInst
54 File *os.File
55 Size int
56 KeepFile bool
57 Cmd *exec.Cmd
58 }
59
60
61
62 func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
63 if *keep {
64 log.Printf("%s\n", strings.Join(cmd, " "))
65 }
66 ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
67 out, err := ext.Cmd.StdoutPipe()
68 if err != nil {
69 return nil, fmt.Errorf("stdoutpipe: %v", err)
70 }
71 if err := ext.Cmd.Start(); err != nil {
72 return nil, fmt.Errorf("exec: %v", err)
73 }
74
75 b := bufio.NewReaderSize(out, 1<<20)
76 return b, nil
77 }
78
79
80 func (ext *ExtDis) Wait() error {
81 return ext.Cmd.Wait()
82 }
83
84
85
86
87
88
89
90
91
92
93
94
95 func testExtDis(
96 t *testing.T,
97 syntax string,
98 arch int,
99 extdis func(ext *ExtDis) error,
100 generate func(f func([]byte)),
101 allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool,
102 ) {
103 decoderCover = make([]bool, len(decoder))
104 defer func() {
105 decoderCover = nil
106 }()
107
108 start := time.Now()
109 ext := &ExtDis{
110 Dec: make(chan ExtInst),
111 Arch: arch,
112 }
113 errc := make(chan error)
114
115
116 file, f, size, err := writeInst(generate)
117 if err != nil {
118 t.Fatal(err)
119 }
120 ext.Size = size
121 ext.File = f
122 defer func() {
123 f.Close()
124 if !*keep {
125 os.Remove(file)
126 }
127 }()
128
129
130 var (
131 totalTests = 0
132 totalSkips = 0
133 totalErrors = 0
134
135 errors = make([]string, 0, 100)
136 )
137 go func() {
138 errc <- extdis(ext)
139 }()
140 generate(func(enc []byte) {
141 dec, ok := <-ext.Dec
142 if !ok {
143 t.Errorf("decoding stream ended early")
144 return
145 }
146 inst, text := disasm(syntax, arch, pad(enc))
147 totalTests++
148 if *dumpTest {
149 fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
150 }
151 if text != dec.text || inst.Len != dec.nenc {
152 suffix := ""
153 if allowedMismatch(text, size, &inst, dec) {
154 totalSkips++
155 if !*mismatch {
156 return
157 }
158 suffix += " (allowed mismatch)"
159 }
160 totalErrors++
161 if len(errors) >= cap(errors) {
162 j := rand.Intn(totalErrors)
163 if j >= cap(errors) {
164 return
165 }
166 errors = append(errors[:j], errors[j+1:]...)
167 }
168 errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix))
169 }
170 })
171
172 if *mismatch {
173 totalErrors -= totalSkips
174 }
175
176 for _, b := range errors {
177 t.Log(b)
178 }
179
180 if totalErrors > 0 {
181 t.Fail()
182 }
183 t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
184
185 if err := <-errc; err != nil {
186 t.Fatalf("external disassembler: %v", err)
187 }
188 }
189
190 const start = 0x8000
191
192
193
194
195 func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
196 f, err = ioutil.TempFile("", "x86map")
197 if err != nil {
198 return
199 }
200
201 file = f.Name()
202
203 f.Seek(start, io.SeekStart)
204 w := bufio.NewWriter(f)
205 defer w.Flush()
206 size = 0
207 generate(func(x []byte) {
208 if len(x) > 16 {
209 x = x[:16]
210 }
211 if debug {
212 fmt.Printf("%#x: %x%x\n", start+size, x, pops[len(x):])
213 }
214 w.Write(x)
215 w.Write(pops[len(x):])
216 size += len(pops)
217 })
218 return file, f, size, nil
219 }
220
221
222
223
224
225
226 var pops = []byte{
227 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
228 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
229 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
230 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
231 }
232
233
234 func pad(enc []byte) []byte {
235 return append(enc[:len(enc):len(enc)], pops...)
236 }
237
238
239
240 func disasm(syntax string, mode int, src []byte) (inst Inst, text string) {
241
242
243
244
245
246 var cover float64
247 if *printTests {
248 cover -= coverage()
249 }
250
251 inst, err := decode1(src, mode, syntax == "gnu")
252 if err != nil {
253 text = "error: " + err.Error()
254 } else {
255 switch syntax {
256 case "gnu":
257 text = GNUSyntax(inst, 0, nil)
258 case "intel":
259 text = IntelSyntax(inst, 0, nil)
260 case "plan9":
261 text = GoSyntax(inst, 0, nil)
262 default:
263 text = "error: unknown syntax " + syntax
264 }
265 }
266
267 if *printTests {
268 cover += coverage()
269 if cover > 0 {
270 max := len(src)
271 if max > 16 && inst.Len <= 16 {
272 max = 16
273 }
274 fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], mode, syntax, text)
275 }
276 }
277
278 return
279 }
280
281
282
283
284 func coverage() float64 {
285
306
307 var f float64
308
309 f += decodeCoverage()
310 return f
311 }
312
313 func decodeCoverage() float64 {
314 n := 0
315 for _, t := range decoderCover {
316 if t {
317 n++
318 }
319 }
320 return float64(1+n) / float64(1+len(decoderCover))
321 }
322
323
324
325
326 func isPrefix(text string) bool {
327 return prefixByte[text] > 0
328 }
329
330
331 var prefixByte = map[string]byte{
332 "es": 0x26,
333 "cs": 0x2e,
334 "ss": 0x36,
335 "ds": 0x3e,
336 "fs": 0x64,
337 "gs": 0x65,
338 "data16": 0x66,
339 "addr16": 0x67,
340 "lock": 0xf0,
341 "repn": 0xf2,
342 "repne": 0xf2,
343 "rep": 0xf3,
344 "repe": 0xf3,
345 "xacquire": 0xf2,
346 "xrelease": 0xf3,
347 "bnd": 0xf2,
348 "addr32": 0x66,
349 "data32": 0x67,
350 }
351
352
353
354 func hasPrefix(s string, prefixes ...string) bool {
355 for _, prefix := range prefixes {
356 for s := s; s != ""; {
357 if strings.HasPrefix(s, prefix) {
358 return true
359 }
360 i := strings.Index(s, " ")
361 if i < 0 {
362 break
363 }
364 s = s[i+1:]
365 }
366 }
367 return false
368 }
369
370
371 func contains(s string, substrings ...string) bool {
372 for _, sub := range substrings {
373 if strings.Contains(s, sub) {
374 return true
375 }
376 }
377 return false
378 }
379
380
381 func isHex(b byte) bool { return b == '0' || unhex[b] > 0 }
382
383
384
385
386
387 func parseHex(hex []byte, raw []byte) ([]byte, bool) {
388 hex = trimSpace(hex)
389 for j := 0; j < len(hex); {
390 for hex[j] == ' ' || hex[j] == '\t' {
391 j++
392 }
393 if j >= len(hex) {
394 break
395 }
396 if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
397 return nil, false
398 }
399 raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]])
400 j += 2
401 }
402 return raw, true
403 }
404
405 var unhex = [256]byte{
406 '0': 0,
407 '1': 1,
408 '2': 2,
409 '3': 3,
410 '4': 4,
411 '5': 5,
412 '6': 6,
413 '7': 7,
414 '8': 8,
415 '9': 9,
416 'A': 10,
417 'B': 11,
418 'C': 12,
419 'D': 13,
420 'E': 14,
421 'F': 15,
422 'a': 10,
423 'b': 11,
424 'c': 12,
425 'd': 13,
426 'e': 14,
427 'f': 15,
428 }
429
430
431 func index(s []byte, t string) int {
432 i := 0
433 for {
434 j := bytes.IndexByte(s[i:], t[0])
435 if j < 0 {
436 return -1
437 }
438 i = i + j
439 if i+len(t) > len(s) {
440 return -1
441 }
442 for k := 1; k < len(t); k++ {
443 if s[i+k] != t[k] {
444 goto nomatch
445 }
446 }
447 return i
448 nomatch:
449 i++
450 }
451 }
452
453
454
455 func fixSpace(s []byte) []byte {
456 s = trimSpace(s)
457 for i := 0; i < len(s); i++ {
458 if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
459 goto Fix
460 }
461 }
462 return s
463
464 Fix:
465 b := s
466 w := 0
467 for i := 0; i < len(s); i++ {
468 c := s[i]
469 if c == '\t' || c == '\n' {
470 c = ' '
471 }
472 if c == ' ' && w > 0 && b[w-1] == ' ' {
473 continue
474 }
475 b[w] = c
476 w++
477 }
478 if w > 0 && b[w-1] == ' ' {
479 w--
480 }
481 return b[:w]
482 }
483
484
485 func trimSpace(s []byte) []byte {
486 j := len(s)
487 for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') {
488 j--
489 }
490 i := 0
491 for i < j && (s[i] == ' ' || s[i] == '\t') {
492 i++
493 }
494 return s[i:j]
495 }
496
497
498 var (
499 pcrel = regexp.MustCompile(`^((?:.* )?(?:j[a-z]+|call|ljmp|loopn?e?w?|xbegin)q?(?:,p[nt])?) 0x([0-9a-f]+)$`)
500 pcrelw = regexp.MustCompile(`^((?:.* )?(?:callw|jmpw|xbeginw|ljmpw)(?:,p[nt])?) 0x([0-9a-f]+)$`)
501 )
502
503
504
505
506
507
508
509
510
511 func hexCases(t *testing.T, encoded string) func(func([]byte)) {
512 return func(try func([]byte)) {
513 for _, x := range strings.Fields(encoded) {
514 src, err := hex.DecodeString(x)
515 if err != nil {
516 t.Errorf("parsing %q: %v", x, err)
517 }
518 try(src)
519 }
520 }
521 }
522
523
524
525 func testdataCases(t *testing.T) func(func([]byte)) {
526 var codes [][]byte
527 data, err := ioutil.ReadFile("testdata/decode.txt")
528 if err != nil {
529 t.Fatal(err)
530 }
531 for _, line := range strings.Split(string(data), "\n") {
532 line = strings.TrimSpace(line)
533 if line == "" || strings.HasPrefix(line, "#") {
534 continue
535 }
536 f := strings.Fields(line)[0]
537 i := strings.Index(f, "|")
538 if i < 0 {
539 t.Errorf("parsing %q: missing | separator", f)
540 continue
541 }
542 if i%2 != 0 {
543 t.Errorf("parsing %q: misaligned | separator", f)
544 }
545 code, err := hex.DecodeString(f[:i] + f[i+1:])
546 if err != nil {
547 t.Errorf("parsing %q: %v", f, err)
548 continue
549 }
550 codes = append(codes, code)
551 }
552
553 return func(try func([]byte)) {
554 for _, code := range codes {
555 try(code)
556 }
557 }
558 }
559
560
561
562 func manyPrefixes(try func([]byte)) {
563 var prefixBytes = []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36, 0x66, 0x67}
564 var enc []byte
565 for i := 0; i < 1<<uint(len(prefixBytes)); i++ {
566 enc = enc[:0]
567 for j, p := range prefixBytes {
568 if i&(1<<uint(j)) != 0 {
569 enc = append(enc, p)
570 }
571 }
572 if len(enc) > 0 {
573 k := i % len(enc)
574 enc[0], enc[k] = enc[k], enc[0]
575 }
576 try(enc)
577 }
578 }
579
580
581
582 func basicPrefixes(try func([]byte)) {
583 try(nil)
584 for _, b := range []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36} {
585 try([]byte{b})
586 }
587 }
588
589 func rexPrefixes(try func([]byte)) {
590 try(nil)
591 for _, b := range []byte{0x40, 0x48, 0x43, 0x4C} {
592 try([]byte{b})
593 }
594 }
595
596
597
598 func concat(gen1, gen2 func(func([]byte))) func(func([]byte)) {
599 return func(try func([]byte)) {
600 gen1(func(enc1 []byte) {
601 gen2(func(enc2 []byte) {
602 try(append(enc1[:len(enc1):len(enc1)], enc2...))
603 })
604 })
605 }
606 }
607
608
609
610 func concat3(gen1, gen2, gen3 func(func([]byte))) func(func([]byte)) {
611 return func(try func([]byte)) {
612 gen1(func(enc1 []byte) {
613 gen2(func(enc2 []byte) {
614 gen3(func(enc3 []byte) {
615 try(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...))
616 })
617 })
618 })
619 }
620 }
621
622
623
624 func concat4(gen1, gen2, gen3, gen4 func(func([]byte))) func(func([]byte)) {
625 return func(try func([]byte)) {
626 gen1(func(enc1 []byte) {
627 gen2(func(enc2 []byte) {
628 gen3(func(enc3 []byte) {
629 gen4(func(enc4 []byte) {
630 try(append(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...), enc4...))
631 })
632 })
633 })
634 })
635 }
636 }
637
638
639 func filter(gen func(func([]byte)), ok func([]byte) bool) func(func([]byte)) {
640 return func(try func([]byte)) {
641 gen(func(enc []byte) {
642 if ok(enc) {
643 try(enc)
644 }
645 })
646 }
647 }
648
649
650 func enum8bit(try func([]byte)) {
651 for i := 0; i < 1<<8; i++ {
652 try([]byte{byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
653 }
654 }
655
656
657 func enum16bit(try func([]byte)) {
658 for i := 0; i < 1<<16; i++ {
659 try([]byte{byte(i), byte(i >> 8), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
660 }
661 }
662
663
664 func enum24bit(try func([]byte)) {
665 for i := 0; i < 1<<24; i++ {
666 try([]byte{byte(i), byte(i >> 8), byte(i >> 16), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
667 }
668 }
669
670
671
672 func enumModRM(try func([]byte)) {
673 for i := 0; i < 256; i++ {
674 if (i>>3)&07 == 04 && i>>6 != 3 {
675 for j := 0; j < 256; j++ {
676 try([]byte{0, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
677 try([]byte{1, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
678 }
679 } else {
680 try([]byte{0, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
681 try([]byte{1, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
682 }
683 }
684 }
685
686
687
688 func fixed(b ...byte) func(func([]byte)) {
689 return func(try func([]byte)) {
690 try(b)
691 }
692 }
693
694
695
696
697
698
699
700
701
702
703
704
705
706 func testBasic(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) {
707 testfn(t, concat3(basicPrefixes, fixed(opcode...), enum8bit))
708 if testing.Short() {
709 return
710 }
711
712 t.Parallel()
713 testfn(t, concat3(basicPrefixes, fixed(opcode...), enum16bit))
714 if !*longTest {
715 return
716 }
717
718 name := caller(2)
719 op1 := make([]byte, len(opcode)+1)
720 copy(op1, opcode)
721 for i := 0; i < 256; i++ {
722 log.Printf("%s 24-bit: %d/256\n", name, i)
723 op1[len(opcode)] = byte(i)
724 testfn(t, concat(fixed(op1...), enum16bit))
725 }
726 }
727
728 func testBasicREX(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) {
729 testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum8bit), isValidREX))
730 if testing.Short() {
731 return
732 }
733
734 t.Parallel()
735 testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum16bit), isValidREX))
736 if !*longTest {
737 return
738 }
739
740 name := caller(2)
741 op1 := make([]byte, len(opcode)+1)
742 copy(op1, opcode)
743 for i := 0; i < 256; i++ {
744 log.Printf("%s 24-bit: %d/256\n", name, i)
745 op1[len(opcode)] = byte(i)
746 testfn(t, filter(concat3(rexPrefixes, fixed(op1...), enum16bit), isValidREX))
747 }
748 }
749
750
751
752
753
754
755 func testPrefix(t *testing.T, testfn func(*testing.T, func(func([]byte)))) {
756 t.Parallel()
757 testfn(t, concat(manyPrefixes, enum8bit))
758 if testing.Short() || !*longTest {
759 return
760 }
761
762 name := caller(2)
763 for i := 0; i < 256; i++ {
764 log.Printf("%s 16-bit: %d/256\n", name, i)
765 testfn(t, concat3(manyPrefixes, fixed(byte(i)), enum8bit))
766 }
767 }
768
769 func testPrefixREX(t *testing.T, testfn func(*testing.T, func(func([]byte)))) {
770 t.Parallel()
771 testfn(t, filter(concat3(manyPrefixes, rexPrefixes, enum8bit), isValidREX))
772 if testing.Short() || !*longTest {
773 return
774 }
775
776 name := caller(2)
777 for i := 0; i < 256; i++ {
778 log.Printf("%s 16-bit: %d/256\n", name, i)
779 testfn(t, filter(concat4(manyPrefixes, rexPrefixes, fixed(byte(i)), enum8bit), isValidREX))
780 }
781 }
782
783 func caller(skip int) string {
784 pc, _, _, _ := runtime.Caller(skip)
785 f := runtime.FuncForPC(pc)
786 name := "?"
787 if f != nil {
788 name = f.Name()
789 if i := strings.LastIndex(name, "."); i >= 0 {
790 name = name[i+1:]
791 }
792 }
793 return name
794 }
795
796 func isValidREX(x []byte) bool {
797 i := 0
798 for i < len(x) && isPrefixByte(x[i]) {
799 i++
800 }
801 if i < len(x) && Prefix(x[i]).IsREX() {
802 i++
803 if i < len(x) {
804 return !isPrefixByte(x[i]) && !Prefix(x[i]).IsREX()
805 }
806 }
807 return true
808 }
809
810 func isPrefixByte(b byte) bool {
811 switch b {
812 case 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65, 0x66, 0x67, 0xF0, 0xF2, 0xF3:
813 return true
814 }
815 return false
816 }
817
View as plain text