// Copyright 2018 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. package xeddata import ( "bytes" "errors" "fmt" "io" "io/ioutil" "os" "path/filepath" "regexp" "strings" ) // Types for XED enum-like constants. type ( // OperandSizeMode describes operand size mode (66H prefix). OperandSizeMode int // AddressSizeMode describes address size mode (67H prefix). AddressSizeMode int // CPUMode describes availability in certain CPU mode. CPUMode int ) // Possible operand size modes. XED calls it OSZ. const ( OpSize16 OperandSizeMode = iota OpSize32 OpSize64 ) // Possible address size modes. XED calls it ASZ. const ( AddrSize16 AddressSizeMode = iota AddrSize32 AddrSize64 ) // Possible CPU modes. XED calls it MODE. const ( Mode16 CPUMode = iota Mode32 Mode64 ) var sizeStrings = [...]string{"16", "32", "64"} // sizeString maps size enumeration value to it's string representation. func sizeString(size int) string { // Panic more gracefully than with "index out of range". // If client code specified invalid size enumeration, // this is programming error that should be fixed, not "handled". if size >= len(sizeStrings) { panic(fmt.Sprintf("illegal size value: %d", size)) } return sizeStrings[size] } // String returns osz bit size string. Panics on illegal enumerations. func (osz OperandSizeMode) String() string { return sizeString(int(osz)) } // String returns asz bit size string. Panics on illegal enumerations. func (asz AddressSizeMode) String() string { return sizeString(int(asz)) } // Database holds information that is required to // properly handle XED datafiles. type Database struct { widths map[string]*width // all-widths.txt states map[string]string // all-state.txt xtypes map[string]*xtype // all-element-types.txt } // width is a "all-width.txt" record. type width struct { // Default xtype name (examples: int, i8, f32). xtype string // 16, 32 and 64 bit sizes (all may have same value). sizes [3]string } // xtype is a "all-element-type.txt" record. type xtype struct { // Name is xtype identifier. name string // baseType specifies xtype base type. // See "all-element-type-base.txt". baseType string // Size is an operand data size in bits. size string } // NewDatabase returns Database that loads everything // it can find in xedPath. // Missing lookup file is not an error, but error during // parsing of found file is. // // Lookup: // // "$xedPath/all-state.txt" => db.LoadStates() // "$xedPath/all-widths.txt" => db.LoadWidths() // "$xedPath/all-element-types.txt" => db.LoadXtypes() // // $xedPath is the interpolated value of function argument. // // The call NewDatabase("") is valid and returns empty database. // Load methods can be used to read lookup files one-by-one. func NewDatabase(xedPath string) (*Database, error) { var db Database stat, err := os.Stat(xedPath) if err != nil { return nil, err } if !stat.IsDir() { return nil, errors.New("xedPath is not directory") } states, err := os.Open(filepath.Join(xedPath, "all-state.txt")) if err == nil { err = db.LoadStates(states) if err != nil { return &db, err } } widths, err := os.Open(filepath.Join(xedPath, "all-widths.txt")) if err == nil { err = db.LoadWidths(widths) if err != nil { return &db, err } } xtypes, err := os.Open(filepath.Join(xedPath, "all-element-types.txt")) if err == nil { err = db.LoadXtypes(xtypes) if err != nil { return &db, err } } return &db, nil } // LoadWidths reads XED widths definitions from r and updates db. // "widths" are 16/32/64 bit mode type sizes. // See "$XED/obj/dgen/all-widths.txt". func (db *Database) LoadWidths(r io.Reader) error { var err error db.widths, err = parseWidths(r) return err } // LoadStates reads XED states definitions from r and updates db. // "states" are simple macro substitutions without parameters. // See "$XED/obj/dgen/all-state.txt". func (db *Database) LoadStates(r io.Reader) error { var err error db.states, err = parseStates(r) return err } // LoadXtypes reads XED xtypes definitions from r and updates db. // "xtypes" are low-level XED type names. // See "$XED/obj/dgen/all-element-types.txt". // See "$XED/obj/dgen/all-element-type-base.txt". func (db *Database) LoadXtypes(r io.Reader) error { var err error db.xtypes, err = parseXtypes(r) return err } // WidthSize translates width string to size string using desired // SizeMode m. For some widths output is the same for any valid value of m. func (db *Database) WidthSize(width string, m OperandSizeMode) string { info := db.widths[width] if info == nil { return "" } return info.sizes[m] } func parseWidths(r io.Reader) (map[string]*width, error) { data, err := ioutil.ReadAll(r) if err != nil { return nil, fmt.Errorf("parse widths: %v", err) } // Lines have two forms: // 1. name xtype size [# comment] // 2. name xtype size16, size32, size64 [# comment] reLine := regexp.MustCompile(`(^\s*\w+\s+\w+\s+\w+\s+\w+\s+\w+)|(^\s*\w+\s+\w+\s+\w+)`) widths := make(map[string]*width, 128) for _, l := range bytes.Split(data, []byte("\n")) { var name, xtype, size16, size32, size64 string if m := reLine.FindSubmatch(l); m != nil { var f [][]byte if m[1] != nil { f = bytes.Fields(m[1]) } else { f = bytes.Fields(m[2]) } name = string(f[0]) xtype = string(f[1]) if len(f) > 3 { size16 = string(f[2]) size32 = string(f[3]) size64 = string(f[4]) } else { size16 = string(f[2]) size32 = size16 size64 = size16 } } if name != "" { widths[name] = &width{ xtype: xtype, sizes: [3]string{size16, size32, size64}, } } } return widths, nil } func parseStates(r io.Reader) (map[string]string, error) { data, err := ioutil.ReadAll(r) if err != nil { return nil, fmt.Errorf("parse states: %v", err) } // Lines have form of "name ...replacements [# comment]". // This regexp captures the name and everything until line end or comment. lineRE := regexp.MustCompile(`^\s*(\w+)\s+([^#]+)`) states := make(map[string]string, 128) for _, l := range strings.Split(string(data), "\n") { if m := lineRE.FindStringSubmatch(l); m != nil { name, replacements := m[1], m[2] states[name] = strings.TrimSpace(replacements) } } return states, nil } func parseXtypes(r io.Reader) (map[string]*xtype, error) { data, err := ioutil.ReadAll(r) if err != nil { return nil, fmt.Errorf("parse xtypes: %v", err) } // Lines have form of "name baseType size [# comment]". lineRE := regexp.MustCompile(`^\s*(\w+)\s+(\w+)\s*(\d+)`) xtypes := make(map[string]*xtype) for _, l := range strings.Split(string(data), "\n") { if m := lineRE.FindStringSubmatch(l); m != nil { name, baseType, size := m[1], m[2], m[3] xtypes[name] = &xtype{ name: name, baseType: baseType, size: size, } } } return xtypes, nil }