Golang程序  |  413行  |  9.94 KB

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
}