package prog import ( "fmt" ) type anyTypes struct { union *UnionType array *ArrayType blob *BufferType ptrPtr *PtrType ptr64 *PtrType res16 *ResourceType res32 *ResourceType res64 *ResourceType resdec *ResourceType reshex *ResourceType resoct *ResourceType } // This generates type descriptions for: // // resource ANYRES16[int16]: 0xffffffffffffffff, 0 // resource ANYRES32[int32]: 0xffffffffffffffff, 0 // resource ANYRES64[int64]: 0xffffffffffffffff, 0 // ANY [ // bin array[int8] // ptr ptr[in, array[ANY], opt] // ptr64 ptr64[in, array[ANY], opt] // res16 ANYRES16 // res32 ANYRES32 // res64 ANYRES64 // resdec fmt[dec, ANYRES64] // reshex fmt[hex, ANYRES64] // resoct fmt[oct, ANYRES64] // ] [varlen] func initAnyTypes(target *Target) { target.any.union = &UnionType{ FldName: "ANYUNION", } target.any.array = &ArrayType{ TypeCommon: TypeCommon{ TypeName: "ANYARRAY", FldName: "ANYARRAY", IsVarlen: true, }, Type: target.any.union, } target.any.ptrPtr = &PtrType{ TypeCommon: TypeCommon{ TypeName: "ptr", FldName: "ANYPTR", TypeSize: target.PtrSize, IsOptional: true, }, Type: target.any.array, } target.any.ptr64 = &PtrType{ TypeCommon: TypeCommon{ TypeName: "ptr64", FldName: "ANYPTR64", TypeSize: 8, IsOptional: true, }, Type: target.any.array, } target.any.blob = &BufferType{ TypeCommon: TypeCommon{ TypeName: "ANYBLOB", FldName: "ANYBLOB", IsVarlen: true, }, } createResource := func(name, base string, bf BinaryFormat, size uint64) *ResourceType { return &ResourceType{ TypeCommon: TypeCommon{ TypeName: name, FldName: name, ArgDir: DirIn, TypeSize: size, IsOptional: true, }, ArgFormat: bf, Desc: &ResourceDesc{ Name: name, Kind: []string{name}, Values: []uint64{^uint64(0), 0}, Type: &IntType{ IntTypeCommon: IntTypeCommon{ TypeCommon: TypeCommon{ TypeName: base, TypeSize: size, }, }, }, }, } } target.any.res16 = createResource("ANYRES16", "int16", FormatNative, 2) target.any.res32 = createResource("ANYRES32", "int32", FormatNative, 4) target.any.res64 = createResource("ANYRES64", "int64", FormatNative, 8) target.any.resdec = createResource("ANYRESDEC", "int64", FormatStrDec, 20) target.any.reshex = createResource("ANYRESHEX", "int64", FormatStrHex, 18) target.any.resoct = createResource("ANYRESOCT", "int64", FormatStrOct, 23) target.any.union.StructDesc = &StructDesc{ TypeCommon: TypeCommon{ TypeName: "ANYUNION", FldName: "ANYUNION", IsVarlen: true, ArgDir: DirIn, }, Fields: []Type{ target.any.blob, target.any.ptrPtr, target.any.ptr64, target.any.res16, target.any.res32, target.any.res64, target.any.resdec, target.any.reshex, target.any.resoct, }, } } func (target *Target) makeAnyPtrType(size uint64, field string) *PtrType { // We need to make a copy because type holds field name, // and field names are used as len target. var typ PtrType if size == target.PtrSize { typ = *target.any.ptrPtr } else if size == 8 { typ = *target.any.ptr64 } else { panic(fmt.Sprintf("bad pointer size %v", size)) } typ.TypeSize = size if field != "" { typ.FldName = field } return &typ } func (target *Target) isAnyPtr(typ Type) bool { ptr, ok := typ.(*PtrType) return ok && ptr.Type == target.any.array } func (p *Prog) complexPtrs() (res []*PointerArg) { for _, c := range p.Calls { ForeachArg(c, func(arg Arg, ctx *ArgCtx) { if ptrArg, ok := arg.(*PointerArg); ok && p.Target.isComplexPtr(ptrArg) { res = append(res, ptrArg) ctx.Stop = true } }) } return } func (target *Target) isComplexPtr(arg *PointerArg) bool { if arg.Res == nil || arg.Type().Dir() != DirIn { return false } if target.isAnyPtr(arg.Type()) { return true } res := false ForeachSubArg(arg.Res, func(a1 Arg, ctx *ArgCtx) { switch typ := a1.Type().(type) { case *StructType: if typ.Varlen() { res = true ctx.Stop = true } case *UnionType: if typ.Varlen() && len(typ.Fields) > 5 { res = true ctx.Stop = true } case *PtrType: if a1 != arg { ctx.Stop = true } } }) return res } func (target *Target) CallContainsAny(c *Call) (res bool) { ForeachArg(c, func(arg Arg, ctx *ArgCtx) { if target.isAnyPtr(arg.Type()) { res = true ctx.Stop = true } }) return } func (target *Target) ArgContainsAny(arg0 Arg) (res bool) { ForeachSubArg(arg0, func(arg Arg, ctx *ArgCtx) { if target.isAnyPtr(arg.Type()) { res = true ctx.Stop = true } }) return } func (target *Target) squashPtr(arg *PointerArg, preserveField bool) { if arg.Res == nil || arg.VmaSize != 0 { panic("bad ptr arg") } res0 := arg.Res size0 := res0.Size() var elems []Arg target.squashPtrImpl(arg.Res, &elems) field := "" if preserveField { field = arg.Type().FieldName() } arg.typ = target.makeAnyPtrType(arg.Type().Size(), field) arg.Res = MakeGroupArg(arg.typ.(*PtrType).Type, elems) if size := arg.Res.Size(); size != size0 { panic(fmt.Sprintf("squash changed size %v->%v for %v", size0, size, res0.Type())) } } func (target *Target) squashPtrImpl(a Arg, elems *[]Arg) { if a.Type().BitfieldLength() != 0 { panic("bitfield in squash") } var pad uint64 switch arg := a.(type) { case *ConstArg: target.squashConst(arg, elems) case *ResultArg: target.squashResult(arg, elems) case *PointerArg: if arg.Res != nil { target.squashPtr(arg, false) *elems = append(*elems, MakeUnionArg(target.any.union, arg)) } else { elem := target.ensureDataElem(elems) addr := target.PhysicalAddr(arg) for i := uint64(0); i < arg.Size(); i++ { elem.data = append(elem.Data(), byte(addr)) addr >>= 8 } } case *UnionArg: if !arg.Type().Varlen() { pad = arg.Size() - arg.Option.Size() } target.squashPtrImpl(arg.Option, elems) case *DataArg: if arg.Type().Dir() == DirOut { pad = arg.Size() } else { elem := target.ensureDataElem(elems) elem.data = append(elem.Data(), arg.Data()...) } case *GroupArg: target.squashGroup(arg, elems) default: panic("bad arg kind") } if pad != 0 { elem := target.ensureDataElem(elems) elem.data = append(elem.Data(), make([]byte, pad)...) } } func (target *Target) squashConst(arg *ConstArg, elems *[]Arg) { if IsPad(arg.Type()) { elem := target.ensureDataElem(elems) elem.data = append(elem.Data(), make([]byte, arg.Size())...) return } v, bf := target.squashedValue(arg) var data []byte switch bf { case FormatNative: for i := uint64(0); i < arg.Size(); i++ { data = append(data, byte(v)) v >>= 8 } case FormatStrDec: data = []byte(fmt.Sprintf("%020v", v)) case FormatStrHex: data = []byte(fmt.Sprintf("0x%016x", v)) case FormatStrOct: data = []byte(fmt.Sprintf("%023o", v)) default: panic(fmt.Sprintf("unknown binary format: %v", bf)) } if uint64(len(data)) != arg.Size() { panic("squashed value of wrong size") } elem := target.ensureDataElem(elems) elem.data = append(elem.Data(), data...) } func (target *Target) squashResult(arg *ResultArg, elems *[]Arg) { switch arg.Type().Format() { case FormatNative, FormatBigEndian: switch arg.Size() { case 2: arg.typ = target.any.res16 case 4: arg.typ = target.any.res32 case 8: arg.typ = target.any.res64 default: panic("bad size") } case FormatStrDec: arg.typ = target.any.resdec case FormatStrHex: arg.typ = target.any.reshex case FormatStrOct: arg.typ = target.any.resoct default: panic("bad") } *elems = append(*elems, MakeUnionArg(target.any.union, arg)) } func (target *Target) squashGroup(arg *GroupArg, elems *[]Arg) { var pad uint64 if typ, ok := arg.Type().(*StructType); ok && typ.Varlen() && typ.AlignAttr != 0 { var fieldsSize uint64 for _, fld := range arg.Inner { if !fld.Type().BitfieldMiddle() { fieldsSize += fld.Size() } } if fieldsSize%typ.AlignAttr != 0 { pad = typ.AlignAttr - fieldsSize%typ.AlignAttr } } var bitfield uint64 for _, fld := range arg.Inner { // Squash bitfields separately. if bfLen := fld.Type().BitfieldLength(); bfLen != 0 { bfOff := fld.Type().BitfieldOffset() // Note: we can have a ResultArg here as well, // but it is unsupported at the moment. v, bf := target.squashedValue(fld.(*ConstArg)) if bf != FormatNative { panic(fmt.Sprintf("bitfield has bad format %v", bf)) } bitfield |= (v & ((1 << bfLen) - 1)) << bfOff if !fld.Type().BitfieldMiddle() { elem := target.ensureDataElem(elems) for i := uint64(0); i < fld.Size(); i++ { elem.data = append(elem.Data(), byte(bitfield)) bitfield >>= 8 } bitfield = 0 } continue } target.squashPtrImpl(fld, elems) } if pad != 0 { elem := target.ensureDataElem(elems) elem.data = append(elem.Data(), make([]byte, pad)...) } } func (target *Target) squashedValue(arg *ConstArg) (uint64, BinaryFormat) { bf := arg.Type().Format() if _, ok := arg.Type().(*CsumType); ok { // We can't compute value for the checksum here, // but at least leave something recognizable by hints code. // TODO: hints code won't recognize this, because it won't find // the const in any arg. We either need to put this const as // actual csum arg value, or special case it in hints. return 0xabcdef1234567890, FormatNative } // Note: we need a constant value, but it depends on pid for proc. v, _ := arg.Value() if bf == FormatBigEndian { bf = FormatNative switch arg.Size() { case 2: v = uint64(swap16(uint16(v))) case 4: v = uint64(swap32(uint32(v))) case 8: v = swap64(v) default: panic(fmt.Sprintf("bad const size %v", arg.Size())) } } return v, bf } func (target *Target) ensureDataElem(elems *[]Arg) *DataArg { if len(*elems) == 0 { res := MakeDataArg(target.any.blob, nil) *elems = append(*elems, MakeUnionArg(target.any.union, res)) return res } res, ok := (*elems)[len(*elems)-1].(*UnionArg).Option.(*DataArg) if !ok { res = MakeDataArg(target.any.blob, nil) *elems = append(*elems, MakeUnionArg(target.any.union, res)) } return res }