// Copyright 2017 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. // gen generates instruction tables (ifuzz/insns.go) from Intel XED tables. // Tables used to generate insns.go are checked in in all-enc-instructions.txt. package main import ( "bufio" "fmt" "os" "reflect" "strconv" "strings" "github.com/google/syzkaller/pkg/ifuzz" "github.com/google/syzkaller/pkg/serializer" ) // nolint: gocyclo func main() { if len(os.Args) != 2 { failf("usage: gen instructions.txt") } f, err := os.Open(os.Args[1]) if err != nil { failf("failed to open input file: %v", err) } defer f.Close() skipped := 0 saved := "" var insns []*ifuzz.Insn var insn, insn1 *ifuzz.Insn s := bufio.NewScanner(f) for i := 1; s.Scan(); i++ { reportError := func(msg string, args ...interface{}) { fmt.Fprintf(os.Stderr, "line %v: %v\n", i, s.Text()) failf(msg, args...) } line := s.Text() if comment := strings.IndexByte(line, '#'); comment != -1 { line = line[:comment] } line = strings.TrimSpace(line) if line == "" { continue } if line[len(line)-1] == '\\' { saved += line[:len(line)-1] continue } line = saved + line saved = "" if line == "{" { insn = new(ifuzz.Insn) continue } if line == "}" { if insn1 != nil { insns = append(insns, insn1) insn1 = nil insn = nil } continue } colon := strings.IndexByte(line, ':') if colon == -1 { reportError("no colon") } name := strings.TrimSpace(line[:colon]) if name == "" { reportError("empty attribute name") } var vals []string for _, v := range strings.Split(line[colon+1:], " ") { v = strings.TrimSpace(v) if v == "" { continue } vals = append(vals, v) } switch name { case "ICLASS": if len(vals) != 1 { reportError("ICLASS has more than one value") } insn.Name = vals[0] case "CPL": if len(vals) != 1 { reportError("CPL has more than one value") } if vals[0] != "0" && vals[0] != "3" { reportError("unknown CPL value: %v", vals[0]) } insn.Priv = vals[0] == "0" case "EXTENSION": if len(vals) != 1 { reportError("EXTENSION has more than one value") } insn.Extension = vals[0] switch insn.Extension { case "FMA", "AVX2", "AVX", "F16C", "BMI2", "BMI", "XOP", "FMA4", "AVXAES", "BMI1", "AVX2GATHER": insn.Mode = 1<= 0; j-- { insn1 := deduped[j] if insn.Mod == 3 && insn1.Mod == -3 || insn.Mod == -3 && insn1.Mod == 3 || insn1.Mod == -1 { insn.Mod = insn1.Mod } if reflect.DeepEqual(insn, insn1) { if insn.Mod != mod0 { insn1.Mod = -1 } continue nextInsn } insn.Mod = mod0 } deduped = append(deduped, insn) } fmt.Fprintf(os.Stderr, "deduped %v instructions\n", len(insns)-len(deduped)) insns = deduped fmt.Printf("// AUTOGENERATED FILE\n\n") fmt.Printf("package ifuzz\n\n") fmt.Printf("import . \"github.com/google/syzkaller/pkg/ifuzz\"\n\n") fmt.Printf("func init() { Insns = insns }\n\n") fmt.Printf("var insns = ") serializer.Write(os.Stdout, insns) fmt.Fprintf(os.Stderr, "handled %v, skipped %v\n", len(insns), skipped) } type errSkip string func (err errSkip) Error() string { return string(err) } // nolint: gocyclo func parsePattern(insn *ifuzz.Insn, vals []string) error { if insn.Opcode != nil { return fmt.Errorf("PATTERN is already parsed for the instruction") } // As spelled these have incorrect format for 16-bit addressing mode and with 67 prefix. if insn.Name == "NOP5" || insn.Name == "NOP6" || insn.Name == "NOP7" || insn.Name == "NOP8" || insn.Name == "NOP9" { return errSkip("") } if insn.Mode == 0 { insn.Mode = 1< 7 { return fmt.Errorf("bad REG value: %v", insn.Mod) } if insn.Rm < -1 || insn.Rm > 7 { return fmt.Errorf("bad RM value: %v", insn.Mod) } } if insn.Imm != 0 && len(insn.Suffix) != 0 { return fmt.Errorf("both immediate and suffix opcode") } if insn.Mode == 0 { return errSkip("no modes for instruction") } return nil } func parseOperands(insn *ifuzz.Insn, vals []string) error { for _, v := range vals { switch v { case "REG0=SEG():r", "REG1=SEG():r", "REG0=SEG():w": if insn.Reg != -1 { return fmt.Errorf("REG=SEG() operand, but fixed reg") } insn.Reg = -6 case "REG0=CR_R():w", "REG1=CR_R():r": if insn.Reg != -1 { return fmt.Errorf("REG=CR_R() operand, but fixed reg") } insn.Reg = -8 insn.NoSibDisp = true case "REG0=DR_R():w", "REG1=DR_R():r": insn.NoSibDisp = true case "MEM0:r:mem16", "MEM0:w:mem16", "MEM0:r:mem16int", "MEM0:w:mem16int": insn.Mem16 = true case "MEM0:r:mem32real", "MEM0:r:mem32int", "MEM0:w:mem32real", "MEM0:w:mem32int": insn.Mem32 = true } } return nil } func parseModrm(v string) (int8, error) { if len(v) < 4 || len(v) > 7 || v[0] != '[' || v[len(v)-1] != ']' { return 0, fmt.Errorf("malformed") } if v == "[mm]" || v == "[rrr]" || v == "[nnn]" { return -1, nil } if !strings.HasPrefix(v, "[0b") { return 0, fmt.Errorf("malformed") } var vv int8 for i := 3; i < len(v)-1; i++ { if v[i] != '0' && v[i] != '1' { return 0, fmt.Errorf("malformed") } vv *= 2 if v[i] == '1' { vv++ } } return vv, nil } func addImm(insn *ifuzz.Insn, imm int8) { if insn.Imm == 0 { insn.Imm = imm return } if insn.Imm2 == 0 { insn.Imm2 = imm return } panic("too many immediates") } func failf(msg string, args ...interface{}) { fmt.Fprintf(os.Stderr, msg+"\n", args...) os.Exit(1) }