github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/compiler/const_file.go (about) 1 // Copyright 2020 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package compiler 5 6 import ( 7 "bufio" 8 "bytes" 9 "fmt" 10 "os" 11 "path/filepath" 12 "regexp" 13 "sort" 14 "strconv" 15 "strings" 16 17 "github.com/google/syzkaller/pkg/ast" 18 ) 19 20 // ConstFile serializes/deserializes .const files. 21 type ConstFile struct { 22 arches map[string]bool 23 m map[string]constVal 24 } 25 26 type constVal struct { 27 name string 28 vals map[string]uint64 // arch -> value 29 } 30 31 const undefined = "???" 32 33 func NewConstFile() *ConstFile { 34 return &ConstFile{ 35 arches: make(map[string]bool), 36 m: make(map[string]constVal), 37 } 38 } 39 40 func (cf *ConstFile) AddArch(arch string, consts map[string]uint64, undeclared map[string]bool) error { 41 cf.arches[arch] = true 42 for name, val := range consts { 43 if err := cf.addConst(arch, name, val, true); err != nil { 44 return err 45 } 46 } 47 for name := range undeclared { 48 if err := cf.addConst(arch, name, 0, false); err != nil { 49 return err 50 } 51 } 52 return nil 53 } 54 55 func (cf *ConstFile) addConst(arch, name string, val uint64, declared bool) error { 56 cv := cf.m[name] 57 if cv.vals == nil { 58 cv.name = name 59 cv.vals = make(map[string]uint64) 60 } 61 if val0, declared0 := cv.vals[arch]; declared && declared0 && val != val0 { 62 return fmt.Errorf("const=%v arch=%v has different values: %v[%v] vs %v[%v]", 63 name, arch, val, declared, val0, declared0) 64 } 65 if declared { 66 cv.vals[arch] = val 67 } 68 cf.m[name] = cv 69 return nil 70 } 71 72 func (cf *ConstFile) Arch(arch string) map[string]uint64 { 73 if cf == nil { 74 return nil 75 } 76 m := make(map[string]uint64) 77 for name, cv := range cf.m { 78 if v, ok := cv.vals[arch]; ok { 79 m[name] = v 80 } 81 } 82 return m 83 } 84 85 func (cf *ConstFile) ExistsAny(constName string) bool { 86 return len(cf.m[constName].vals) > 0 87 } 88 89 func (cf *ConstFile) Serialize() []byte { 90 if len(cf.arches) == 0 { 91 return nil 92 } 93 var arches []string 94 for arch := range cf.arches { 95 arches = append(arches, arch) 96 } 97 98 sort.Strings(arches) 99 var consts []constVal 100 for _, cv := range cf.m { 101 consts = append(consts, cv) 102 } 103 sort.Slice(consts, func(i, j int) bool { 104 return consts[i].name < consts[j].name 105 }) 106 buf := new(bytes.Buffer) 107 fmt.Fprintf(buf, "# Code generated by syz-sysgen. DO NOT EDIT.\n") 108 fmt.Fprintf(buf, "arches = %v\n", strings.Join(arches, ", ")) 109 for _, cv := range consts { 110 fmt.Fprintf(buf, "%v = ", cv.name) 111 if len(cv.vals) == 0 { 112 // Undefined for all arches. 113 fmt.Fprintf(buf, "%v\n", undefined) 114 continue 115 } 116 count := make(map[uint64]int) 117 max, dflt := 0, uint64(0) 118 for _, val := range cv.vals { 119 count[val]++ 120 if count[val] > 1 && (count[val] > max || count[val] == max && val < dflt) { 121 max, dflt = count[val], val 122 } 123 } 124 if max != 0 { 125 // Have a default value. 126 fmt.Fprintf(buf, "%v", dflt) 127 } 128 handled := make([]bool, len(arches)) 129 for i, arch := range arches { 130 val, ok := cv.vals[arch] 131 if ok && (max != 0 && val == dflt) || handled[i] { 132 // Default value or serialized on a previous iteration. 133 continue 134 } 135 if i != 0 || max != 0 { 136 fmt.Fprintf(buf, ", ") 137 } 138 fmt.Fprintf(buf, "%v:", arch) 139 for j := i + 1; j < len(arches); j++ { 140 // Add more arches with the same value. 141 arch1 := arches[j] 142 val1, ok1 := cv.vals[arch1] 143 if ok1 == ok && val1 == val { 144 fmt.Fprintf(buf, "%v:", arch1) 145 handled[j] = true 146 } 147 } 148 if ok { 149 fmt.Fprintf(buf, "%v", val) 150 } else { 151 fmt.Fprint(buf, undefined) 152 } 153 } 154 fmt.Fprintf(buf, "\n") 155 } 156 return buf.Bytes() 157 } 158 159 func DeserializeConstFile(glob string, eh ast.ErrorHandler) *ConstFile { 160 if eh == nil { 161 eh = ast.LoggingHandler 162 } 163 files, err := filepath.Glob(glob) 164 if err != nil { 165 eh(ast.Pos{}, fmt.Sprintf("failed to find const files: %v", err)) 166 return nil 167 } 168 if len(files) == 0 { 169 eh(ast.Pos{}, fmt.Sprintf("no const files matched by glob %q", glob)) 170 return nil 171 } 172 cf := NewConstFile() 173 oldFormat := regexp.MustCompile(`_([a-z0-9]+)\.const$`) 174 for _, f := range files { 175 data, err := os.ReadFile(f) 176 if err != nil { 177 eh(ast.Pos{}, fmt.Sprintf("failed to read const file: %v", err)) 178 return nil 179 } 180 // Support for old per-arch format. 181 // Remove it once we don't have any *_arch.const files anymore. 182 arch := "" 183 if match := oldFormat.FindStringSubmatch(f); match != nil { 184 arch = match[1] 185 } 186 if !cf.deserializeFile(data, filepath.Base(f), arch, eh) { 187 return nil 188 } 189 } 190 return cf 191 } 192 193 func (cf *ConstFile) deserializeFile(data []byte, file, arch string, eh ast.ErrorHandler) bool { 194 pos := ast.Pos{File: file, Line: 1} 195 errf := func(msg string, args ...interface{}) bool { 196 eh(pos, fmt.Sprintf(msg, args...)) 197 return false 198 } 199 s := bufio.NewScanner(bytes.NewReader(data)) 200 var arches []string 201 for ; s.Scan(); pos.Line++ { 202 line := s.Text() 203 if line == "" || line[0] == '#' { 204 continue 205 } 206 eq := strings.IndexByte(line, '=') 207 if eq == -1 { 208 return errf("expect '='") 209 } 210 name, val := strings.TrimSpace(line[:eq]), strings.TrimSpace(line[eq+1:]) 211 if arch != "" { 212 // Old format. 213 if !cf.parseOldConst(arch, name, val, errf) { 214 return false 215 } 216 continue 217 } 218 if arch == "" && len(arches) == 0 { 219 if name != "arches" { 220 return errf("missing arches header") 221 } 222 for _, arch := range strings.Split(val, ",") { 223 arches = append(arches, strings.TrimSpace(arch)) 224 } 225 continue 226 } 227 if !cf.parseConst(arches, name, val, errf) { 228 return false 229 } 230 } 231 if err := s.Err(); err != nil { 232 return errf("failed to parse: %v", err) 233 } 234 return true 235 } 236 237 type errft func(msg string, args ...interface{}) bool 238 239 func (cf *ConstFile) parseConst(arches []string, name, line string, errf errft) bool { 240 var dflt map[string]uint64 241 for _, pair := range strings.Split(line, ",") { 242 fields := strings.Split(pair, ":") 243 if len(fields) == 1 { 244 // Default value. 245 if dflt != nil { 246 return errf("duplicate default value") 247 } 248 dflt = make(map[string]uint64) 249 valStr := strings.TrimSpace(fields[0]) 250 if valStr == undefined { 251 continue 252 } 253 val, err := strconv.ParseUint(valStr, 0, 64) 254 if err != nil { 255 return errf("failed to parse int: %v", err) 256 } 257 for _, arch := range arches { 258 dflt[arch] = val 259 } 260 continue 261 } 262 if len(fields) < 2 { 263 return errf("bad value: %v", pair) 264 } 265 valStr := strings.TrimSpace(fields[len(fields)-1]) 266 defined := valStr != undefined 267 var val uint64 268 if defined { 269 var err error 270 if val, err = strconv.ParseUint(valStr, 0, 64); err != nil { 271 return errf("failed to parse int: %v", err) 272 } 273 } 274 for _, arch := range fields[:len(fields)-1] { 275 arch = strings.TrimSpace(arch) 276 delete(dflt, arch) 277 if err := cf.addConst(arch, name, val, defined); err != nil { 278 return errf("%v", err) 279 } 280 } 281 } 282 for arch, val := range dflt { 283 if err := cf.addConst(arch, name, val, true); err != nil { 284 return errf("%v", err) 285 } 286 } 287 return true 288 } 289 290 func (cf *ConstFile) parseOldConst(arch, name, line string, errf errft) bool { 291 val, err := strconv.ParseUint(strings.TrimSpace(line), 0, 64) 292 if err != nil { 293 return errf("failed to parse int: %v", err) 294 } 295 if err := cf.addConst(arch, name, val, true); err != nil { 296 return errf("%v", err) 297 } 298 return true 299 }