golang.org/x/arch@v0.17.0/x86/xeddata/database.go (about) 1 // Copyright 2018 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 package xeddata 6 7 import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "io" 12 "io/ioutil" 13 "os" 14 "path/filepath" 15 "regexp" 16 "strings" 17 ) 18 19 // Types for XED enum-like constants. 20 type ( 21 // OperandSizeMode describes operand size mode (66H prefix). 22 OperandSizeMode int 23 24 // AddressSizeMode describes address size mode (67H prefix). 25 AddressSizeMode int 26 27 // CPUMode describes availability in certain CPU mode. 28 CPUMode int 29 ) 30 31 // Possible operand size modes. XED calls it OSZ. 32 const ( 33 OpSize16 OperandSizeMode = iota 34 OpSize32 35 OpSize64 36 ) 37 38 // Possible address size modes. XED calls it ASZ. 39 const ( 40 AddrSize16 AddressSizeMode = iota 41 AddrSize32 42 AddrSize64 43 ) 44 45 // Possible CPU modes. XED calls it MODE. 46 const ( 47 Mode16 CPUMode = iota 48 Mode32 49 Mode64 50 ) 51 52 var sizeStrings = [...]string{"16", "32", "64"} 53 54 // sizeString maps size enumeration value to it's string representation. 55 func sizeString(size int) string { 56 // Panic more gracefully than with "index out of range". 57 // If client code specified invalid size enumeration, 58 // this is programming error that should be fixed, not "handled". 59 if size >= len(sizeStrings) { 60 panic(fmt.Sprintf("illegal size value: %d", size)) 61 } 62 return sizeStrings[size] 63 } 64 65 // String returns osz bit size string. Panics on illegal enumerations. 66 func (osz OperandSizeMode) String() string { return sizeString(int(osz)) } 67 68 // String returns asz bit size string. Panics on illegal enumerations. 69 func (asz AddressSizeMode) String() string { return sizeString(int(asz)) } 70 71 // Database holds information that is required to 72 // properly handle XED datafiles. 73 type Database struct { 74 widths map[string]*width // all-widths.txt 75 states map[string]string // all-state.txt 76 xtypes map[string]*xtype // all-element-types.txt 77 78 // extraWidth is a "all-extra-widths.txt" record. 79 // 80 // It provides a default mapping from an operand type to a width code. 81 // 82 // The key is one of three things: 83 // - "XED_REG_<name>" for a register (e.g., "XED_REG_EAX") 84 // - "<name>()" for a non-terminal (e.g., "GPR32_R()"") 85 // - "<name>" for an immediate const (e.g., "AGEN") 86 extraWidths map[string]string // all-extra-widths.txt 87 } 88 89 // width is a "all-widths.txt" record. 90 type width struct { 91 // Default xtype name (examples: int, i8, f32). 92 xtype string 93 94 // 16, 32 and 64 bit sizes (all may have same value). 95 sizes [3]string 96 } 97 98 // xtype is a "all-element-type.txt" record. 99 type xtype struct { 100 // Name is xtype identifier. 101 name string 102 103 // baseType specifies xtype base type. 104 // See "all-element-type-base.txt". 105 baseType string 106 107 // Size is an operand data size in bits. 108 size string 109 } 110 111 // NewDatabase returns Database that loads everything 112 // it can find in xedPath. 113 // Missing lookup file is not an error, but error during 114 // parsing of found file is. 115 // 116 // Lookup: 117 // 118 // "$xedPath/all-state.txt" => db.LoadStates() 119 // "$xedPath/all-widths.txt" => db.LoadWidths() 120 // "$xedPath/all-element-types.txt" => db.LoadXtypes() 121 // 122 // $xedPath is the interpolated value of function argument. 123 // 124 // The call NewDatabase("") is valid and returns empty database. 125 // Load methods can be used to read lookup files one-by-one. 126 func NewDatabase(xedPath string) (*Database, error) { 127 var db Database 128 129 stat, err := os.Stat(xedPath) 130 if err != nil { 131 return nil, err 132 } 133 if !stat.IsDir() { 134 return nil, errors.New("xedPath is not directory") 135 } 136 137 states, err := os.Open(filepath.Join(xedPath, "all-state.txt")) 138 if err == nil { 139 err = db.LoadStates(states) 140 if err != nil { 141 return &db, err 142 } 143 } 144 145 widths, err := os.Open(filepath.Join(xedPath, "all-widths.txt")) 146 if err == nil { 147 err = db.LoadWidths(widths) 148 if err != nil { 149 return &db, err 150 } 151 } 152 153 extraWidths, err := os.Open(filepath.Join(xedPath, "all-extra-widths.txt")) 154 if err == nil { 155 db.extraWidths, err = parseExtraWidths(extraWidths) 156 if err != nil { 157 return &db, err 158 } 159 } 160 161 xtypes, err := os.Open(filepath.Join(xedPath, "all-element-types.txt")) 162 if err == nil { 163 err = db.LoadXtypes(xtypes) 164 if err != nil { 165 return &db, err 166 } 167 } 168 169 return &db, nil 170 } 171 172 // LoadWidths reads XED widths definitions from r and updates db. 173 // "widths" are 16/32/64 bit mode type sizes. 174 // See "$XED/obj/dgen/all-widths.txt". 175 func (db *Database) LoadWidths(r io.Reader) error { 176 var err error 177 db.widths, err = parseWidths(r) 178 return err 179 } 180 181 // LoadStates reads XED states definitions from r and updates db. 182 // "states" are simple macro substitutions without parameters. 183 // See "$XED/obj/dgen/all-state.txt". 184 func (db *Database) LoadStates(r io.Reader) error { 185 var err error 186 db.states, err = parseStates(r) 187 return err 188 } 189 190 // LoadXtypes reads XED xtypes definitions from r and updates db. 191 // "xtypes" are low-level XED type names. 192 // See "$XED/obj/dgen/all-element-types.txt". 193 // See "$XED/obj/dgen/all-element-type-base.txt". 194 func (db *Database) LoadXtypes(r io.Reader) error { 195 var err error 196 db.xtypes, err = parseXtypes(r) 197 return err 198 } 199 200 // WidthSize translates width string to size string using desired 201 // SizeMode m. For some widths output is the same for any valid value of m. 202 // 203 // The size string may be a decimal number of bytes, like "8". It may of the 204 // form "%dbits" to indicate a bit width. Or in some cases it's "0" for 205 // "unusual" registers. 206 func (db *Database) WidthSize(width string, m OperandSizeMode) string { 207 info := db.widths[width] 208 if info == nil { 209 return "" 210 } 211 return info.sizes[m] 212 } 213 214 func parseWidths(r io.Reader) (map[string]*width, error) { 215 data, err := ioutil.ReadAll(r) 216 if err != nil { 217 return nil, fmt.Errorf("parse widths: %v", err) 218 } 219 220 // Lines have two forms: 221 // 1. name xtype size [# comment] 222 // 2. name xtype size16, size32, size64 [# comment] 223 reLine := regexp.MustCompile(`(^\s*\w+\s+\w+\s+\w+\s+\w+\s+\w+)|(^\s*\w+\s+\w+\s+\w+)`) 224 225 widths := make(map[string]*width, 128) 226 for _, l := range bytes.Split(data, []byte("\n")) { 227 var name, xtype, size16, size32, size64 string 228 229 if m := reLine.FindSubmatch(l); m != nil { 230 var f [][]byte 231 if m[1] != nil { 232 f = bytes.Fields(m[1]) 233 } else { 234 f = bytes.Fields(m[2]) 235 } 236 237 name = string(f[0]) 238 xtype = string(f[1]) 239 if len(f) > 3 { 240 size16 = string(f[2]) 241 size32 = string(f[3]) 242 size64 = string(f[4]) 243 } else { 244 size16 = string(f[2]) 245 size32 = size16 246 size64 = size16 247 } 248 } 249 if name != "" { 250 widths[name] = &width{ 251 xtype: xtype, 252 sizes: [3]string{size16, size32, size64}, 253 } 254 } 255 } 256 257 return widths, nil 258 } 259 260 func parseExtraWidths(r io.Reader) (map[string]string, error) { 261 extraWidths := make(map[string]string) 262 for line, err := range readLines(r) { 263 if err != nil { 264 return nil, err 265 } 266 f := bytes.Fields(line.data) 267 if len(f) != 3 { 268 return nil, fmt.Errorf("want 3 fields, got %d", len(f)) 269 } 270 switch string(f[0]) { 271 default: 272 return nil, fmt.Errorf("unknown extra width type %s", f[0]) 273 case "imm_const": 274 extraWidths[string(f[1])] = string(f[2]) 275 case "reg": 276 extraWidths["XED_REG_"+string(f[1])] = string(f[2]) 277 case "nt": 278 extraWidths[string(f[1])+"()"] = string(f[2]) 279 } 280 } 281 return extraWidths, nil 282 } 283 284 func parseStates(r io.Reader) (map[string]string, error) { 285 data, err := ioutil.ReadAll(r) 286 if err != nil { 287 return nil, fmt.Errorf("parse states: %v", err) 288 } 289 290 // Lines have form of "name ...replacements [# comment]". 291 // This regexp captures the name and everything until line end or comment. 292 lineRE := regexp.MustCompile(`^\s*(\w+)\s+([^#]+)`) 293 294 states := make(map[string]string, 128) 295 for _, l := range strings.Split(string(data), "\n") { 296 if m := lineRE.FindStringSubmatch(l); m != nil { 297 name, replacements := m[1], m[2] 298 states[name] = strings.TrimSpace(replacements) 299 } 300 } 301 302 return states, nil 303 } 304 305 func parseXtypes(r io.Reader) (map[string]*xtype, error) { 306 data, err := ioutil.ReadAll(r) 307 if err != nil { 308 return nil, fmt.Errorf("parse xtypes: %v", err) 309 } 310 311 // Lines have form of "name baseType size [# comment]". 312 lineRE := regexp.MustCompile(`^\s*(\w+)\s+(\w+)\s*(\d+)`) 313 314 xtypes := make(map[string]*xtype) 315 for _, l := range strings.Split(string(data), "\n") { 316 if m := lineRE.FindStringSubmatch(l); m != nil { 317 name, baseType, size := m[1], m[2], m[3] 318 xtypes[name] = &xtype{ 319 name: name, 320 baseType: baseType, 321 size: size, 322 } 323 } 324 } 325 326 return xtypes, nil 327 }