| 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 |
| } |