func (a *Arg) serialize(buf io.Writer, vars map[*Arg]int, varSeq *int) { if a == nil { fmt.Fprintf(buf, "nil") return } if len(a.Uses) != 0 { fmt.Fprintf(buf, "<r%v=>", *varSeq) vars[a] = *varSeq *varSeq++ } switch a.Kind { case ArgConst: fmt.Fprintf(buf, "0x%x", a.Val) case ArgResult: id, ok := vars[a.Res] if !ok { panic("no result") } fmt.Fprintf(buf, "r%v", id) if a.OpDiv != 0 { fmt.Fprintf(buf, "/%v", a.OpDiv) } if a.OpAdd != 0 { fmt.Fprintf(buf, "+%v", a.OpAdd) } case ArgPointer: fmt.Fprintf(buf, "&%v=", serializeAddr(a, true)) a.Res.serialize(buf, vars, varSeq) case ArgPageSize: fmt.Fprintf(buf, "%v", serializeAddr(a, false)) case ArgData: fmt.Fprintf(buf, "\"%v\"", hex.EncodeToString(a.Data)) case ArgGroup: var delims []byte switch a.Type.(type) { case *sys.StructType: delims = []byte{'{', '}'} case sys.ArrayType: delims = []byte{'[', ']'} default: panic("unknown group type") } buf.Write([]byte{delims[0]}) for i, a1 := range a.Inner { if a1 != nil && sys.IsPad(a1.Type) { continue } if i != 0 { fmt.Fprintf(buf, ", ") } a1.serialize(buf, vars, varSeq) } buf.Write([]byte{delims[1]}) case ArgUnion: fmt.Fprintf(buf, "@%v=", a.OptionType.Name()) a.Option.serialize(buf, vars, varSeq) default: panic("unknown arg kind") } }
func (p *Prog) Serialize() []byte { /* if err := p.validate(); err != nil { panic("serializing invalid program") } */ buf := new(bytes.Buffer) vars := make(map[*Arg]int) varSeq := 0 for _, c := range p.Calls { if len(c.Ret.Uses) != 0 { fmt.Fprintf(buf, "r%v = ", varSeq) vars[c.Ret] = varSeq varSeq++ } fmt.Fprintf(buf, "%v(", c.Meta.Name) for i, a := range c.Args { if sys.IsPad(a.Type) { continue } if i != 0 { fmt.Fprintf(buf, ", ") } a.serialize(buf, vars, &varSeq) } fmt.Fprintf(buf, ")\n") } return buf.Bytes() }
func assignSizes(args []*Arg) { // Create a map of args and calculate size of the whole struct. argsMap := make(map[string]*Arg) var parentSize uintptr for _, arg := range args { parentSize += arg.Size() if sys.IsPad(arg.Type) { continue } argsMap[arg.Type.Name()] = arg } // Fill in size arguments. for _, arg := range args { if arg = arg.InnerArg(); arg == nil { continue // Pointer to optional len field, no need to fill in value. } if typ, ok := arg.Type.(*sys.LenType); ok { if typ.Buf == "parent" { arg.Val = parentSize continue } buf, ok := argsMap[typ.Buf] if !ok { panic(fmt.Sprintf("len field '%v' references non existent field '%v', argsMap: %+v", typ.Name(), typ.Buf, argsMap)) } *arg = *generateSize(buf.InnerArg(), typ) } } }
func assignSizes(types []sys.Type, args []*Arg) { argsMap := make(map[string]*Arg) typesMap := make(map[string]sys.Type) // Create a map of args and types. for i, typ := range types { if sys.IsPad(typ) { continue } if typ.Name() == "parent" { panic("parent is reserved len name") } innerArg := args[i].InnerArg(typ) innerType := typ.InnerType() if _, ok := argsMap[typ.Name()]; ok { panic(fmt.Sprintf("mutiple args with the same name '%v', types: %+v, args: %+v", typ.Name(), types, args)) } argsMap[typ.Name()] = innerArg typesMap[typ.Name()] = innerType } // Calculate size of the whole struct. var parentSize uintptr for i, typ := range types { parentSize += args[i].Size(typ) } // Fill in size arguments. for i, typ := range types { if lenType, ok := typ.InnerType().(sys.LenType); ok { lenArg := args[i].InnerArg(typ) if lenArg == nil { // Pointer to optional len field, no need to fill in value. continue } if lenType.Buf == "parent" { *lenArg = *constArg(parentSize) continue } arg, ok := argsMap[lenType.Buf] if !ok { panic(fmt.Sprintf("len field '%v' references non existent field '%v', argsMap: %+v, typesMap: %+v", lenType.Name(), lenType.Buf, argsMap, typesMap)) } typ := typesMap[lenType.Buf] *lenArg = *generateSize(typ, arg, lenType) } } }
func (p *Prog) SerializeForExec(pid int) []byte { if err := p.validate(); err != nil { panic(fmt.Errorf("serializing invalid program: %v", err)) } var instrSeq uintptr w := &execContext{args: make(map[*Arg]*argInfo)} for _, c := range p.Calls { // Calculate arg offsets within structs. foreachArg(c, func(arg, base *Arg, _ *[]*Arg) { if base == nil || arg.Kind == ArgGroup || arg.Kind == ArgUnion { return } if w.args[base] == nil { w.args[base] = &argInfo{} } w.args[arg] = &argInfo{Offset: w.args[base].CurSize} w.args[base].CurSize += arg.Size() }) // Generate copyin instructions that fill in data into pointer arguments. foreachArg(c, func(arg, _ *Arg, _ *[]*Arg) { if arg.Kind == ArgPointer && arg.Res != nil { var rec func(*Arg) rec = func(arg1 *Arg) { if arg1.Kind == ArgGroup { for _, arg2 := range arg1.Inner { rec(arg2) } return } if arg1.Kind == ArgUnion { rec(arg1.Option) return } if sys.IsPad(arg1.Type) { return } if arg1.Kind == ArgData && len(arg1.Data) == 0 { return } if arg1.Type.Dir() != sys.DirOut { w.write(ExecInstrCopyin) w.write(physicalAddr(arg) + w.args[arg1].Offset) w.writeArg(arg1, pid) instrSeq++ } } rec(arg.Res) } }) // Generate the call itself. w.write(uintptr(c.Meta.ID)) w.write(uintptr(len(c.Args))) for _, arg := range c.Args { w.writeArg(arg, pid) } w.args[c.Ret] = &argInfo{Idx: instrSeq} instrSeq++ // Generate copyout instructions that persist interesting return values. foreachArg(c, func(arg, base *Arg, _ *[]*Arg) { if len(arg.Uses) == 0 { return } switch arg.Kind { case ArgReturn: // Idx is already assigned above. case ArgConst, ArgResult: // Create a separate copyout instruction that has own Idx. if base.Kind != ArgPointer { panic("arg base is not a pointer") } info := w.args[arg] info.Idx = instrSeq instrSeq++ w.write(ExecInstrCopyout) w.write(physicalAddr(base) + info.Offset) w.write(arg.Size()) default: panic("bad arg kind in copyout") } }) } w.write(ExecInstrEOF) return w.buf }
func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) { r := "" if p.Char() == '<' { p.Parse('<') r = p.Ident() p.Parse('=') p.Parse('>') } var arg *Arg switch p.Char() { case '0': val := p.Ident() v, err := strconv.ParseUint(val, 0, 64) if err != nil { return nil, fmt.Errorf("wrong arg value '%v': %v", val, err) } arg = constArg(uintptr(v)) case 'r': id := p.Ident() v, ok := vars[id] if !ok || v == nil { return nil, fmt.Errorf("result %v references unknown variable (vars=%+v)", id, vars) } arg = resultArg(v) if p.Char() == '/' { p.Parse('/') op := p.Ident() v, err := strconv.ParseUint(op, 0, 64) if err != nil { return nil, fmt.Errorf("wrong result div op: '%v'", op) } arg.OpDiv = uintptr(v) } if p.Char() == '+' { p.Parse('+') op := p.Ident() v, err := strconv.ParseUint(op, 0, 64) if err != nil { return nil, fmt.Errorf("wrong result add op: '%v'", op) } arg.OpAdd = uintptr(v) } case '&': var typ1 sys.Type switch t1 := typ.(type) { case sys.PtrType: typ1 = t1.Type case sys.VmaType: default: return nil, fmt.Errorf("& arg is not a pointer: %#v", typ) } p.Parse('&') page, off, size, err := parseAddr(p, true) if err != nil { return nil, err } p.Parse('=') inner, err := parseArg(typ1, p, vars) if err != nil { return nil, err } arg = pointerArg(page, off, size, inner) case '(': page, off, _, err := parseAddr(p, false) if err != nil { return nil, err } arg = pageSizeArg(page, off) case '"': p.Parse('"') val := "" if p.Char() != '"' { val = p.Ident() } p.Parse('"') data, err := hex.DecodeString(val) if err != nil { return nil, fmt.Errorf("data arg has bad value '%v'", val) } arg = dataArg(data) case '{': t1, ok := typ.(*sys.StructType) if !ok { return nil, fmt.Errorf("'{' arg is not a struct: %#v", typ) } p.Parse('{') var inner []*Arg for i := 0; p.Char() != '}'; i++ { if i >= len(t1.Fields) { return nil, fmt.Errorf("wrong struct arg count: %v, want %v", i+1, len(t1.Fields)) } fld := t1.Fields[i] if sys.IsPad(fld) { inner = append(inner, constArg(0)) } else { arg, err := parseArg(fld, p, vars) if err != nil { return nil, err } inner = append(inner, arg) if p.Char() != '}' { p.Parse(',') } } } p.Parse('}') if sys.IsPad(t1.Fields[len(t1.Fields)-1]) { inner = append(inner, constArg(0)) } arg = groupArg(inner) case '[': t1, ok := typ.(sys.ArrayType) if !ok { return nil, fmt.Errorf("'[' arg is not an array: %#v", typ) } p.Parse('[') var inner []*Arg for i := 0; p.Char() != ']'; i++ { arg, err := parseArg(t1.Type, p, vars) if err != nil { return nil, err } inner = append(inner, arg) if p.Char() != ']' { p.Parse(',') } } p.Parse(']') arg = groupArg(inner) case '@': t1, ok := typ.(*sys.UnionType) if !ok { return nil, fmt.Errorf("'@' arg is not a union: %#v", typ) } p.Parse('@') name := p.Ident() p.Parse('=') var optType sys.Type for _, t2 := range t1.Options { if name == t2.Name() { optType = t2 break } } if optType == nil { return nil, fmt.Errorf("union arg %v has unknown option: %v", typ.Name(), name) } opt, err := parseArg(optType, p, vars) if err != nil { return nil, err } arg = unionArg(opt, optType) case 'n': p.Parse('n') p.Parse('i') p.Parse('l') if r != "" { return nil, fmt.Errorf("named nil argument") } default: return nil, fmt.Errorf("failed to parse argument at %v (line #%v/%v: %v)", int(p.Char()), p.l, p.i, p.s) } if r != "" { vars[r] = arg } return arg, nil }
func Deserialize(data []byte) (prog *Prog, err error) { prog = new(Prog) p := &parser{r: bufio.NewScanner(bytes.NewReader(data))} vars := make(map[string]*Arg) for p.Scan() { if p.EOF() || p.Char() == '#' { continue } name := p.Ident() r := "" if p.Char() == '=' { r = name p.Parse('=') name = p.Ident() } meta := sys.CallMap[name] if meta == nil { return nil, fmt.Errorf("unknown syscall %v", name) } c := &Call{Meta: meta} prog.Calls = append(prog.Calls, c) p.Parse('(') for i := 0; p.Char() != ')'; i++ { if i >= len(meta.Args) { return nil, fmt.Errorf("wrong call arg count: %v, want %v", i+1, len(meta.Args)) } typ := meta.Args[i] if sys.IsPad(typ) { return nil, fmt.Errorf("padding in syscall %v arguments", name) } arg, err := parseArg(typ, p, vars) if err != nil { return nil, err } c.Args = append(c.Args, arg) if p.Char() != ')' { p.Parse(',') } } p.Parse(')') if !p.EOF() { return nil, fmt.Errorf("tailing data (line #%v)", p.l) } if len(c.Args) != len(meta.Args) { return nil, fmt.Errorf("wrong call arg count: %v, want %v", len(c.Args), len(meta.Args)) } if err := assignTypeAndDir(c); err != nil { return nil, err } if r != "" { vars[r] = c.Ret } } if p.Err() != nil { return nil, err } if err := prog.validate(); err != nil { return nil, err } return }