// 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. // X86map constructs the x86 opcode map from the instruction set CSV file. // // Usage: // // x86map [-fmt=format] x86.csv // // The known output formats are: // // text (default) - print decoding tree in text form // decoder - print decoding tables for the x86asm package // scanner - print scanning tables for x86scan package package main import ( "bufio" "bytes" "encoding/csv" "flag" "fmt" "io" "log" "os" "sort" "strconv" "strings" ) var format = flag.String("fmt", "text", "output format: text, decoder") var inputFile string func usage() { fmt.Fprintf(os.Stderr, "usage: x86map [-fmt=format] x86.csv\n") os.Exit(2) } func main() { log.SetFlags(0) log.SetPrefix("x86map: ") flag.Usage = usage flag.Parse() if flag.NArg() != 1 { usage() } inputFile = flag.Arg(0) var print func(*Prog) switch *format { default: log.Fatalf("unknown output format %q", *format) case "text": print = printText case "decoder": print = printDecoder case "scanner": print = printScanner } p, err := readCSV(flag.Arg(0)) if err != nil { log.Fatal(err) } //p = mergeTail(p) print(p) } // readCSV reads the CSV file and returns the corresponding Prog. // It may print details about problems to standard error using the log package. func readCSV(file string) (*Prog, error) { // Read input. // Skip leading blank and # comment lines. f, err := os.Open(file) if err != nil { return nil, err } b := bufio.NewReader(f) for { c, err := b.ReadByte() if err != nil { break } if c == '\n' { continue } if c == '#' { b.ReadBytes('\n') continue } b.UnreadByte() break } table, err := csv.NewReader(b).ReadAll() if err != nil { return nil, fmt.Errorf("parsing %s: %v", file, err) } if len(table) == 0 { return nil, fmt.Errorf("empty csv input") } if len(table[0]) < 6 { return nil, fmt.Errorf("csv too narrow: need at least six columns") } p := &Prog{} for _, row := range table { add(p, row[0], row[1], row[2], row[3], row[4], row[5]) } check(p) return p, nil } // A Prog is a single node in the tree representing the instruction format. // Collectively the tree of nodes form a kind of program for decoding. // Each Prog has a single action, identifying the kind of node it is, // and then children to be executed according to the action. // For example, the Prog with Action="decode" has children named for each // possible next byte in the input, and those children are the decoding // tree to execute for the corresponding bytes. type Prog struct { Path string Action string Child map[string]*Prog PC int TailID int } // keys returns the child keys in sorted order. func (p *Prog) keys() []string { var keys []string for key := range p.Child { keys = append(keys, key) } sort.Strings(keys) return keys } // findChildLeaf finds a leaf node in the subtree rooted at p // and returns that node's full path. The path is useful in error // messages as an example of where a particular subtree is headed. func (p *Prog) findChildLeaf() string { for { if len(p.Child) == 0 { return p.Path } p = p.Child[p.keys()[0]] } } // walk advances from p to apply the given action and key. // If p has no action yet, the action is recorded as p.Action. // Otherwise the action must match p's action: every node in the // tree can have at most one action, although possibly with many // alternative keys. // If p already has an alternative with the given key, walk returns // that preexisting subtree. Otherwise walk allocates a new Prog // representing that subtree and returns that node. func (p *Prog) walk(action, key, text, opcode string) *Prog { if p.Action == "" { p.Action = action } else if p.Action != action { log.Printf("%s; %s: conflicting paths %s and %s|%s %s\n", text, opcode, p.findChildLeaf(), p.Path, action, key) return new(Prog) } q := p.Child[key] if q == nil { if p.Child == nil { p.Child = make(map[string]*Prog) } q = new(Prog) q.Path = fmt.Sprintf("%s|%s %s", p.Path, action, key) p.Child[key] = q } return q } // add adds a single instructions to the tree rooted at root. // The string arguments match the CSV: instruction mnemonic, // opcode encoding, validity in 32- and 64-bit modes, CPUID // feature set (ignored), and additional tags. // // In effect, add adds a new path through the tree leading to // the given instruction, but it reuses as much of the existing // tree structure as possible. For example if there have already // been instructions added starting with 0F and this instruction // also starts with 0F, that 0F subtree node is reused instead of // allocating a parallel one. To maximize the reuse, the check action // sequence along the path being added is the same for every instruction: // encoding pieces needed to make a decision, 64-bit mode check, // rex check, prefix check, address size check, data size check, // register vs memory argument check. Once all those checks have // been applied, the assumption is that we have uniquely identified // an instruction, and at that point it is okay to diverge from the // uniform pattern to set the opcode and read the specific arguments // corresponding to the instruction at hand. // // The maximimal reuse of the existing tree means that the tree // resulting from all adds have been done amounts to a decision tree. // There is one detail that makes it non-deterministic: some checks // do not matter to some instructions and those are recorded as "any" keys. // If you are decoding and there is a key for the specific thing you are // seeing as well as the "any" key, both must be considered. To avoid // adding complexity to the decoder execution, the 'check' function // removes this case by merging "any" trees into specific keys when // present. func add(root *Prog, text, opcode, valid32, valid64, cpuid, tags string) { // These are not real instructions: they are either // prefixes for other instructions, composite instructions // built from multiple individual instructions, or alternate // mnemonics of other encodings. // Discard for disassembly, because we want a unique decoding. if strings.Contains(tags, "pseudo") { return } // Treat REX.W + opcode as being like having an "operand64" tag. // The REX.W flag sets the operand size to 64 bits; in this way it is // not much different than the 66 prefix that inverts 32 vs 16 bits. if strings.Contains(opcode, "REX.W") { if !strings.Contains(tags, "operand64") { if tags != "" { tags += "," } tags += "operand64" } } // If there is more than one operand size given, we need to do // a separate add for each size, because we need multiple // keys to be added in the operand size branch, and the code makes // a linear pass through the tree adding just one key to each node. // We would need to do the same for any other possible repeated tag // (for example, if an instruction could have multiple address sizes) // but so far operand size is the only tag we have needed to repeat. if strings.Count(tags, "operand") > 1 { f := strings.Split(tags, ",") var ops []string w := 0 for _, tag := range f { if strings.HasPrefix(tag, "operand") { ops = append(ops, tag) } else { if strings.Contains(tag, "operand") { log.Fatalf("unknown tag %q", tag) } f[w] = tag w++ } } f = f[:w] for _, op := range ops { add(root, text, opcode, valid32, valid64, cpuid, strings.Join(append(f, op), ",")) } return } p := root walk := func(action, item string) { p = p.walk(action, item, text, opcode) } // Ignore VEX instructions for now. if strings.HasPrefix(opcode, "VEX") { if !strings.HasPrefix(text, "VMOVNTDQ") && !strings.HasPrefix(text, "VMOVDQA") && !strings.HasPrefix(text, "VMOVDQU") && !strings.HasPrefix(text, "VZEROUPPER") { return } if !strings.HasPrefix(opcode, "VEX.256") && !strings.HasPrefix(text, "VZEROUPPER") { return } if !strings.Contains(tags, "VEXC4") { add(root, text, opcode, valid32, valid64, cpuid, tags+",VEXC4") } encoding := strings.Fields(opcode) walk("decode", encoding[1]) walk("is64", "any") if strings.Contains(tags, "VEXC4") { walk("prefix", "C4") } else { walk("prefix", "C5") } for _, pref := range strings.Split(encoding[0], ".") { if isVexEncodablePrefix[pref] { walk("prefix", pref) } } } var rex, prefix string encoding := strings.Fields(opcode) if len(encoding) > 0 && strings.HasPrefix(encoding[0], "REX") { rex = encoding[0] encoding = encoding[1:] if len(encoding) > 0 && encoding[0] == "+" { encoding = encoding[1:] } } if len(encoding) > 0 && isPrefix[encoding[0]] { prefix = encoding[0] encoding = encoding[1:] } if rex == "" && len(encoding) > 0 && strings.HasPrefix(encoding[0], "REX") { rex = encoding[0] if rex == "REX" { log.Printf("REX without REX.W: %s %s", text, opcode) } encoding = encoding[1:] if len(encoding) > 0 && encoding[0] == "+" { encoding = encoding[1:] } } if len(encoding) > 0 && isPrefix[encoding[0]] { log.Printf("%s %s: too many prefixes", text, opcode) return } var haveModRM, havePlus bool var usedReg string for len(encoding) > 0 && (isHex(encoding[0]) || isSlashNum(encoding[0])) { key := encoding[0] if isSlashNum(key) { if usedReg != "" { log.Printf("%s %s: multiple modrm checks", text, opcode) } haveModRM = true usedReg = key } if i := strings.Index(key, "+"); i >= 0 { key = key[:i+1] havePlus = true } walk("decode", key) encoding = encoding[1:] } if valid32 != "V" { walk("is64", "1") } else if valid64 != "V" { walk("is64", "0") } else { walk("is64", "any") } if prefix == "" { prefix = "0" } walk("prefix", prefix) if strings.Contains(tags, "address16") { walk("addrsize", "16") } else if strings.Contains(tags, "address32") { walk("addrsize", "32") } else if strings.Contains(tags, "address64") { walk("addrsize", "64") } else { walk("addrsize", "any") } if strings.Contains(tags, "operand16") { walk("datasize", "16") } else if strings.Contains(tags, "operand32") { walk("datasize", "32") } else if strings.Contains(tags, "operand64") { walk("datasize", "64") } else { walk("datasize", "any") } if len(encoding) > 0 && encoding[0] == "/r" { haveModRM = true } if haveModRM { if strings.Contains(tags, "modrm_regonly") { walk("ismem", "0") } else if strings.Contains(tags, "modrm_memonly") { walk("ismem", "1") } else { walk("ismem", "any") } } walk("op", strings.Fields(text)[0]) if len(encoding) > 0 && strings.HasPrefix(encoding[0], "VEX") { for _, field := range encoding[2:] { walk("read", field) } } else { for _, field := range encoding { walk("read", field) } } var usedRM string for _, arg := range strings.Fields(text)[1:] { arg = strings.TrimRight(arg, ",") if usesReg[arg] && !haveModRM && !havePlus { log.Printf("%s %s: no modrm field to use for %s", text, opcode, arg) continue } if usesRM[arg] && !haveModRM { log.Printf("%s %s: no modrm field to use for %s", text, opcode, arg) continue } if usesReg[arg] { if usedReg != "" { log.Printf("%s %s: modrm reg field used by both %s and %s", text, opcode, usedReg, arg) continue } usedReg = arg } if usesRM[arg] { if usedRM != "" { log.Printf("%s %s: modrm r/m field used by both %s and %s", text, opcode, usedRM, arg) continue } usedRM = arg } walk("arg", arg) } walk("match", "!") } // allKeys records the list of all possible child keys for actions that support "any". var allKeys = map[string][]string{ "is64": {"0", "1"}, "ismem": {"0", "1"}, "addrsize": {"16", "32", "64"}, "datasize": {"16", "32", "64"}, } // check checks that the program tree is well-formed. // It also merges "any" keys into specific decoding keys in order to // create an invariant that a particular check node either has a // single "any" child - making it a no-op - or has no "any" children. // See the discussion of "any" in the comment for add above. func check(p *Prog) { if p.Child["any"] != nil && len(p.Child) > 1 { for _, key := range p.keys() { if key != "any" { mergeCopy(p.Child[key], p.Child["any"]) } } if allKeys[p.Action] == nil { log.Printf("%s: unknown key space for %s=any", p.Path, p.Action) } for _, key := range allKeys[p.Action] { if p.Child[key] == nil { p.Child[key] = p.Child["any"] } } delete(p.Child, "any") } for _, q := range p.Child { check(q) } switch p.Action { case "op", "read", "arg": if len(p.Child) > 1 { log.Printf("%s: multiple children for action=%s: %v", p.Path, p.Action, p.keys()) } } } // mergeCopy merges a copy of the tree rooted at src into dst. // It is only used once no more paths will be added to the tree, // so it is safe to introduce cross-links that make the program // a dag rather than a tree. func mergeCopy(dst, src *Prog) { //log.Printf("merge %s|%s and %s|%s\n", dst.Path, dst.Action, src.Path, src.Action) if dst.Action != src.Action { log.Printf("cannot merge %s|%s and %s|%s", dst.Path, dst.Action, src.Path, src.Action) return } for _, key := range src.keys() { if dst.Child[key] == nil { // Create new subtree by creating cross-link. dst.Child[key] = src.Child[key] } else { // Merge src subtree into existing dst subtree. mergeCopy(dst.Child[key], src.Child[key]) } } } // set returns a map mapping each of the words in all to true. func set(all string) map[string]bool { m := map[string]bool{} for _, f := range strings.Fields(all) { m[f] = true } return m } // isPrefix records the x86 opcode prefix bytes. var isPrefix = set(` 26 2E 36 3E 64 65 66 67 F0 F2 F3 `) // usesReg records the argument codes that use the modrm reg field. var usesReg = set(` r8 r16 r32 r64 `) // usesRM records the argument codes that use the modrm r/m field. var usesRM = set(` r/m8 r/m16 r/m32 r/m64 `) var isVexEncodablePrefix = set(` 0F 0F38 0F3A 66 F3 F2 `) // isHex reports whether the argument is a two digit hex number // possibly followed by a +foo suffix. func isHex(s string) bool { if i := strings.Index(s, "+"); i >= 0 { s = s[:i] } if len(s) != 2 { return false } for i := 0; i < len(s); i++ { c := s[i] if '0' <= c && c <= '9' || 'A' <= c && c <= 'F' { continue } return false } return true } // isSlashNum reports whether the argument is /n for some number n in [0,7]. func isSlashNum(s string) bool { return len(s) == 2 && s[0] == '/' && '0' <= s[1] && s[1] <= '7' } // mergeTail is supposed to merge common subtrees (program tails), // reducing the size of the final program code. // It identifies the subtrees using a bottom-up canonicalization. // // THIS CODE DOES NOT WORK. IT NEEDS TO BE DEBUGGED. func mergeTail(p *Prog, emitted map[string]*Prog) *Prog { if emitted == nil { emitted = make(map[string]*Prog) } if p.Action == "match" { return p } for _, key := range p.keys() { p.Child[key] = mergeTail(p.Child[key], emitted) } op := "" for _, key := range p.keys() { q := p.Child[key] if q.Action != "op" || len(q.Child) > 1 { op = "" break } qop := q.keys()[0] if op == "" { op = qop } else if op != qop { op = "" break } } if op != "" { // Pull 'op x' up above the discriminator. p1 := new(Prog) *p1 = *p for _, key := range p.keys() { p1.Child[key] = p.Child[key].Child[op] } p.Action = "op" p.Child = map[string]*Prog{op: p1} } var buf bytes.Buffer fmt.Fprintf(&buf, "%s\n", p.Action) for _, key := range p.keys() { fmt.Fprintf(&buf, "%s %d\n", key, p.Child[key].TailID) } key := buf.String() if q := emitted[key]; q != nil { return q } emitted[key] = p p.TailID = len(emitted) return p } // printText prints the tree in textual form. func printText(p *Prog) { printTree(os.Stdout, p, 0, false) } var tabs = strings.Repeat(" ", 100) func printTree(w io.Writer, p *Prog, depth int, compact bool) { if compact && len(p.Child) == 1 { fmt.Fprintf(w, "%.*s%s", 4*depth, tabs, p.Action) for len(p.Child) == 1 { key := p.keys()[0] child := p.Child[key] fmt.Fprintf(w, " %s %s", key, child.Action) p = child } fmt.Fprintf(w, "\n") } else { fmt.Fprintf(w, "%.*s%s\n", 4*depth, tabs, p.Action) } for _, key := range p.keys() { fmt.Fprintf(w, "%.*s%s\n", 4*(depth+1), tabs, key) printTree(w, p.Child[key], depth+2, compact) } } // printDecoder prints a Go array containing the decoder program. // It runs in two passes, both of which traverse and could generate // the entire program. The first pass records the PC for each Prog node, // and the second pass emits the actual program, using the PCs as jump // targets in the places where the program is a dag rather than a tree. func printDecoder(p *Prog) { opMap := map[string]bool{ "PAUSE": true, } printDecoderPass(p, 1, false, opMap) fmt.Printf("// Code generated by x86map -fmt=decoder %s DO NOT EDIT.\n", inputFile) fmt.Printf("\n") fmt.Printf("package x86asm\n\n") fmt.Printf("var decoder = [...]uint16{\n\tuint16(xFail),\n") printDecoderPass(p, 1, true, opMap) fmt.Printf("}\n\n") var ops []string for op := range opMap { ops = append(ops, op) } sort.Strings(ops) fmt.Printf("const (\n") fmt.Printf("\t_ Op = iota\n\n") last := "" for _, op := range ops { fmt.Printf("\t%s\n", op) last = op } fmt.Printf(")\n\n") fmt.Printf("const maxOp = %s\n\n", last) fmt.Printf("var opNames = [...]string{\n") for _, op := range ops { fmt.Printf("\t%s: \"%s\",\n", op, op) } fmt.Printf("}\n") } // printScanner prints the decoding table for a scanner. // The scanner can identify instruction boundaries but does not do // full decoding. It is meant to be lighter weight than the x86asm // decoder tables. func printScanner(p *Prog) { walkScanTree(p, -1) var out []uint16 out = append(out, 0) emitScanFunc(p, &out) fmt.Printf("var scanProg = []uint16{\n") fmt.Printf("\t/*0*/ 0, // dead\n") for i := 1; i < len(out); i++ { fmt.Printf("\t/*%d*/ ", i) switch out[i] { default: log.Fatalf("malformed program %#x", out[i]) case scanMatch: fmt.Printf("scanMatch,\n") continue case scanJump: fmt.Printf("scanJump, %d,\n", out[i+1]) i++ continue case scanSwitchByte: fmt.Printf("scanSwitchByte,\n") for j := 0; j < 256/8; j++ { fmt.Printf("\t") fmt.Printf("/* %#02x-%#02x */", j*8, j*8+7) for k := 0; k < 8; k++ { fmt.Printf(" %d,", out[i+1+j*8+k]) } fmt.Printf("\n") } i += 256 continue case scanSwitchSlash: fmt.Printf("scanSwitchSlash, %d,\n", out[i+1]) n := int(out[i+1]) for j := 0; j < n; j++ { fmt.Printf("\t/* byte */ %#x, %d,\n", out[i+2+2*j], out[i+2+2*j+1]) } for j := 0; j < 8; j++ { fmt.Printf("\t/* /%d */ %d,\n", j, out[i+2+2*n+j]) } i += 1 + 2*n + 8 continue case scanSwitchPrefix: fmt.Printf("scanSwitchPrefix, %d,\n", out[i+1]) n := int(out[i+1]) for j := 0; j < n; j++ { fmt.Printf("\t/* prefix */ %#x, %d,\n", out[i+2+2*j], out[i+2+2*j+1]) } i += 1 + 2*n continue case scanSwitchIs64: fmt.Printf("scanSwitchIs64, %d, %d\n", out[i+1], out[i+2]) i += 2 continue case scanSwitchDatasize: fmt.Printf("scanSwitchDatasize, %d, %d, %d\n", out[i+1], out[i+2], out[i+3]) i += 3 continue case scanSwitchIsMem: fmt.Printf("scanSwitchIsMem, %d, %d\n", out[i+1], out[i+2]) i += 2 continue case scanReadModRM: fmt.Printf("scanReadModRM,\n") continue case scanReadIB: fmt.Printf("scanReadIB,\n") continue case scanReadIW: fmt.Printf("scanReadIW,\n") continue case scanReadIWD: fmt.Printf("scanReadIWD,\n") continue case scanReadIWDO: fmt.Printf("scanReadIWDO,\n") continue case scanReadCWD: fmt.Printf("scanReadCWD,\n") continue case scanReadCB: fmt.Printf("scanReadCB,\n") continue case scanReadCDP: fmt.Printf("scanReadCDP,\n") continue case scanReadCM: fmt.Printf("scanReadCM,\n") continue } } fmt.Printf("}\n") } func walkScanTree(p *Prog, is64 int) { keys := p.keys() for _, key := range keys { if p.Action == "is64" { switch key { case "0": is64 = 0 case "1": is64 = 1 } } walkScanTree(p.Child[key], is64) } switch p.Action { case "read", "match": // keep return case "decode": if len(keys) >= 8 && keys[0] == "/0" && keys[7] == "/7" && allSame(p, keys) { p.Action = "read" p.Child = map[string]*Prog{"/r": p.Child[keys[0]]} return } case "op", "arg": // drop *p = *p.Child[keys[0]] return case "prefix": if len(keys) >= 1 && keys[0] == "0" && allSame(p, keys) { *p = *p.Child[keys[0]] return } case "is64", "addrsize", "datasize", "ismem": if len(keys) == 1 && keys[0] == "any" { *p = *p.Child[keys[0]] return } nkey := len(allKeys[p.Action]) if p.Action == "addrsize" { nkey = 2 } if p.Action == "datasize" && is64 == 0 { nkey = 2 } if len(keys) == nkey && allSame(p, keys) { *p = *p.Child[keys[0]] return } } switch p.Action { case "datasize": if len(keys) == 2 && is64 == 0 || len(keys) == 3 { if treeText(p.Child["16"]) == "read iw match ! \n" && treeText(p.Child["32"]) == "read id match ! \n" && (len(keys) == 2 || treeText(p.Child["64"]) == "read id match ! \n") { p.Action = "read" p.Child = map[string]*Prog{"iwd/d": p.Child["16"].Child["iw"]} return } if len(keys) == 3 && treeText(p.Child["16"]) == "read iw match ! \n" && treeText(p.Child["32"]) == "read id match ! \n" && treeText(p.Child["64"]) == "read io match ! \n" { p.Action = "read" p.Child = map[string]*Prog{"iwdo/d": p.Child["16"].Child["iw"]} return } if treeText(p.Child["16"]) == "read /r read iw match ! \n" && treeText(p.Child["32"]) == "read /r read id match ! \n" && (len(keys) == 2 || treeText(p.Child["64"]) == "read /r read id match ! \n") { p.Action = "read" p.Child = map[string]*Prog{"/r": {Action: "read", Child: map[string]*Prog{"iwd/d": p.Child["16"].Child["/r"].Child["iw"]}}} return } if treeText(p.Child["16"]) == "read cw match ! \n" && treeText(p.Child["32"]) == "read cd match ! \n" && (len(keys) == 2 || treeText(p.Child["64"]) == "read cd match ! \n") { p.Action = "read" p.Child = map[string]*Prog{"cwd/d": p.Child["16"].Child["cw"]} return } if treeText(p.Child["16"]) == "read cd match ! \n" && treeText(p.Child["32"]) == "read cp match ! \n" && (len(keys) == 2 || treeText(p.Child["64"]) == "read cp match ! \n") { p.Action = "read" p.Child = map[string]*Prog{"cdp/d": p.Child["16"].Child["cd"]} return } fmt.Printf("!! %q\n", treeText(p.Child["16"])) } case "is64": if len(keys) == 2 && treeText(p.Child["0"]) == "read cwd/d match ! \n" && treeText(p.Child["1"]) == "read cd match ! \n" { *p = *p.Child["0"] return } if len(keys) == 2 && treeText(p.Child["0"]) == "read iwd/d match ! \n" && treeText(p.Child["1"]) == "read iwdo/d match ! \n" { *p = *p.Child["1"] return } } /* match := make(map[string][]string) for _, key := range keys { text := treeText(p.Child[key]) match[text] = append(match[text], key) } child := make(map[string]*Prog) for _, keys := range match { child[strings.Join(keys, ",")] = p.Child[keys[0]] } p.Child = child */ } func treeText(p *Prog) string { var buf bytes.Buffer printTree(&buf, p, 0, true) return buf.String() } func allSame(p *Prog, keys []string) bool { var tree string for i, key := range keys { if i == 0 { tree = treeText(p.Child[key]) continue } if treeText(p.Child[key]) != tree { return false } } return true } var scanCache = map[string]uint16{} const ( _ uint16 = iota scanMatch scanJump scanSwitchByte scanSwitchSlash scanSwitchIs64 scanSwitchDatasize scanSwitchIsMem scanSwitchPrefix scanReadModRM scanReadIB scanReadIW scanReadIWD scanReadIWDO scanReadCWD scanReadCB scanReadCDP scanReadCM ) func decodeKeyPlus(key string) (val, n int) { n = 1 if strings.HasSuffix(key, "+") { n = 8 key = key[:len(key)-1] } v, err := strconv.ParseUint(key, 16, 8) if err != nil { log.Fatalf("unexpected decode key %q", key) } return int(v), n } func decodeKey(key string) int { val, n := decodeKeyPlus(key) if n != 1 { log.Panicf("unexpected decode key+ %q", key) } return val } func emitScanFunc(p *Prog, out *[]uint16) uint16 { keys := p.keys() text := treeText(p) if off, ok := scanCache[text]; ok { return off } start := uint16(len(*out)) scanCache[text] = start switch p.Action { case "decode": if keys[0][0] != '/' { *out = append(*out, scanSwitchByte) off := len(*out) for i := 0; i < 256; i++ { *out = append(*out, 0) } for _, key := range keys { val, n := decodeKeyPlus(key) dst := emitScanFunc(p.Child[key], out) for j := 0; j < n; j++ { (*out)[off+val+j] = dst } } return start } n := len(keys) for n > 0 && keys[n-1][0] != '/' { n-- } total := 0 for i := n; i < len(keys); i++ { key := keys[i] _, n := decodeKeyPlus(key) total += n } *out = append(*out, scanSwitchSlash, uint16(total)) off := len(*out) for i := 0; i < total; i++ { *out = append(*out, 0, 0) } for i := 0; i < 8; i++ { *out = append(*out, 0) } for i := n; i < len(keys); i++ { key := keys[i] val, valn := decodeKeyPlus(key) targ := emitScanFunc(p.Child[key], out) for j := 0; j < valn; j++ { (*out)[off] = uint16(val + j) off++ (*out)[off] = targ off++ } } for i := 0; i < n; i++ { key := keys[i] if len(key) != 2 || key[0] != '/' || key[1] < '0' || '8' <= key[1] { log.Fatalf("unexpected decode key %q", key) } (*out)[off+int(key[1]-'0')] = emitScanFunc(p.Child[key], out) } return start case "read": switch keys[0] { default: log.Fatalf("unexpected read %q", keys[0]) case "/r": *out = append(*out, scanReadModRM) case "ib": *out = append(*out, scanReadIB) case "iw": *out = append(*out, scanReadIW) case "cb": *out = append(*out, scanReadCB) case "cm": *out = append(*out, scanReadCM) case "iwd/d": *out = append(*out, scanReadIWD) case "iwdo/d": *out = append(*out, scanReadIWDO) case "cwd/d": *out = append(*out, scanReadCWD) case "cdp/d": *out = append(*out, scanReadCDP) } next := p.Child[keys[0]] if next.Action == "match" { *out = append(*out, scanMatch) } else { *out = append(*out, scanJump, 0) off := len(*out) (*out)[off-1] = emitScanFunc(next, out) } return start case "match": *out = append(*out, scanMatch) return start case "is64": *out = append(*out, scanSwitchIs64, 0, 0) if next := p.Child["0"]; next != nil { (*out)[start+1] = emitScanFunc(next, out) } if next := p.Child["1"]; next != nil { (*out)[start+2] = emitScanFunc(next, out) } return start case "ismem": *out = append(*out, scanSwitchIsMem, 0, 0) if next := p.Child["0"]; next != nil { (*out)[start+1] = emitScanFunc(next, out) } if next := p.Child["1"]; next != nil { (*out)[start+2] = emitScanFunc(next, out) } return start case "datasize": *out = append(*out, scanSwitchDatasize, 0, 0, 0) if next := p.Child["16"]; next != nil { (*out)[start+1] = emitScanFunc(next, out) } if next := p.Child["32"]; next != nil { (*out)[start+2] = emitScanFunc(next, out) } if next := p.Child["64"]; next != nil { (*out)[start+3] = emitScanFunc(next, out) } return start case "prefix": *out = append(*out, scanSwitchPrefix, uint16(len(keys))) n := len(keys) for i := 0; i < n; i++ { *out = append(*out, uint16(decodeKey(keys[i])), 0) } for i := 0; i < n; i++ { (*out)[int(start)+2+2*i+1] = emitScanFunc(p.Child[keys[i]], out) } return start } log.Fatalf("unexpected action %q", p.Action) return start } // printDecoderPass prints the decoding table program for p, // assuming that we are emitting code at the given program counter. // It returns the new current program counter, that is, the program // counter after the printed instructions. // If printing==false, printDecoderPass does not print the actual // code words but still does the PC computation. func printDecoderPass(p *Prog, pc int, printing bool, ops map[string]bool) int { // Record PC on first pass. if p.PC == 0 { p.PC = pc } // If PC doesn't match, we've already printed this code // because it was reached some other way. Jump to that copy. if p.PC != pc { if printing { fmt.Printf("/*%d*/\tuint16(xJump), %d,\n", pc, p.PC) } return pc + 2 } // Otherwise, emit the code for the given action. // At the bottom, if next is non-nil, emit code for next. // Then emit the code for the children named by the keys. keys := p.keys() var next *Prog switch p.Action { default: log.Printf("printDecoderPass: unknown action %q: %s", p.Action, p.Path) case "decode": // Decode hex bytes or /n modrm op checks. // Hex bytes take priority, so do them first. // Hex bytes of the form "40+" indicate an // 8 entry-wide swath of codes: 40, 41, ..., 47. hex := 0 slash := 0 for _, key := range keys { if isHex(key) { if strings.Contains(key, "+") { hex += 8 } else { hex++ } } if isSlashNum(key) { slash++ } } if hex > 0 { // TODO(rsc): Introduce an xCondByte256 that has 256 child entries // and no explicit keys. That will cut the size in half for large // tables. if printing { fmt.Printf("/*%d*/\tuint16(xCondByte), %d,\n", pc, hex) for _, key := range keys { if !isHex(key) { continue } if i := strings.Index(key, "+"); i >= 0 { nextPC := p.Child[key].PC n, _ := strconv.ParseUint(key[:i], 16, 0) for j := 0; j < 8; j++ { fmt.Printf("\t%#02x, %d,\n", int(n)+j, nextPC) } continue } fmt.Printf("\t0x%s, %d,\n", key, p.Child[key].PC) } } pc += 2 + 2*hex // All other condition checks fail the decoding if nothing is found, // but this one falls through so that we can then do /n checks. // If there are no upcoming /n checks, insert an explicit failure. if slash == 0 { if printing { fmt.Printf("\tuint16(xFail),\n") } pc++ } } if slash > 0 { if printing { fmt.Printf("/*%d*/\tuint16(xCondSlashR),\n", pc) for i := 0; i < 8; i++ { fmt.Printf("\t%d, // %d\n", p.childPC(fmt.Sprintf("/%d", i)), i) } } pc += 1 + 8 } case "is64": // Decode based on processor mode: 64-bit or not. if len(keys) == 1 && keys[0] == "any" { next = p.Child["any"] break } if p.Child["any"] != nil { log.Printf("%s: mixed is64 keys: %v", p.Path, keys) } if printing { fmt.Printf("/*%d*/\tuint16(xCondIs64), %d, %d,\n", pc, p.childPC("0"), p.childPC("1")) } pc += 3 case "prefix": // Decode based on presence of prefix. // The "0" prefix means "none of the above", so if there's // nothing else, it's the same as "any". if len(keys) == 1 && (keys[0] == "any" || keys[0] == "0") { next = p.Child["any"] break } if p.Child["any"] != nil { log.Printf("%s: mixed prefix keys: %v", p.Path, keys) } // Emit the prefixes in reverse sorted order, so that F3 and F2 are // considered before 66, and the fallback 0 is considered last. if printing { fmt.Printf("/*%d*/\tuint16(xCondPrefix), %d,\n", pc, len(keys)) for i := len(keys) - 1; i >= 0; i-- { key := keys[i] nextPC := p.Child[key].PC fmt.Printf("\t0x%s, %d,\n", key, nextPC) } } pc += 2 + 2*len(keys) case "addrsize": // Decode based on address size attribute. if len(keys) == 1 && keys[0] == "any" { next = p.Child["any"] break } if p.Child["any"] != nil { log.Printf("%s: mixed addrsize keys: %v", p.Path, keys) } if printing { fmt.Printf("/*%d*/\tuint16(xCondAddrSize), %d, %d, %d,\n", pc, p.childPC("16"), p.childPC("32"), p.childPC("64")) } pc += 4 case "datasize": // Decode based on operand size attribute. if len(keys) == 1 && keys[0] == "any" { next = p.Child["any"] break } if p.Child["any"] != nil { log.Printf("%s: mixed datasize keys: %v", p.Path, keys) } if printing { fmt.Printf("/*%d*/\tuint16(xCondDataSize), %d, %d, %d,\n", pc, p.childPC("16"), p.childPC("32"), p.childPC("64")) } pc += 4 case "ismem": // Decode based on modrm form: memory or register reference. if len(keys) == 1 && keys[0] == "any" { next = p.Child["any"] break } if p.Child["any"] != nil { log.Printf("%s: mixed ismem keys: %v", p.Path, keys) } if printing { fmt.Printf("/*%d*/\tuint16(xCondIsMem), %d, %d,\n", pc, p.childPC("0"), p.childPC("1")) } pc += 3 case "op": // Set opcode. ops[keys[0]] = true if printing { fmt.Printf("/*%d*/\tuint16(xSetOp), uint16(%s),\n", pc, keys[0]) } next = p.Child[keys[0]] pc += 2 case "read": // Read argument bytes. if printing { fmt.Printf("/*%d*/\tuint16(xRead%s),\n", pc, xOp(keys[0])) } next = p.Child[keys[0]] pc++ case "arg": // Record instruction argument (interpret bytes loaded with read). if printing { fmt.Printf("/*%d*/\tuint16(xArg%s),\n", pc, xOp(keys[0])) } next = p.Child[keys[0]] pc++ case "match": // Finish match. if printing { fmt.Printf("/*%d*/\tuint16(xMatch),\n", pc) } pc++ return pc } if next != nil { pc = printDecoderPass(next, pc, printing, ops) } for _, key := range keys { q := p.Child[key] if q.PC == 0 || q.PC == pc { pc = printDecoderPass(q, pc, printing, ops) } } return pc } // childPC returns the PC for the given child key. // If the key is not present, it returns PC 0, // which is known to be an xFail instruction. func (p *Prog) childPC(key string) int { q := p.Child[key] if q == nil { return 0 } return q.PC } // isLower reports whether c is an ASCII lower case letter. func isLower(c byte) bool { return 'a' <= c && c <= 'z' } // isLetterDigit reports whether c is an ASCII letter or digit. func isLetterDigit(c byte) bool { return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' } // xOp converts arg, an Intel manual shorthand, into a decoder opcode suffix. // The standard form is LeadingUpperLetter with a few punctuation symbols // turned into purely lower case words: M16and32, M16colon32, CR0dashCR7. func xOp(arg string) string { var buf []byte for i := 0; i < len(arg); i++ { c := arg[i] if isLower(c) && (i == 0 || !isLetterDigit(arg[i-1])) { c -= 'a' - 'A' } buf = append(buf, c) } return argFix.Replace(string(buf)) } var argFix = strings.NewReplacer( "/R", "SlashR", "/", "", "<", "", ">", "", "+", "plus", "-", "dash", ":", "colon", "&", "and", "ST(0)", "ST", "ST(I)", "STi", "ST(I)+Op", "STi", )