github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/compiler/gen.go (about) 1 // Copyright 2017 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 "bytes" 8 "fmt" 9 "reflect" 10 "sort" 11 12 "github.com/google/syzkaller/pkg/ast" 13 "github.com/google/syzkaller/pkg/serializer" 14 "github.com/google/syzkaller/prog" 15 ) 16 17 const sizeUnassigned = ^uint64(0) 18 19 func (comp *compiler) genResources() []*prog.ResourceDesc { 20 var resources []*prog.ResourceDesc 21 for name, n := range comp.resources { 22 if !comp.used[name] { 23 continue 24 } 25 resources = append(resources, comp.genResource(n)) 26 } 27 sort.Slice(resources, func(i, j int) bool { 28 return resources[i].Name < resources[j].Name 29 }) 30 return resources 31 } 32 33 func (comp *compiler) genResource(n *ast.Resource) *prog.ResourceDesc { 34 res := &prog.ResourceDesc{ 35 Name: n.Name.Name, 36 } 37 for n != nil { 38 res.Values = append(genIntArray(n.Values), res.Values...) 39 res.Kind = append([]string{n.Name.Name}, res.Kind...) 40 n = comp.resources[n.Base.Ident] 41 } 42 if len(res.Values) == 0 { 43 res.Values = []uint64{0} 44 } 45 return res 46 } 47 48 func (comp *compiler) collectCallArgSizes() map[string][]uint64 { 49 argPos := make(map[string]ast.Pos) 50 callArgSizes := make(map[string][]uint64) 51 for _, decl := range comp.desc.Nodes { 52 n, ok := decl.(*ast.Call) 53 if !ok { 54 continue 55 } 56 // Figure out number of arguments and their sizes for each syscall. 57 // For example, we may have: 58 // ioctl(fd fd, cmd int32, arg intptr) 59 // ioctl$FOO(fd fd, cmd const[FOO]) 60 // Here we will figure out that ioctl$FOO have 3 args, even that 61 // only 2 are specified and that size of cmd is 4 even that 62 // normally we would assume it's 8 (intptr). 63 argSizes := callArgSizes[n.CallName] 64 for i, arg := range n.Args { 65 if len(argSizes) <= i { 66 argSizes = append(argSizes, comp.ptrSize) 67 } 68 desc, _, _ := comp.getArgsBase(arg.Type, true) 69 typ := comp.genField(arg, comp.ptrSize, prog.DirInOut) 70 // Ignore all types with base (const, flags). We don't have base in syscall args. 71 // Also ignore resources and pointers because fd can be 32-bits and pointer 64-bits, 72 // and then there is no way to fix this. 73 // The only relevant types left is plain int types. 74 if desc != typeInt { 75 continue 76 } 77 if !comp.target.Int64SyscallArgs && typ.Size() > comp.ptrSize { 78 comp.error(arg.Pos, "%v arg %v is larger than pointer size", n.Name.Name, arg.Name.Name) 79 continue 80 } 81 argID := fmt.Sprintf("%v|%v", n.CallName, i) 82 if _, ok := argPos[argID]; !ok { 83 argSizes[i] = typ.Size() 84 argPos[argID] = arg.Pos 85 continue 86 } 87 if argSizes[i] != typ.Size() { 88 comp.error(arg.Pos, "%v arg %v is redeclared with size %v, previously declared with size %v at %v", 89 n.Name.Name, arg.Name.Name, typ.Size(), argSizes[i], argPos[argID]) 90 continue 91 } 92 } 93 callArgSizes[n.CallName] = argSizes 94 } 95 return callArgSizes 96 } 97 98 func (comp *compiler) genSyscalls() []*prog.Syscall { 99 callArgSizes := comp.collectCallArgSizes() 100 var calls []*prog.Syscall 101 for _, decl := range comp.desc.Nodes { 102 if n, ok := decl.(*ast.Call); ok && n.NR != ^uint64(0) { 103 calls = append(calls, comp.genSyscall(n, callArgSizes[n.CallName])) 104 } 105 } 106 // We assign SquashableElem here rather than during pointer type generation 107 // because during pointer generation recursive struct types may not be fully 108 // generated yet, thus ForeachArgType won't observe all types. 109 prog.ForeachTypePost(calls, func(typ prog.Type, ctx *prog.TypeCtx) { 110 if ptr, ok := typ.(*prog.PtrType); ok { 111 ptr.SquashableElem = isSquashableElem(ptr.Elem, ptr.ElemDir) 112 } 113 }) 114 sort.Slice(calls, func(i, j int) bool { 115 return calls[i].Name < calls[j].Name 116 }) 117 return calls 118 } 119 120 func (comp *compiler) genSyscall(n *ast.Call, argSizes []uint64) *prog.Syscall { 121 var ret prog.Type 122 if n.Ret != nil { 123 ret = comp.genType(n.Ret, comp.ptrSize) 124 } 125 var attrs prog.SyscallAttrs 126 descAttrs := comp.parseIntAttrs(callAttrs, n, n.Attrs) 127 for desc, val := range descAttrs { 128 fld := reflect.ValueOf(&attrs).Elem().FieldByName(desc.Name) 129 switch desc.Type { 130 case intAttr: 131 fld.SetUint(val) 132 case flagAttr: 133 fld.SetBool(val != 0) 134 default: 135 panic(fmt.Sprintf("unexpected attrDesc type: %q", desc.Type)) 136 } 137 } 138 fields, _ := comp.genFieldArray(n.Args, argSizes) 139 return &prog.Syscall{ 140 Name: n.Name.Name, 141 CallName: n.CallName, 142 NR: n.NR, 143 MissingArgs: len(argSizes) - len(n.Args), 144 Args: fields, 145 Ret: ret, 146 Attrs: attrs, 147 } 148 } 149 150 type typeProxy struct { 151 typ prog.Type 152 id string 153 ref prog.Ref 154 locations []*prog.Type 155 } 156 157 func (comp *compiler) generateTypes(syscalls []*prog.Syscall) []prog.Type { 158 // Replace all Type's in the descriptions with Ref's 159 // and prepare a sorted array of corresponding real types. 160 proxies := make(map[string]*typeProxy) 161 prog.ForeachTypePost(syscalls, func(typ prog.Type, ctx *prog.TypeCtx) { 162 if _, ok := typ.(prog.Ref); ok { 163 return 164 } 165 if !typ.Varlen() && typ.Size() == sizeUnassigned { 166 panic("unassigned size") 167 } 168 id := typ.Name() 169 switch typ.(type) { 170 case *prog.StructType, *prog.UnionType: 171 // There types can be uniquely identified with the name. 172 default: 173 buf := new(bytes.Buffer) 174 serializer.Write(buf, typ) 175 id = buf.String() 176 } 177 proxy := proxies[id] 178 if proxy == nil { 179 proxy = &typeProxy{ 180 typ: typ, 181 id: id, 182 ref: prog.Ref(len(proxies)), 183 } 184 proxies[id] = proxy 185 } 186 *ctx.Ptr = proxy.ref 187 proxy.locations = append(proxy.locations, ctx.Ptr) 188 }) 189 array := make([]*typeProxy, 0, len(proxies)) 190 for _, proxy := range proxies { 191 array = append(array, proxy) 192 } 193 sort.Slice(array, func(i, j int) bool { 194 return array[i].id < array[j].id 195 }) 196 types := make([]prog.Type, len(array)) 197 for i, proxy := range array { 198 types[i] = proxy.typ 199 for _, loc := range proxy.locations { 200 *loc = prog.Ref(i) 201 } 202 } 203 return types 204 } 205 206 func (comp *compiler) layoutTypes(syscalls []*prog.Syscall) { 207 // Calculate struct/union/array sizes, add padding to structs, mark bitfields. 208 padded := make(map[prog.Type]bool) 209 prog.ForeachTypePost(syscalls, func(typ prog.Type, _ *prog.TypeCtx) { 210 comp.layoutType(typ, padded) 211 }) 212 } 213 214 func (comp *compiler) layoutType(typ prog.Type, padded map[prog.Type]bool) { 215 if padded[typ] { 216 return 217 } 218 switch t := typ.(type) { 219 case *prog.ArrayType: 220 comp.layoutType(t.Elem, padded) 221 comp.layoutArray(t) 222 case *prog.StructType: 223 for _, f := range t.Fields { 224 comp.layoutType(f.Type, padded) 225 } 226 comp.layoutStruct(t) 227 case *prog.UnionType: 228 for _, f := range t.Fields { 229 comp.layoutType(f.Type, padded) 230 } 231 comp.layoutUnion(t) 232 default: 233 return 234 } 235 if !typ.Varlen() && typ.Size() == sizeUnassigned { 236 panic("size unassigned") 237 } 238 padded[typ] = true 239 } 240 241 func (comp *compiler) layoutArray(t *prog.ArrayType) { 242 t.TypeSize = 0 243 if t.Kind == prog.ArrayRangeLen && t.RangeBegin == t.RangeEnd && !t.Elem.Varlen() { 244 t.TypeSize = t.RangeBegin * t.Elem.Size() 245 } 246 } 247 248 func (comp *compiler) layoutUnion(t *prog.UnionType) { 249 t.TypeSize = 0 250 structNode := comp.structs[t.TypeName] 251 if structNode == conditionalFieldWrapper { 252 return 253 } 254 attrs := comp.parseIntAttrs(unionAttrs, structNode, structNode.Attrs) 255 if attrs[attrVarlen] != 0 { 256 return 257 } 258 sizeAttr, hasSize := attrs[attrSize] 259 for i, fld := range t.Fields { 260 sz := fld.Size() 261 if hasSize && sz > sizeAttr { 262 comp.error(structNode.Fields[i].Pos, "union %v has size attribute %v"+ 263 " which is less than field %v size %v", 264 structNode.Name.Name, sizeAttr, fld.Type.Name(), sz) 265 } 266 if t.TypeSize < sz { 267 t.TypeSize = sz 268 } 269 } 270 if hasSize { 271 t.TypeSize = sizeAttr 272 } 273 } 274 275 func (comp *compiler) layoutStruct(t *prog.StructType) { 276 // Add paddings, calculate size, mark bitfields. 277 structNode := comp.structs[t.TypeName] 278 varlen := false 279 for _, f := range t.Fields { 280 if f.Varlen() { 281 varlen = true 282 } 283 } 284 attrs := comp.parseIntAttrs(structAttrs, structNode, structNode.Attrs) 285 t.AlignAttr = attrs[attrAlign] 286 comp.layoutStructFields(t, varlen, attrs[attrPacked] != 0) 287 t.TypeSize = 0 288 if !varlen { 289 var size uint64 290 for i, f := range t.Fields { 291 if i == t.OverlayField { 292 size = 0 293 } 294 size += f.Size() 295 if t.TypeSize < size { 296 t.TypeSize = size 297 } 298 } 299 sizeAttr, hasSize := attrs[attrSize] 300 if hasSize { 301 if t.TypeSize > sizeAttr { 302 comp.error(structNode.Attrs[0].Pos, "struct %v has size attribute %v"+ 303 " which is less than struct size %v", 304 structNode.Name.Name, sizeAttr, t.TypeSize) 305 } 306 if pad := sizeAttr - t.TypeSize; pad != 0 { 307 t.Fields = append(t.Fields, genPad(pad)) 308 } 309 t.TypeSize = sizeAttr 310 } 311 } 312 } 313 314 func (comp *compiler) layoutStructFields(t *prog.StructType, varlen, packed bool) { 315 var newFields []prog.Field 316 overlayField0 := t.OverlayField 317 var structAlign, byteOffset, bitOffset uint64 318 for i, field := range t.Fields { 319 f := field.Type 320 if i == overlayField0 { 321 // We layout fields before overlay and the overlay fields effectively as 2 independent structs. 322 // So if we starting overlay, add any trailign padding/finalize bitfield layout and reset state. 323 newFields = comp.finalizeStructFields(t, newFields, varlen, structAlign, byteOffset, bitOffset) 324 t.OverlayField = len(newFields) // update overlay field index after we added paddings 325 structAlign, byteOffset, bitOffset = 0, 0, 0 326 } 327 fieldAlign := uint64(1) 328 if !packed { 329 fieldAlign = f.Alignment() 330 if structAlign < fieldAlign { 331 structAlign = fieldAlign 332 } 333 } 334 fullBitOffset := byteOffset*8 + bitOffset 335 var fieldOffset uint64 336 337 if f.IsBitfield() { 338 unitAlign := f.UnitSize() 339 if packed { 340 unitAlign = 1 341 } 342 fieldOffset = rounddown(fullBitOffset/8, unitAlign) 343 unitBits := f.UnitSize() * 8 344 occupiedBits := fullBitOffset - fieldOffset*8 345 remainBits := unitBits - occupiedBits 346 347 if remainBits < f.BitfieldLength() { 348 fieldOffset = roundup(roundup(fullBitOffset, 8)/8, unitAlign) 349 fullBitOffset, bitOffset = 0, 0 350 } else if fieldOffset*8 >= fullBitOffset { 351 fullBitOffset, bitOffset = fieldOffset*8, 0 352 } 353 fieldBitOffset := (fullBitOffset - fieldOffset*8) % unitBits 354 setBitfieldOffset(f, fieldBitOffset) 355 } else { 356 fieldOffset = roundup(roundup(fullBitOffset, 8)/8, fieldAlign) 357 bitOffset = 0 358 } 359 if fieldOffset > byteOffset { 360 pad := fieldOffset - byteOffset 361 byteOffset += pad 362 if i != 0 && t.Fields[i-1].IsBitfield() { 363 setBitfieldTypeSize(t.Fields[i-1].Type, pad) 364 if bitOffset >= 8*pad { 365 // The padding is due to bitfields, so consume the bitOffset. 366 bitOffset -= 8 * pad 367 } else if bitOffset >= 8 { 368 // Unclear is this is a bug or not and what to do in this case. 369 // But since we don't have any descriptions that trigger this, 370 // let's just guard with the panic. 371 panic(fmt.Sprintf("bad bitOffset: %v.%v pad=%v bitOffset=%v", 372 t.Name(), field.Name, pad, bitOffset)) 373 } 374 } else { 375 newFields = append(newFields, genPad(pad)) 376 } 377 } 378 if f.IsBitfield() { 379 if byteOffset > fieldOffset { 380 unitOffset := byteOffset - fieldOffset 381 setBitfieldUnitOffset(f, unitOffset) 382 } 383 } 384 newFields = append(newFields, field) 385 if f.IsBitfield() { 386 bitOffset += f.BitfieldLength() 387 } else if !f.Varlen() { 388 // Increase offset if the current field except when it's 389 // the last field in a struct and has variable length. 390 byteOffset += f.Size() 391 } 392 } 393 t.Fields = comp.finalizeStructFields(t, newFields, varlen, structAlign, byteOffset, bitOffset) 394 } 395 396 func (comp *compiler) finalizeStructFields(t *prog.StructType, fields []prog.Field, varlen bool, 397 structAlign, byteOffset, bitOffset uint64) []prog.Field { 398 if bitOffset != 0 { 399 pad := roundup(bitOffset, 8) / 8 400 byteOffset += pad 401 i := len(fields) 402 if i != 0 && fields[i-1].IsBitfield() { 403 setBitfieldTypeSize(fields[i-1].Type, pad) 404 } else { 405 fields = append(fields, genPad(pad)) 406 } 407 } 408 409 if t.AlignAttr != 0 { 410 structAlign = t.AlignAttr 411 } 412 if !varlen && structAlign != 0 && byteOffset%structAlign != 0 { 413 pad := structAlign - byteOffset%structAlign 414 fields = append(fields, genPad(pad)) 415 } 416 return fields 417 } 418 419 func roundup(v, a uint64) uint64 { 420 return rounddown(v+a-1, a) 421 } 422 423 func rounddown(v, a uint64) uint64 { 424 if (a & (a - 1)) != 0 { 425 panic(fmt.Sprintf("rounddown(%v)", a)) 426 } 427 return v & ^(a - 1) 428 } 429 430 func bitfieldFields(t0 prog.Type) (*uint64, *uint64, *uint64) { 431 switch t := t0.(type) { 432 case *prog.IntType: 433 return &t.TypeSize, &t.BitfieldOff, &t.BitfieldUnitOff 434 case *prog.ConstType: 435 return &t.TypeSize, &t.BitfieldOff, &t.BitfieldUnitOff 436 case *prog.LenType: 437 return &t.TypeSize, &t.BitfieldOff, &t.BitfieldUnitOff 438 case *prog.FlagsType: 439 return &t.TypeSize, &t.BitfieldOff, &t.BitfieldUnitOff 440 case *prog.ProcType: 441 return &t.TypeSize, &t.BitfieldOff, &t.BitfieldUnitOff 442 default: 443 panic(fmt.Sprintf("type %#v can't be a bitfield", t)) 444 } 445 } 446 447 func setBitfieldTypeSize(t prog.Type, v uint64) { 448 p, _, _ := bitfieldFields(t) 449 *p = v 450 } 451 452 func setBitfieldOffset(t prog.Type, v uint64) { 453 _, p, _ := bitfieldFields(t) 454 *p = v 455 } 456 457 func setBitfieldUnitOffset(t prog.Type, v uint64) { 458 _, _, p := bitfieldFields(t) 459 *p = v 460 } 461 462 func genPad(size uint64) prog.Field { 463 return prog.Field{ 464 Type: &prog.ConstType{ 465 IntTypeCommon: genIntCommon(genCommon("pad", size, false), 0, false), 466 IsPad: true, 467 }, 468 } 469 } 470 471 func (comp *compiler) genFieldArray(fields []*ast.Field, argSizes []uint64) ([]prog.Field, int) { 472 outOverlay := -1 473 for i, f := range fields { 474 intAttrs := comp.parseIntAttrs(structFieldAttrs, f, f.Attrs) 475 if intAttrs[attrOutOverlay] > 0 { 476 outOverlay = i 477 } 478 } 479 var res []prog.Field 480 for i, f := range fields { 481 overlayDir := prog.DirInOut 482 if outOverlay != -1 { 483 overlayDir = prog.DirIn 484 if i >= outOverlay { 485 overlayDir = prog.DirOut 486 } 487 } 488 res = append(res, comp.genField(f, argSizes[i], overlayDir)) 489 } 490 return res, outOverlay 491 } 492 493 func (comp *compiler) genFieldDir(attrs map[*attrDesc]uint64) (prog.Dir, bool) { 494 switch { 495 case attrs[attrIn] != 0: 496 return prog.DirIn, true 497 case attrs[attrOut] != 0: 498 return prog.DirOut, true 499 case attrs[attrInOut] != 0: 500 return prog.DirInOut, true 501 default: 502 return prog.DirIn, false 503 } 504 } 505 506 func (comp *compiler) genField(f *ast.Field, argSize uint64, overlayDir prog.Dir) prog.Field { 507 intAttrs, exprAttrs := comp.parseAttrs(structFieldAttrs, f, f.Attrs) 508 dir, hasDir := overlayDir, true 509 if overlayDir == prog.DirInOut { 510 dir, hasDir = comp.genFieldDir(intAttrs) 511 } 512 return prog.Field{ 513 Name: f.Name.Name, 514 Type: comp.genType(f.Type, argSize), 515 HasDirection: hasDir, 516 Direction: dir, 517 Condition: exprAttrs[attrIf], 518 } 519 } 520 521 var conditionalFieldWrapper = &ast.Struct{} 522 523 // For structs, we wrap conditional fields in anonymous unions with a @void field. 524 func (comp *compiler) wrapConditionalField(name string, field prog.Field) prog.Field { 525 common := genCommon(fmt.Sprintf("_%s_%s_wrapper", name, field.Name), sizeUnassigned, false) 526 common.IsVarlen = true 527 common.TypeAlign = field.Type.Alignment() 528 529 // Fake the corresponding ast.Struct. 530 comp.structs[common.TypeName] = conditionalFieldWrapper 531 532 voidBase := genIntCommon(genCommon("void", sizeUnassigned, false), 0, false) 533 voidType := typeVoid.Gen(comp, nil, nil, voidBase) 534 535 var newCondition prog.Expression 536 if field.Condition != nil { 537 newCondition = field.Condition.Clone() 538 // Prepend "parent:". 539 newCondition.ForEachValue(func(val *prog.Value) { 540 if len(val.Path) == 0 { 541 return 542 } 543 if val.Path[0] == prog.ParentRef { 544 val.Path = append([]string{prog.ParentRef}, val.Path...) 545 } else { 546 // Single "parent:" would not change anything. 547 val.Path = append([]string{prog.ParentRef, prog.ParentRef}, val.Path...) 548 } 549 }) 550 } 551 552 return prog.Field{ 553 Name: field.Name, 554 Type: &prog.UnionType{ 555 TypeCommon: common, 556 Fields: []prog.Field{ 557 { 558 Name: "value", 559 Type: field.Type, 560 HasDirection: field.HasDirection, 561 Direction: field.Direction, 562 Condition: newCondition, 563 }, 564 { 565 Name: "void", 566 Type: voidType, 567 Condition: &prog.BinaryExpression{ 568 Operator: prog.OperatorCompareEq, 569 Left: newCondition, 570 Right: &prog.Value{Value: 0x0, Path: nil}, 571 }, 572 }, 573 }, 574 }, 575 } 576 } 577 578 func (comp *compiler) genType(t *ast.Type, argSize uint64) prog.Type { 579 desc, args, base := comp.getArgsBase(t, argSize != 0) 580 if desc.Gen == nil { 581 panic(fmt.Sprintf("no gen for %v %#v", t.Ident, t)) 582 } 583 if argSize != 0 { 584 // Now that we know a more precise size, patch the type. 585 // This is somewhat hacky. Ideally we figure out the size earlier, 586 // store it somewhere and use during generation of the arg base type. 587 base.TypeSize = argSize 588 if desc.CheckConsts != nil { 589 desc.CheckConsts(comp, t, args, base) 590 } 591 } 592 base.IsVarlen = desc.Varlen != nil && desc.Varlen(comp, t, args) 593 return desc.Gen(comp, t, args, base) 594 } 595 596 const valueIdent = "value" 597 598 var binaryOperatorMap = map[ast.Operator]prog.BinaryOperator{ 599 ast.OperatorCompareEq: prog.OperatorCompareEq, 600 ast.OperatorCompareNeq: prog.OperatorCompareNeq, 601 ast.OperatorBinaryAnd: prog.OperatorBinaryAnd, 602 } 603 604 func (comp *compiler) genExpression(t *ast.Type) prog.Expression { 605 if binary := t.Expression; binary != nil { 606 operator, ok := binaryOperatorMap[binary.Operator] 607 if !ok { 608 comp.error(binary.Pos, "unknown binary operator") 609 return nil 610 } 611 return &prog.BinaryExpression{ 612 Operator: operator, 613 Left: comp.genExpression(binary.Left), 614 Right: comp.genExpression(binary.Right), 615 } 616 } else { 617 return comp.genValue(t) 618 } 619 } 620 621 func (comp *compiler) genValue(val *ast.Type) *prog.Value { 622 if val.Ident == valueIdent { 623 if len(val.Args) != 1 { 624 comp.error(val.Pos, "value reference must have only one argument") 625 return nil 626 } 627 arg := val.Args[0] 628 if arg.Args != nil { 629 comp.error(val.Pos, "value aguments must not have any further arguments") 630 return nil 631 } 632 path := []string{arg.Ident} 633 for _, elem := range arg.Colon { 634 if elem.Args != nil { 635 comp.error(arg.Pos, "value path elements must not have any attributes") 636 return nil 637 } 638 path = append(path, elem.Ident) 639 } 640 return &prog.Value{Path: path} 641 } 642 if val.Expression != nil || val.HasString { 643 comp.error(val.Pos, "the token must be either an integer or an identifier") 644 return nil 645 } 646 if len(val.Args) != 0 { 647 comp.error(val.Pos, "consts in expressions must not have any arguments") 648 return nil 649 } 650 return &prog.Value{ 651 Value: val.Value, 652 } 653 } 654 655 func genCommon(name string, size uint64, opt bool) prog.TypeCommon { 656 return prog.TypeCommon{ 657 TypeName: name, 658 TypeSize: size, 659 IsOptional: opt, 660 } 661 } 662 663 func genIntCommon(com prog.TypeCommon, bitLen uint64, bigEndian bool) prog.IntTypeCommon { 664 bf := prog.FormatNative 665 if bigEndian { 666 bf = prog.FormatBigEndian 667 } 668 bfUnit := uint64(0) 669 if bitLen != 0 { 670 bfUnit = com.TypeSize 671 com.TypeSize = 0 672 } 673 return prog.IntTypeCommon{ 674 TypeCommon: com, 675 ArgFormat: bf, 676 BitfieldLen: bitLen, 677 BitfieldUnit: bfUnit, 678 } 679 } 680 681 func genIntArray(a []*ast.Int) []uint64 { 682 r := make([]uint64, len(a)) 683 for i, v := range a { 684 r[i] = v.Value 685 } 686 return r 687 } 688 689 func genStrArray(a []*ast.String) []string { 690 r := make([]string, len(a)) 691 for i, v := range a { 692 r[i] = v.Value 693 } 694 return r 695 }