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