diff options
Diffstat (limited to 'src/cmd/vendor/golang.org/x/arch/x86/x86asm/ext_test.go')
-rw-r--r-- | src/cmd/vendor/golang.org/x/arch/x86/x86asm/ext_test.go | 811 |
1 files changed, 0 insertions, 811 deletions
diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/ext_test.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/ext_test.go deleted file mode 100644 index 526ef5aa26..0000000000 --- a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/ext_test.go +++ /dev/null @@ -1,811 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Support for testing against external disassembler program. - -package x86asm - -import ( - "bufio" - "bytes" - "encoding/hex" - "flag" - "fmt" - "io" - "io/ioutil" - "log" - "math/rand" - "os" - "os/exec" - "regexp" - "runtime" - "strings" - "testing" - "time" -) - -var ( - printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths") - dumpTest = flag.Bool("dump", false, "dump all encodings") - mismatch = flag.Bool("mismatch", false, "log allowed mismatches") - longTest = flag.Bool("long", false, "long test") - keep = flag.Bool("keep", false, "keep object files around") - debug = false -) - -// An ExtInst represents a single decoded instruction parsed -// from an external disassembler's output. -type ExtInst struct { - addr uint32 - enc [32]byte - nenc int - text string -} - -func (r ExtInst) String() string { - return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text) -} - -// An ExtDis is a connection between an external disassembler and a test. -type ExtDis struct { - Arch int - Dec chan ExtInst - File *os.File - Size int - KeepFile bool - Cmd *exec.Cmd -} - -// Run runs the given command - the external disassembler - and returns -// a buffered reader of its standard output. -func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) { - if *keep { - log.Printf("%s\n", strings.Join(cmd, " ")) - } - ext.Cmd = exec.Command(cmd[0], cmd[1:]...) - out, err := ext.Cmd.StdoutPipe() - if err != nil { - return nil, fmt.Errorf("stdoutpipe: %v", err) - } - if err := ext.Cmd.Start(); err != nil { - return nil, fmt.Errorf("exec: %v", err) - } - - b := bufio.NewReaderSize(out, 1<<20) - return b, nil -} - -// Wait waits for the command started with Run to exit. -func (ext *ExtDis) Wait() error { - return ext.Cmd.Wait() -} - -// testExtDis tests a set of byte sequences against an external disassembler. -// The disassembler is expected to produce the given syntax and be run -// in the given architecture mode (16, 32, or 64-bit). -// The extdis function must start the external disassembler -// and then parse its output, sending the parsed instructions on ext.Dec. -// The generate function calls its argument f once for each byte sequence -// to be tested. The generate function itself will be called twice, and it must -// make the same sequence of calls to f each time. -// When a disassembly does not match the internal decoding, -// allowedMismatch determines whether this mismatch should be -// allowed, or else considered an error. -func testExtDis( - t *testing.T, - syntax string, - arch int, - extdis func(ext *ExtDis) error, - generate func(f func([]byte)), - allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool, -) { - start := time.Now() - ext := &ExtDis{ - Dec: make(chan ExtInst), - Arch: arch, - } - errc := make(chan error) - - // First pass: write instructions to input file for external disassembler. - file, f, size, err := writeInst(generate) - if err != nil { - t.Fatal(err) - } - ext.Size = size - ext.File = f - defer func() { - f.Close() - if !*keep { - os.Remove(file) - } - }() - - // Second pass: compare disassembly against our decodings. - var ( - totalTests = 0 - totalSkips = 0 - totalErrors = 0 - - errors = make([]string, 0, 100) // sampled errors, at most cap - ) - go func() { - errc <- extdis(ext) - }() - generate(func(enc []byte) { - dec, ok := <-ext.Dec - if !ok { - t.Errorf("decoding stream ended early") - return - } - inst, text := disasm(syntax, arch, pad(enc)) - totalTests++ - if *dumpTest { - fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc) - } - if text != dec.text || inst.Len != dec.nenc { - suffix := "" - if allowedMismatch(text, size, &inst, dec) { - totalSkips++ - if !*mismatch { - return - } - suffix += " (allowed mismatch)" - } - totalErrors++ - if len(errors) >= cap(errors) { - j := rand.Intn(totalErrors) - if j >= cap(errors) { - return - } - errors = append(errors[:j], errors[j+1:]...) - } - errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix)) - } - }) - - if *mismatch { - totalErrors -= totalSkips - } - - for _, b := range errors { - t.Log(b) - } - - if totalErrors > 0 { - t.Fail() - } - t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds()) - - if err := <-errc; err != nil { - t.Fatalf("external disassembler: %v", err) - } -} - -const start = 0x8000 // start address of text - -// writeInst writes the generated byte sequences to a new file -// starting at offset start. That file is intended to be the input to -// the external disassembler. -func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) { - f, err = ioutil.TempFile("", "x86map") - if err != nil { - return - } - - file = f.Name() - - f.Seek(start, io.SeekStart) - w := bufio.NewWriter(f) - defer w.Flush() - size = 0 - generate(func(x []byte) { - if len(x) > 16 { - x = x[:16] - } - if debug { - fmt.Printf("%#x: %x%x\n", start+size, x, pops[len(x):]) - } - w.Write(x) - w.Write(pops[len(x):]) - size += len(pops) - }) - return file, f, size, nil -} - -// 0x5F is a single-byte pop instruction. -// We pad the bytes we want decoded with enough 0x5Fs -// that no matter what state the instruction stream is in -// after reading our bytes, the pops will get us back to -// a forced instruction boundary. -var pops = []byte{ - 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, - 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, - 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, - 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, -} - -// pad pads the code sequence with pops. -func pad(enc []byte) []byte { - return append(enc[:len(enc):len(enc)], pops...) -} - -// disasm returns the decoded instruction and text -// for the given source bytes, using the given syntax and mode. -func disasm(syntax string, mode int, src []byte) (inst Inst, text string) { - // If printTests is set, we record the coverage value - // before and after, and we write out the inputs for which - // coverage went up, in the format expected in testdata/decode.text. - // This produces a fairly small set of test cases that exercise nearly - // all the code. - var cover float64 - if *printTests { - cover -= coverage() - } - - inst, err := decode1(src, mode, syntax == "gnu") - if err != nil { - text = "error: " + err.Error() - } else { - switch syntax { - case "gnu": - text = GNUSyntax(inst, 0, nil) - case "intel": - text = IntelSyntax(inst, 0, nil) - case "plan9": // [sic] - text = GoSyntax(inst, 0, nil) - default: - text = "error: unknown syntax " + syntax - } - } - - if *printTests { - cover += coverage() - if cover > 0 { - max := len(src) - if max > 16 && inst.Len <= 16 { - max = 16 - } - fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], mode, syntax, text) - } - } - - return -} - -// coverage returns a floating point number denoting the -// test coverage until now. The number increases when new code paths are exercised, -// both in the Go program and in the decoder byte code. -func coverage() float64 { - /* - testing.Coverage is not in the main distribution. - The implementation, which must go in package testing, is: - - // Coverage reports the current code coverage as a fraction in the range [0, 1]. - func Coverage() float64 { - var n, d int64 - for _, counters := range cover.Counters { - for _, c := range counters { - if c > 0 { - n++ - } - d++ - } - } - if d == 0 { - return 0 - } - return float64(n) / float64(d) - } - */ - - var f float64 - // f += testing.Coverage() - f += decodeCoverage() - return f -} - -func decodeCoverage() float64 { - n := 0 - for _, t := range decoderCover { - if t { - n++ - } - } - return float64(1+n) / float64(1+len(decoderCover)) -} - -// Helpers for writing disassembler output parsers. - -// isPrefix reports whether text is the name of an instruction prefix. -func isPrefix(text string) bool { - return prefixByte[text] > 0 -} - -// prefixByte maps instruction prefix text to actual prefix byte values. -var prefixByte = map[string]byte{ - "es": 0x26, - "cs": 0x2e, - "ss": 0x36, - "ds": 0x3e, - "fs": 0x64, - "gs": 0x65, - "data16": 0x66, - "addr16": 0x67, - "lock": 0xf0, - "repn": 0xf2, - "repne": 0xf2, - "rep": 0xf3, - "repe": 0xf3, - "xacquire": 0xf2, - "xrelease": 0xf3, - "bnd": 0xf2, - "addr32": 0x66, - "data32": 0x67, -} - -// hasPrefix reports whether any of the space-separated words in the text s -// begins with any of the given prefixes. -func hasPrefix(s string, prefixes ...string) bool { - for _, prefix := range prefixes { - for s := s; s != ""; { - if strings.HasPrefix(s, prefix) { - return true - } - i := strings.Index(s, " ") - if i < 0 { - break - } - s = s[i+1:] - } - } - return false -} - -// contains reports whether the text s contains any of the given substrings. -func contains(s string, substrings ...string) bool { - for _, sub := range substrings { - if strings.Contains(s, sub) { - return true - } - } - return false -} - -// isHex reports whether b is a hexadecimal character (0-9A-Fa-f). -func isHex(b byte) bool { return b == '0' || unhex[b] > 0 } - -// parseHex parses the hexadecimal byte dump in hex, -// appending the parsed bytes to raw and returning the updated slice. -// The returned bool signals whether any invalid hex was found. -// Spaces and tabs between bytes are okay but any other non-hex is not. -func parseHex(hex []byte, raw []byte) ([]byte, bool) { - hex = trimSpace(hex) - for j := 0; j < len(hex); { - for hex[j] == ' ' || hex[j] == '\t' { - j++ - } - if j >= len(hex) { - break - } - if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) { - return nil, false - } - raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]]) - j += 2 - } - return raw, true -} - -var unhex = [256]byte{ - '0': 0, - '1': 1, - '2': 2, - '3': 3, - '4': 4, - '5': 5, - '6': 6, - '7': 7, - '8': 8, - '9': 9, - 'A': 10, - 'B': 11, - 'C': 12, - 'D': 13, - 'E': 14, - 'F': 15, - 'a': 10, - 'b': 11, - 'c': 12, - 'd': 13, - 'e': 14, - 'f': 15, -} - -// index is like bytes.Index(s, []byte(t)) but avoids the allocation. -func index(s []byte, t string) int { - i := 0 - for { - j := bytes.IndexByte(s[i:], t[0]) - if j < 0 { - return -1 - } - i = i + j - if i+len(t) > len(s) { - return -1 - } - for k := 1; k < len(t); k++ { - if s[i+k] != t[k] { - goto nomatch - } - } - return i - nomatch: - i++ - } -} - -// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s. -// If s must be rewritten, it is rewritten in place. -func fixSpace(s []byte) []byte { - s = trimSpace(s) - for i := 0; i < len(s); i++ { - if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' { - goto Fix - } - } - return s - -Fix: - b := s - w := 0 - for i := 0; i < len(s); i++ { - c := s[i] - if c == '\t' || c == '\n' { - c = ' ' - } - if c == ' ' && w > 0 && b[w-1] == ' ' { - continue - } - b[w] = c - w++ - } - if w > 0 && b[w-1] == ' ' { - w-- - } - return b[:w] -} - -// trimSpace trims leading and trailing space from s, returning a subslice of s. -func trimSpace(s []byte) []byte { - j := len(s) - for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') { - j-- - } - i := 0 - for i < j && (s[i] == ' ' || s[i] == '\t') { - i++ - } - return s[i:j] -} - -// pcrel and pcrelw match instructions using relative addressing mode. -var ( - pcrel = regexp.MustCompile(`^((?:.* )?(?:j[a-z]+|call|ljmp|loopn?e?w?|xbegin)q?(?:,p[nt])?) 0x([0-9a-f]+)$`) - pcrelw = regexp.MustCompile(`^((?:.* )?(?:callw|jmpw|xbeginw|ljmpw)(?:,p[nt])?) 0x([0-9a-f]+)$`) -) - -// Generators. -// -// The test cases are described as functions that invoke a callback repeatedly, -// with a new input sequence each time. These helpers make writing those -// a little easier. - -// hexCases generates the cases written in hexadecimal in the encoded string. -// Spaces in 'encoded' separate entire test cases, not individual bytes. -func hexCases(t *testing.T, encoded string) func(func([]byte)) { - return func(try func([]byte)) { - for _, x := range strings.Fields(encoded) { - src, err := hex.DecodeString(x) - if err != nil { - t.Errorf("parsing %q: %v", x, err) - } - try(src) - } - } -} - -// testdataCases generates the test cases recorded in testdata/decode.txt. -// It only uses the inputs; it ignores the answers recorded in that file. -func testdataCases(t *testing.T) func(func([]byte)) { - var codes [][]byte - data, err := ioutil.ReadFile("testdata/decode.txt") - if err != nil { - t.Fatal(err) - } - for _, line := range strings.Split(string(data), "\n") { - line = strings.TrimSpace(line) - if line == "" || strings.HasPrefix(line, "#") { - continue - } - f := strings.Fields(line)[0] - i := strings.Index(f, "|") - if i < 0 { - t.Errorf("parsing %q: missing | separator", f) - continue - } - if i%2 != 0 { - t.Errorf("parsing %q: misaligned | separator", f) - } - code, err := hex.DecodeString(f[:i] + f[i+1:]) - if err != nil { - t.Errorf("parsing %q: %v", f, err) - continue - } - codes = append(codes, code) - } - - return func(try func([]byte)) { - for _, code := range codes { - try(code) - } - } -} - -// manyPrefixes generates all possible 2⁹ combinations of nine chosen prefixes. -// The relative ordering of the prefixes within the combinations varies deterministically. -func manyPrefixes(try func([]byte)) { - var prefixBytes = []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36, 0x66, 0x67} - var enc []byte - for i := 0; i < 1<<uint(len(prefixBytes)); i++ { - enc = enc[:0] - for j, p := range prefixBytes { - if i&(1<<uint(j)) != 0 { - enc = append(enc, p) - } - } - if len(enc) > 0 { - k := i % len(enc) - enc[0], enc[k] = enc[k], enc[0] - } - try(enc) - } -} - -// basicPrefixes geneartes 8 different possible prefix cases: no prefix -// and then one each of seven different prefix bytes. -func basicPrefixes(try func([]byte)) { - try(nil) - for _, b := range []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36} { - try([]byte{b}) - } -} - -func rexPrefixes(try func([]byte)) { - try(nil) - for _, b := range []byte{0x40, 0x48, 0x43, 0x4C} { - try([]byte{b}) - } -} - -// concat takes two generators and returns a generator for the -// cross product of the two, concatenating the results from each. -func concat(gen1, gen2 func(func([]byte))) func(func([]byte)) { - return func(try func([]byte)) { - gen1(func(enc1 []byte) { - gen2(func(enc2 []byte) { - try(append(enc1[:len(enc1):len(enc1)], enc2...)) - }) - }) - } -} - -// concat3 takes three generators and returns a generator for the -// cross product of the three, concatenating the results from each. -func concat3(gen1, gen2, gen3 func(func([]byte))) func(func([]byte)) { - return func(try func([]byte)) { - gen1(func(enc1 []byte) { - gen2(func(enc2 []byte) { - gen3(func(enc3 []byte) { - try(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...)) - }) - }) - }) - } -} - -// concat4 takes four generators and returns a generator for the -// cross product of the four, concatenating the results from each. -func concat4(gen1, gen2, gen3, gen4 func(func([]byte))) func(func([]byte)) { - return func(try func([]byte)) { - gen1(func(enc1 []byte) { - gen2(func(enc2 []byte) { - gen3(func(enc3 []byte) { - gen4(func(enc4 []byte) { - try(append(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...), enc4...)) - }) - }) - }) - }) - } -} - -// filter generates the sequences from gen that satisfy ok. -func filter(gen func(func([]byte)), ok func([]byte) bool) func(func([]byte)) { - return func(try func([]byte)) { - gen(func(enc []byte) { - if ok(enc) { - try(enc) - } - }) - } -} - -// enum8bit generates all possible 1-byte sequences, followed by distinctive padding. -func enum8bit(try func([]byte)) { - for i := 0; i < 1<<8; i++ { - try([]byte{byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) - } -} - -// enum8bit generates all possible 2-byte sequences, followed by distinctive padding. -func enum16bit(try func([]byte)) { - for i := 0; i < 1<<16; i++ { - try([]byte{byte(i), byte(i >> 8), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) - } -} - -// enum24bit generates all possible 3-byte sequences, followed by distinctive padding. -func enum24bit(try func([]byte)) { - for i := 0; i < 1<<24; i++ { - try([]byte{byte(i), byte(i >> 8), byte(i >> 16), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) - } -} - -// enumModRM generates all possible modrm bytes and, for modrm values that indicate -// a following sib byte, all possible modrm, sib combinations. -func enumModRM(try func([]byte)) { - for i := 0; i < 256; i++ { - if (i>>3)&07 == 04 && i>>6 != 3 { // has sib - for j := 0; j < 256; j++ { - try([]byte{0, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings - try([]byte{1, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings - } - } else { - try([]byte{0, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings - try([]byte{1, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings - } - } -} - -// fixed generates the single case b. -// It's mainly useful to prepare an argument for concat or concat3. -func fixed(b ...byte) func(func([]byte)) { - return func(try func([]byte)) { - try(b) - } -} - -// testBasic runs the given test function with cases all using opcode as the initial opcode bytes. -// It runs three phases: -// -// First, zero-or-one prefixes followed by opcode followed by all possible 1-byte values. -// If in -short mode, that's all. -// -// Second, zero-or-one prefixes followed by opcode followed by all possible 2-byte values. -// If not in -long mode, that's all. This phase and the next run in parallel with other tests -// (using t.Parallel). -// -// Finally, opcode followed by all possible 3-byte values. The test can take a very long time -// and prints progress messages to package log. -func testBasic(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) { - testfn(t, concat3(basicPrefixes, fixed(opcode...), enum8bit)) - if testing.Short() { - return - } - - t.Parallel() - testfn(t, concat3(basicPrefixes, fixed(opcode...), enum16bit)) - if !*longTest { - return - } - - name := caller(2) - op1 := make([]byte, len(opcode)+1) - copy(op1, opcode) - for i := 0; i < 256; i++ { - log.Printf("%s 24-bit: %d/256\n", name, i) - op1[len(opcode)] = byte(i) - testfn(t, concat(fixed(op1...), enum16bit)) - } -} - -func testBasicREX(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) { - testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum8bit), isValidREX)) - if testing.Short() { - return - } - - t.Parallel() - testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum16bit), isValidREX)) - if !*longTest { - return - } - - name := caller(2) - op1 := make([]byte, len(opcode)+1) - copy(op1, opcode) - for i := 0; i < 256; i++ { - log.Printf("%s 24-bit: %d/256\n", name, i) - op1[len(opcode)] = byte(i) - testfn(t, filter(concat3(rexPrefixes, fixed(op1...), enum16bit), isValidREX)) - } -} - -// testPrefix runs the given test function for all many prefix possibilities -// followed by all possible 1-byte sequences. -// -// If in -long mode, it then runs a test of all the prefix possibilities followed -// by all possible 2-byte sequences. -func testPrefix(t *testing.T, testfn func(*testing.T, func(func([]byte)))) { - t.Parallel() - testfn(t, concat(manyPrefixes, enum8bit)) - if testing.Short() || !*longTest { - return - } - - name := caller(2) - for i := 0; i < 256; i++ { - log.Printf("%s 16-bit: %d/256\n", name, i) - testfn(t, concat3(manyPrefixes, fixed(byte(i)), enum8bit)) - } -} - -func testPrefixREX(t *testing.T, testfn func(*testing.T, func(func([]byte)))) { - t.Parallel() - testfn(t, filter(concat3(manyPrefixes, rexPrefixes, enum8bit), isValidREX)) - if testing.Short() || !*longTest { - return - } - - name := caller(2) - for i := 0; i < 256; i++ { - log.Printf("%s 16-bit: %d/256\n", name, i) - testfn(t, filter(concat4(manyPrefixes, rexPrefixes, fixed(byte(i)), enum8bit), isValidREX)) - } -} - -func caller(skip int) string { - pc, _, _, _ := runtime.Caller(skip) - f := runtime.FuncForPC(pc) - name := "?" - if f != nil { - name = f.Name() - if i := strings.LastIndex(name, "."); i >= 0 { - name = name[i+1:] - } - } - return name -} - -func isValidREX(x []byte) bool { - i := 0 - for i < len(x) && isPrefixByte(x[i]) { - i++ - } - if i < len(x) && Prefix(x[i]).IsREX() { - i++ - if i < len(x) { - return !isPrefixByte(x[i]) && !Prefix(x[i]).IsREX() - } - } - return true -} - -func isPrefixByte(b byte) bool { - switch b { - case 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65, 0x66, 0x67, 0xF0, 0xF2, 0xF3: - return true - } - return false -} |