LEFT | RIGHT |
(no file at all) | |
| 1 // Copyright 2014 The Go Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style |
| 3 // license that can be found in the LICENSE file. |
| 4 |
| 5 // Support for testing against external disassembler program. |
| 6 |
| 7 package x86asm |
| 8 |
| 9 import ( |
| 10 "bufio" |
| 11 "bytes" |
| 12 "encoding/hex" |
| 13 "flag" |
| 14 "fmt" |
| 15 "io/ioutil" |
| 16 "log" |
| 17 "math/rand" |
| 18 "os" |
| 19 "os/exec" |
| 20 "regexp" |
| 21 "runtime" |
| 22 "strings" |
| 23 "testing" |
| 24 "time" |
| 25 ) |
| 26 |
| 27 var ( |
| 28 printTests = flag.Bool("printtests", false, "print test cases that exerc
ise new code paths") |
| 29 dumpTest = flag.Bool("dump", false, "dump all encodings") |
| 30 mismatch = flag.Bool("mismatch", false, "log allowed mismatches") |
| 31 longTest = flag.Bool("long", false, "long test") |
| 32 keep = flag.Bool("keep", false, "keep object files around") |
| 33 debug = false |
| 34 ) |
| 35 |
| 36 // A ExtInst represents a single decoded instruction parsed |
| 37 // from an external disassembler's output. |
| 38 type ExtInst struct { |
| 39 addr uint32 |
| 40 enc [32]byte |
| 41 nenc int |
| 42 text string |
| 43 } |
| 44 |
| 45 func (r ExtInst) String() string { |
| 46 return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text) |
| 47 } |
| 48 |
| 49 // An ExtDis is a connection between an external disassembler and a test. |
| 50 type ExtDis struct { |
| 51 Arch int |
| 52 Dec chan ExtInst |
| 53 File *os.File |
| 54 Size int |
| 55 KeepFile bool |
| 56 Cmd *exec.Cmd |
| 57 } |
| 58 |
| 59 // Run runs the given command - the external disassembler - and returns |
| 60 // a buffered reader of its standard output. |
| 61 func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) { |
| 62 if *keep { |
| 63 log.Printf("%s\n", strings.Join(cmd, " ")) |
| 64 } |
| 65 ext.Cmd = exec.Command(cmd[0], cmd[1:]...) |
| 66 out, err := ext.Cmd.StdoutPipe() |
| 67 if err != nil { |
| 68 return nil, fmt.Errorf("stdoutpipe: %v", err) |
| 69 } |
| 70 if err := ext.Cmd.Start(); err != nil { |
| 71 return nil, fmt.Errorf("exec: %v", err) |
| 72 } |
| 73 |
| 74 b := bufio.NewReaderSize(out, 1<<20) |
| 75 return b, nil |
| 76 } |
| 77 |
| 78 // Wait waits for the command started with Run to exit. |
| 79 func (ext *ExtDis) Wait() error { |
| 80 return ext.Cmd.Wait() |
| 81 } |
| 82 |
| 83 // testExtDis tests a set of byte sequences against an external disassembler. |
| 84 // The disassembler is expected to produce the given syntax and be run |
| 85 // in the given architecture mode (16, 32, or 64-bit). |
| 86 // The extdis function must start the external disassembler |
| 87 // and then parse its output, sending the parsed instructions on ext.Dec. |
| 88 // The generate function calls its argument f once for each byte sequence |
| 89 // to be tested. The generate function itself will be called twice, and it must |
| 90 // make the same sequence of calls to f each time. |
| 91 // When a disassembly does not match the internal decoding, |
| 92 // allowedMismatch determines whether this mismatch should be |
| 93 // allowed, or else considered an error. |
| 94 func testExtDis( |
| 95 t *testing.T, |
| 96 syntax string, |
| 97 arch int, |
| 98 extdis func(ext *ExtDis) error, |
| 99 generate func(f func([]byte)), |
| 100 allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) boo
l, |
| 101 ) { |
| 102 start := time.Now() |
| 103 ext := &ExtDis{ |
| 104 Dec: make(chan ExtInst), |
| 105 Arch: arch, |
| 106 } |
| 107 errc := make(chan error) |
| 108 |
| 109 // First pass: write instructions to input file for external disassemble
r. |
| 110 file, f, size, err := writeInst(generate) |
| 111 if err != nil { |
| 112 t.Fatal(err) |
| 113 } |
| 114 ext.Size = size |
| 115 ext.File = f |
| 116 defer func() { |
| 117 f.Close() |
| 118 if !*keep { |
| 119 os.Remove(file) |
| 120 } |
| 121 }() |
| 122 |
| 123 // Second pass: compare disassembly against our decodings. |
| 124 var ( |
| 125 totalTests = 0 |
| 126 totalSkips = 0 |
| 127 totalErrors = 0 |
| 128 |
| 129 errors = make([]string, 0, 100) // sampled errors, at most cap |
| 130 ) |
| 131 go func() { |
| 132 errc <- extdis(ext) |
| 133 }() |
| 134 generate(func(enc []byte) { |
| 135 dec, ok := <-ext.Dec |
| 136 if !ok { |
| 137 t.Errorf("decoding stream ended early") |
| 138 return |
| 139 } |
| 140 inst, text := disasm(syntax, arch, pad(enc)) |
| 141 totalTests++ |
| 142 if *dumpTest { |
| 143 fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text,
dec.nenc) |
| 144 } |
| 145 if text != dec.text || inst.Len != dec.nenc { |
| 146 suffix := "" |
| 147 if allowedMismatch(text, size, &inst, dec) { |
| 148 totalSkips++ |
| 149 if !*mismatch { |
| 150 return |
| 151 } |
| 152 suffix += " (allowed mismatch)" |
| 153 } |
| 154 totalErrors++ |
| 155 if len(errors) >= cap(errors) { |
| 156 j := rand.Intn(totalErrors) |
| 157 if j >= cap(errors) { |
| 158 return |
| 159 } |
| 160 errors = append(errors[:j], errors[j+1:]...) |
| 161 } |
| 162 errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d
, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix)) |
| 163 } |
| 164 }) |
| 165 |
| 166 if *mismatch { |
| 167 totalErrors -= totalSkips |
| 168 } |
| 169 |
| 170 for _, b := range errors { |
| 171 t.Log(b) |
| 172 } |
| 173 |
| 174 if totalErrors > 0 { |
| 175 t.Fail() |
| 176 } |
| 177 t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/s
econd", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(star
t).Seconds()) |
| 178 |
| 179 if err := <-errc; err != nil { |
| 180 t.Fatal("external disassembler: %v", err) |
| 181 } |
| 182 |
| 183 } |
| 184 |
| 185 const start = 0x8000 // start address of text |
| 186 |
| 187 // writeInst writes the generated byte sequences to a new file |
| 188 // starting at offset start. That file is intended to be the input to |
| 189 // the external disassembler. |
| 190 func writeInst(generate func(func([]byte))) (file string, f *os.File, size int,
err error) { |
| 191 f, err = ioutil.TempFile("", "x86map") |
| 192 if err != nil { |
| 193 return |
| 194 } |
| 195 |
| 196 file = f.Name() |
| 197 |
| 198 f.Seek(start, 0) |
| 199 w := bufio.NewWriter(f) |
| 200 defer w.Flush() |
| 201 size = 0 |
| 202 generate(func(x []byte) { |
| 203 if len(x) > 16 { |
| 204 x = x[:16] |
| 205 } |
| 206 if debug { |
| 207 fmt.Printf("%#x: %x%x\n", start+size, x, pops[len(x):]) |
| 208 } |
| 209 w.Write(x) |
| 210 w.Write(pops[len(x):]) |
| 211 size += len(pops) |
| 212 }) |
| 213 return file, f, size, nil |
| 214 } |
| 215 |
| 216 // 0x5F is a single-byte pop instruction. |
| 217 // We pad the bytes we want decoded with enough 0x5Fs |
| 218 // that no matter what state the instruction stream is in |
| 219 // after reading our bytes, the pops will get us back to |
| 220 // a forced instruction boundary. |
| 221 var pops = []byte{ |
| 222 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, |
| 223 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, |
| 224 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, |
| 225 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, |
| 226 } |
| 227 |
| 228 // pad pads the code sequenc with pops. |
| 229 func pad(enc []byte) []byte { |
| 230 return append(enc[:len(enc):len(enc)], pops...) |
| 231 } |
| 232 |
| 233 // disasm returns the decoded instruction and text |
| 234 // for the given source bytes, using the given syntax and mode. |
| 235 func disasm(syntax string, mode int, src []byte) (inst Inst, text string) { |
| 236 // If printTests is set, we record the coverage value |
| 237 // before and after, and we write out the inputs for which |
| 238 // coverage went up, in the format expected in testdata/decode.text. |
| 239 // This produces a fairly small set of test cases that exercise nearly |
| 240 // all the code. |
| 241 var cover float64 |
| 242 if *printTests { |
| 243 cover -= coverage() |
| 244 } |
| 245 |
| 246 inst, err := decode1(src, mode, syntax == "gnu") |
| 247 if err != nil { |
| 248 text = "error: " + err.Error() |
| 249 } else { |
| 250 switch syntax { |
| 251 case "gnu": |
| 252 text = GNUSyntax(inst) |
| 253 case "intel": |
| 254 text = IntelSyntax(inst) |
| 255 case "plan9": |
| 256 text = Plan9Syntax(inst, 0, nil) |
| 257 default: |
| 258 text = "error: unknown syntax " + syntax |
| 259 } |
| 260 } |
| 261 |
| 262 if *printTests { |
| 263 cover += coverage() |
| 264 if cover > 0 { |
| 265 max := len(src) |
| 266 if max > 16 && inst.Len <= 16 { |
| 267 max = 16 |
| 268 } |
| 269 fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[in
st.Len:max], mode, syntax, text) |
| 270 } |
| 271 } |
| 272 |
| 273 return |
| 274 } |
| 275 |
| 276 // coverage returns a floating point number denoting the |
| 277 // test coverage until now. The number increases when new code paths are exercis
ed, |
| 278 // both in the Go program and in the decoder byte code. |
| 279 func coverage() float64 { |
| 280 /* |
| 281 testing.Coverage is not in the main distribution. |
| 282 The implementation, which must go in package testing, is: |
| 283 |
| 284 // Coverage reports the current code coverage as a fraction in t
he range [0, 1]. |
| 285 func Coverage() float64 { |
| 286 var n, d int64 |
| 287 for _, counters := range cover.Counters { |
| 288 for _, c := range counters { |
| 289 if c > 0 { |
| 290 n++ |
| 291 } |
| 292 d++ |
| 293 } |
| 294 } |
| 295 if d == 0 { |
| 296 return 0 |
| 297 } |
| 298 return float64(n) / float64(d) |
| 299 } |
| 300 */ |
| 301 |
| 302 var f float64 |
| 303 // f += testing.Coverage() |
| 304 f += decodeCoverage() |
| 305 return f |
| 306 } |
| 307 |
| 308 func decodeCoverage() float64 { |
| 309 n := 0 |
| 310 for _, t := range decoderCover { |
| 311 if t { |
| 312 n++ |
| 313 } |
| 314 } |
| 315 return float64(1+n) / float64(1+len(decoderCover)) |
| 316 } |
| 317 |
| 318 // Helpers for writing disassembler output parsers. |
| 319 |
| 320 // isPrefix reports whether text is the name of an instruction prefix. |
| 321 func isPrefix(text string) bool { |
| 322 return prefixByte[text] > 0 |
| 323 } |
| 324 |
| 325 // prefixByte maps instruction prefix text to actual prefix byte values. |
| 326 var prefixByte = map[string]byte{ |
| 327 "es": 0x26, |
| 328 "cs": 0x2e, |
| 329 "ss": 0x36, |
| 330 "ds": 0x3e, |
| 331 "fs": 0x64, |
| 332 "gs": 0x65, |
| 333 "data16": 0x66, |
| 334 "addr16": 0x67, |
| 335 "lock": 0xf0, |
| 336 "repn": 0xf2, |
| 337 "repne": 0xf2, |
| 338 "rep": 0xf3, |
| 339 "repe": 0xf3, |
| 340 "xacquire": 0xf2, |
| 341 "xrelease": 0xf3, |
| 342 "bnd": 0xf2, |
| 343 "addr32": 0x66, |
| 344 "data32": 0x67, |
| 345 } |
| 346 |
| 347 // hasPrefix reports whether any of the space-separated words in the text s |
| 348 // begins with any of the given prefixes. |
| 349 func hasPrefix(s string, prefixes ...string) bool { |
| 350 for _, prefix := range prefixes { |
| 351 for s := s; s != ""; { |
| 352 if strings.HasPrefix(s, prefix) { |
| 353 return true |
| 354 } |
| 355 i := strings.Index(s, " ") |
| 356 if i < 0 { |
| 357 break |
| 358 } |
| 359 s = s[i+1:] |
| 360 } |
| 361 } |
| 362 return false |
| 363 } |
| 364 |
| 365 // contains reports whether the text s contains any of the given substrings. |
| 366 func contains(s string, substrings ...string) bool { |
| 367 for _, sub := range substrings { |
| 368 if strings.Contains(s, sub) { |
| 369 return true |
| 370 } |
| 371 } |
| 372 return false |
| 373 } |
| 374 |
| 375 // isHex reports whether b is a hexadecimal character (0-9A-Fa-f). |
| 376 func isHex(b byte) bool { return b == '0' || unhex[b] > 0 } |
| 377 |
| 378 // parseHex parses the hexadecimal byte dump in hex, |
| 379 // appending the parsed bytes to raw and returning the updated slice. |
| 380 // The returned bool signals whether any invalid hex was found. |
| 381 // Spaces and tabs between bytes are okay but any other non-hex is not. |
| 382 func parseHex(hex []byte, raw []byte) ([]byte, bool) { |
| 383 hex = trimSpace(hex) |
| 384 for j := 0; j < len(hex); { |
| 385 for hex[j] == ' ' || hex[j] == '\t' { |
| 386 j++ |
| 387 } |
| 388 if j >= len(hex) { |
| 389 break |
| 390 } |
| 391 if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) { |
| 392 return nil, false |
| 393 } |
| 394 raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]]) |
| 395 j += 2 |
| 396 } |
| 397 return raw, true |
| 398 } |
| 399 |
| 400 var unhex = [256]byte{ |
| 401 '0': 0, |
| 402 '1': 1, |
| 403 '2': 2, |
| 404 '3': 3, |
| 405 '4': 4, |
| 406 '5': 5, |
| 407 '6': 6, |
| 408 '7': 7, |
| 409 '8': 8, |
| 410 '9': 9, |
| 411 'A': 10, |
| 412 'B': 11, |
| 413 'C': 12, |
| 414 'D': 13, |
| 415 'E': 14, |
| 416 'F': 15, |
| 417 'a': 10, |
| 418 'b': 11, |
| 419 'c': 12, |
| 420 'd': 13, |
| 421 'e': 14, |
| 422 'f': 15, |
| 423 } |
| 424 |
| 425 // index is like bytes.Index(s, []byte(t)) but avoids the allocation. |
| 426 func index(s []byte, t string) int { |
| 427 i := 0 |
| 428 for { |
| 429 j := bytes.IndexByte(s[i:], t[0]) |
| 430 if j < 0 { |
| 431 return -1 |
| 432 } |
| 433 i = i + j |
| 434 if i+len(t) > len(s) { |
| 435 return -1 |
| 436 } |
| 437 for k := 1; k < len(t); k++ { |
| 438 if s[i+k] != t[k] { |
| 439 goto nomatch |
| 440 } |
| 441 } |
| 442 return i |
| 443 nomatch: |
| 444 i++ |
| 445 } |
| 446 } |
| 447 |
| 448 // fixSpace rewrites runs of spaces, tabs, and newline characters into single sp
aces in s. |
| 449 // If s must be rewritten, it is rewritten in place. |
| 450 func fixSpace(s []byte) []byte { |
| 451 s = trimSpace(s) |
| 452 for i := 0; i < len(s); i++ { |
| 453 if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1
] == ' ' { |
| 454 goto Fix |
| 455 } |
| 456 } |
| 457 return s |
| 458 |
| 459 Fix: |
| 460 b := s |
| 461 w := 0 |
| 462 for i := 0; i < len(s); i++ { |
| 463 c := s[i] |
| 464 if c == '\t' || c == '\n' { |
| 465 c = ' ' |
| 466 } |
| 467 if c == ' ' && w > 0 && b[w-1] == ' ' { |
| 468 continue |
| 469 } |
| 470 b[w] = c |
| 471 w++ |
| 472 } |
| 473 if w > 0 && b[w-1] == ' ' { |
| 474 w-- |
| 475 } |
| 476 return b[:w] |
| 477 } |
| 478 |
| 479 // trimSpace trims leading and trailing space from s, returning a subslice of s. |
| 480 func trimSpace(s []byte) []byte { |
| 481 j := len(s) |
| 482 for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') { |
| 483 j-- |
| 484 } |
| 485 i := 0 |
| 486 for i < j && (s[i] == ' ' || s[i] == '\t') { |
| 487 i++ |
| 488 } |
| 489 return s[i:j] |
| 490 } |
| 491 |
| 492 // pcrel and pcrelw match instructions using relative addressing mode. |
| 493 var ( |
| 494 pcrel = regexp.MustCompile(`^((?:.* )?(?:j[a-z]+|call|ljmp|loopn?e?w?|x
begin)q?(?:,p[nt])?) 0x([0-9a-f]+)$`) |
| 495 pcrelw = regexp.MustCompile(`^((?:.* )?(?:callw|jmpw|xbeginw|ljmpw)(?:,p
[nt])?) 0x([0-9a-f]+)$`) |
| 496 ) |
| 497 |
| 498 // Generators. |
| 499 // |
| 500 // The test cases are described as functions that invoke a callback repeatedly, |
| 501 // with a new input sequence each time. These helpers make writing those |
| 502 // a little easier. |
| 503 |
| 504 // hexCases generates the cases written in hexadecimal in the encoded string. |
| 505 // Spaces in 'encoded' separate entire test cases, not individual bytes. |
| 506 func hexCases(t *testing.T, encoded string) func(func([]byte)) { |
| 507 return func(try func([]byte)) { |
| 508 for _, x := range strings.Fields(encoded) { |
| 509 src, err := hex.DecodeString(x) |
| 510 if err != nil { |
| 511 t.Errorf("parsing %q: %v", x, err) |
| 512 } |
| 513 try(src) |
| 514 } |
| 515 } |
| 516 } |
| 517 |
| 518 // testdataCases generates the test cases recorded in testdata/decode.txt. |
| 519 // It only uses the inputs; it ignores the answers recorded in that file. |
| 520 func testdataCases(t *testing.T) func(func([]byte)) { |
| 521 var codes [][]byte |
| 522 data, err := ioutil.ReadFile("testdata/decode.txt") |
| 523 if err != nil { |
| 524 t.Fatal(err) |
| 525 } |
| 526 for _, line := range strings.Split(string(data), "\n") { |
| 527 line = strings.TrimSpace(line) |
| 528 if line == "" || strings.HasPrefix(line, "#") { |
| 529 continue |
| 530 } |
| 531 f := strings.Fields(line)[0] |
| 532 i := strings.Index(f, "|") |
| 533 if i < 0 { |
| 534 t.Errorf("parsing %q: missing | separator", f) |
| 535 continue |
| 536 } |
| 537 if i%2 != 0 { |
| 538 t.Errorf("parsing %q: misaligned | separator", f) |
| 539 } |
| 540 code, err := hex.DecodeString(f[:i] + f[i+1:]) |
| 541 if err != nil { |
| 542 t.Errorf("parsing %q: %v", f, err) |
| 543 continue |
| 544 } |
| 545 codes = append(codes, code) |
| 546 } |
| 547 |
| 548 return func(try func([]byte)) { |
| 549 for _, code := range codes { |
| 550 try(code) |
| 551 } |
| 552 } |
| 553 } |
| 554 |
| 555 // manyPrefixes generates all possible 2⁹ combinations of nine chosen prefixes. |
| 556 // The relative ordering of the prefixes within the combinations varies determin
istically. |
| 557 func manyPrefixes(try func([]byte)) { |
| 558 var prefixBytes = []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36, 0x66,
0x67} |
| 559 var enc []byte |
| 560 for i := 0; i < 1<<uint(len(prefixBytes)); i++ { |
| 561 enc = enc[:0] |
| 562 for j, p := range prefixBytes { |
| 563 if i&(1<<uint(j)) != 0 { |
| 564 enc = append(enc, p) |
| 565 } |
| 566 } |
| 567 if len(enc) > 0 { |
| 568 k := i % len(enc) |
| 569 enc[0], enc[k] = enc[k], enc[0] |
| 570 } |
| 571 try(enc) |
| 572 } |
| 573 } |
| 574 |
| 575 // basicPrefixes geneartes 8 different possible prefix cases: no prefix |
| 576 // and then one each of seven different prefix bytes. |
| 577 func basicPrefixes(try func([]byte)) { |
| 578 try(nil) |
| 579 for _, b := range []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36} { |
| 580 try([]byte{b}) |
| 581 } |
| 582 } |
| 583 |
| 584 func rexPrefixes(try func([]byte)) { |
| 585 try(nil) |
| 586 for _, b := range []byte{0x40, 0x48, 0x43, 0x4C} { |
| 587 try([]byte{b}) |
| 588 } |
| 589 } |
| 590 |
| 591 // concat takes two generators and returns a generator for the |
| 592 // cross product of the two, concatenating the results from each. |
| 593 func concat(gen1, gen2 func(func([]byte))) func(func([]byte)) { |
| 594 return func(try func([]byte)) { |
| 595 gen1(func(enc1 []byte) { |
| 596 gen2(func(enc2 []byte) { |
| 597 try(append(enc1[:len(enc1):len(enc1)], enc2...)) |
| 598 }) |
| 599 }) |
| 600 } |
| 601 } |
| 602 |
| 603 // concat3 takes three generators and returns a generator for the |
| 604 // cross product of the three, concatenating the results from each. |
| 605 func concat3(gen1, gen2, gen3 func(func([]byte))) func(func([]byte)) { |
| 606 return func(try func([]byte)) { |
| 607 gen1(func(enc1 []byte) { |
| 608 gen2(func(enc2 []byte) { |
| 609 gen3(func(enc3 []byte) { |
| 610 try(append(append(enc1[:len(enc1):len(en
c1)], enc2...), enc3...)) |
| 611 }) |
| 612 }) |
| 613 }) |
| 614 } |
| 615 } |
| 616 |
| 617 // concat4 takes four generators and returns a generator for the |
| 618 // cross product of the four, concatenating the results from each. |
| 619 func concat4(gen1, gen2, gen3, gen4 func(func([]byte))) func(func([]byte)) { |
| 620 return func(try func([]byte)) { |
| 621 gen1(func(enc1 []byte) { |
| 622 gen2(func(enc2 []byte) { |
| 623 gen3(func(enc3 []byte) { |
| 624 gen4(func(enc4 []byte) { |
| 625 try(append(append(append(enc1[:l
en(enc1):len(enc1)], enc2...), enc3...), enc4...)) |
| 626 }) |
| 627 }) |
| 628 }) |
| 629 }) |
| 630 } |
| 631 } |
| 632 |
| 633 // filter generates the sequences from gen that satisfy ok. |
| 634 func filter(gen func(func([]byte)), ok func([]byte) bool) func(func([]byte)) { |
| 635 return func(try func([]byte)) { |
| 636 gen(func(enc []byte) { |
| 637 if ok(enc) { |
| 638 try(enc) |
| 639 } |
| 640 }) |
| 641 } |
| 642 } |
| 643 |
| 644 // enum8bit generates all possible 1-byte sequences, followed by distinctive pad
ding. |
| 645 func enum8bit(try func([]byte)) { |
| 646 for i := 0; i < 1<<8; i++ { |
| 647 try([]byte{byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x
88}) |
| 648 } |
| 649 } |
| 650 |
| 651 // enum8bit generates all possible 2-byte sequences, followed by distinctive pad
ding. |
| 652 func enum16bit(try func([]byte)) { |
| 653 for i := 0; i < 1<<16; i++ { |
| 654 try([]byte{byte(i), byte(i >> 8), 0x11, 0x22, 0x33, 0x44, 0x55,
0x66, 0x77, 0x88}) |
| 655 } |
| 656 } |
| 657 |
| 658 // enum24bit generates all possible 3-byte sequences, followed by distinctive pa
dding. |
| 659 func enum24bit(try func([]byte)) { |
| 660 for i := 0; i < 1<<24; i++ { |
| 661 try([]byte{byte(i), byte(i >> 8), byte(i >> 16), 0x11, 0x22, 0x3
3, 0x44, 0x55, 0x66, 0x77, 0x88}) |
| 662 } |
| 663 } |
| 664 |
| 665 // enumModRM generates all possible modrm bytes and, for modrm values that indic
ate |
| 666 // a following sib byte, all possible modrm, sib combinations. |
| 667 func enumModRM(try func([]byte)) { |
| 668 for i := 0; i < 256; i++ { |
| 669 if (i>>3)&07 == 04 && i>>6 != 3 { // has sib |
| 670 for j := 0; j < 256; j++ { |
| 671 try([]byte{0, byte(i), byte(j), 0x11, 0x22, 0x33
, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings |
| 672 try([]byte{1, byte(i), byte(j), 0x11, 0x22, 0x33
, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings |
| 673 } |
| 674 } else { |
| 675 try([]byte{0, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x6
6, 0x77, 0x88}) // byte encodings |
| 676 try([]byte{1, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x6
6, 0x77, 0x88}) // word encodings |
| 677 } |
| 678 } |
| 679 } |
| 680 |
| 681 // fixed generates the single case b. |
| 682 // It's mainly useful to prepare an argument for concat or concat3. |
| 683 func fixed(b ...byte) func(func([]byte)) { |
| 684 return func(try func([]byte)) { |
| 685 try(b) |
| 686 } |
| 687 } |
| 688 |
| 689 // testBasic runs the given test function with cases all using opcode as the ini
tial opcode bytes. |
| 690 // It runs three phases: |
| 691 // |
| 692 // First, zero-or-one prefixes followed by opcode followed by all possible 1-byt
e values. |
| 693 // If in -short mode, that's all. |
| 694 // |
| 695 // Second, zero-or-one prefixes followed by opcode followed by all possible 2-by
te values. |
| 696 // If not in -long mode, that's all. This phase and the next run in parallel wit
h other tests |
| 697 // (using t.Parallel). |
| 698 // |
| 699 // Finally, opcode followed by all possible 3-byte values. The test can take a v
ery long time |
| 700 // and prints progress messages to package log. |
| 701 func testBasic(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode
...byte) { |
| 702 testfn(t, concat3(basicPrefixes, fixed(opcode...), enum8bit)) |
| 703 if testing.Short() { |
| 704 return |
| 705 } |
| 706 |
| 707 t.Parallel() |
| 708 testfn(t, concat3(basicPrefixes, fixed(opcode...), enum16bit)) |
| 709 if !*longTest { |
| 710 return |
| 711 } |
| 712 |
| 713 name := caller(2) |
| 714 op1 := make([]byte, len(opcode)+1) |
| 715 copy(op1, opcode) |
| 716 for i := 0; i < 256; i++ { |
| 717 log.Printf("%s 24-bit: %d/256\n", name, i) |
| 718 op1[len(opcode)] = byte(i) |
| 719 testfn(t, concat(fixed(op1...), enum16bit)) |
| 720 } |
| 721 } |
| 722 |
| 723 func testBasicREX(t *testing.T, testfn func(*testing.T, func(func([]byte))), opc
ode ...byte) { |
| 724 testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), e
num8bit), isValidREX)) |
| 725 if testing.Short() { |
| 726 return |
| 727 } |
| 728 |
| 729 t.Parallel() |
| 730 testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), e
num16bit), isValidREX)) |
| 731 if !*longTest { |
| 732 return |
| 733 } |
| 734 |
| 735 name := caller(2) |
| 736 op1 := make([]byte, len(opcode)+1) |
| 737 copy(op1, opcode) |
| 738 for i := 0; i < 256; i++ { |
| 739 log.Printf("%s 24-bit: %d/256\n", name, i) |
| 740 op1[len(opcode)] = byte(i) |
| 741 testfn(t, filter(concat3(rexPrefixes, fixed(op1...), enum16bit),
isValidREX)) |
| 742 } |
| 743 } |
| 744 |
| 745 // testPrefix runs the given test function for all many prefix possibilities |
| 746 // followed by all possible 1-byte sequences. |
| 747 // |
| 748 // If in -long mode, it then runs a test of all the prefix possibilities followe
d |
| 749 // by all possible 2-byte sequences. |
| 750 func testPrefix(t *testing.T, testfn func(*testing.T, func(func([]byte)))) { |
| 751 t.Parallel() |
| 752 testfn(t, concat(manyPrefixes, enum8bit)) |
| 753 if testing.Short() || !*longTest { |
| 754 return |
| 755 } |
| 756 |
| 757 name := caller(2) |
| 758 for i := 0; i < 256; i++ { |
| 759 log.Printf("%s 16-bit: %d/256\n", name, i) |
| 760 testfn(t, concat3(manyPrefixes, fixed(byte(i)), enum8bit)) |
| 761 } |
| 762 } |
| 763 |
| 764 func testPrefixREX(t *testing.T, testfn func(*testing.T, func(func([]byte)))) { |
| 765 t.Parallel() |
| 766 testfn(t, filter(concat3(manyPrefixes, rexPrefixes, enum8bit), isValidRE
X)) |
| 767 if testing.Short() || !*longTest { |
| 768 return |
| 769 } |
| 770 |
| 771 name := caller(2) |
| 772 for i := 0; i < 256; i++ { |
| 773 log.Printf("%s 16-bit: %d/256\n", name, i) |
| 774 testfn(t, filter(concat4(manyPrefixes, rexPrefixes, fixed(byte(i
)), enum8bit), isValidREX)) |
| 775 } |
| 776 } |
| 777 |
| 778 func caller(skip int) string { |
| 779 pc, _, _, _ := runtime.Caller(skip) |
| 780 f := runtime.FuncForPC(pc) |
| 781 name := "?" |
| 782 if f != nil { |
| 783 name = f.Name() |
| 784 if i := strings.LastIndex(name, "."); i >= 0 { |
| 785 name = name[i+1:] |
| 786 } |
| 787 } |
| 788 return name |
| 789 } |
| 790 |
| 791 func isValidREX(x []byte) bool { |
| 792 i := 0 |
| 793 for i < len(x) && isPrefixByte(x[i]) { |
| 794 i++ |
| 795 } |
| 796 if i < len(x) && Prefix(x[i]).IsREX() { |
| 797 i++ |
| 798 if i < len(x) { |
| 799 return !isPrefixByte(x[i]) && !Prefix(x[i]).IsREX() |
| 800 } |
| 801 } |
| 802 return true |
| 803 } |
| 804 |
| 805 func isPrefixByte(b byte) bool { |
| 806 switch b { |
| 807 case 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65, 0x66, 0x67, 0xF0, 0xF2, 0xF3: |
| 808 return true |
| 809 } |
| 810 return false |
| 811 } |
LEFT | RIGHT |