package ctf import ( "bytes" "encoding/json" "fmt" "strconv" "strings" "text/tabwriter" ) type Type interface { ID() int Name() string Info() Info ParentID() int Type() string String() string Dump() string } type Integer struct { id int name string info Info encoding intEncoding } func (i *Integer) ID() int { return i.id } func (i *Integer) Name() string { return i.name } func (i *Integer) Info() Info { return i.info } func (i *Integer) Encoding() string { return i.encoding.Encoding().String() } func (i *Integer) Offset() uint32 { return i.encoding.Offset() } func (i *Integer) Bits() uint32 { return i.encoding.Bits() } func (i *Integer) ParentID() int { return 0 } func (i *Integer) Type() string { return i.name } func (i *Integer) String() string { return i.name } func (i *Integer) Dump() string { return fmt.Sprintf("%s %s %s encoding=%s offset=%d bits=%d\n", isRootIDFmtStr(i.info.IsRoot(), i.id), i.info.Kind(), i.name, i.encoding.Encoding(), i.encoding.Offset(), i.encoding.Bits(), ) } func (i *Integer) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` Encoding intEncoding `json:"encoding,omitempty"` }{ ID: i.id, Name: i.name, Info: i.info, Encoding: i.encoding, }) } type Float struct { id int name string info Info encoding floatEncoding } func (f *Float) ID() int { return f.id } func (f *Float) Name() string { return f.name } func (f *Float) Info() Info { return f.info } func (f *Float) Encoding() string { return f.encoding.Encoding().String() } func (f *Float) Offset() uint32 { return f.encoding.Offset() } func (f *Float) Bits() uint32 { return f.encoding.Bits() } func (f *Float) ParentID() int { return 0 } func (f *Float) Type() string { return f.name } func (f *Float) String() string { return f.name } func (f *Float) Dump() string { return fmt.Sprintf("%s %s %s encoding=%s offset=%d bits=%d", isRootIDFmtStr(f.info.IsRoot(), f.id), f.info.Kind(), f.name, f.encoding.Encoding(), f.encoding.Offset(), f.encoding.Bits(), ) } func (f *Float) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` Encoding floatEncoding `json:"encoding,omitempty"` }{ ID: f.id, Name: f.name, Info: f.info, Encoding: f.encoding, }) } type Array struct { id int name string info Info array lookupFn func(int) Type } func (a *Array) ID() int { return a.id } func (a *Array) Name() string { if a.name == "(anon)" { return "" } return " " + a.name } func (a *Array) Info() Info { return a.info } func (a *Array) Index() string { if t := a.lookupFn(int(a.array.Index)); t != nil { return t.Type() } return "" } func (a *Array) Contents() string { if t := a.lookupFn(int(a.array.Contents)); t != nil { return t.Type() } return "" } func (a *Array) ParentID() int { return 0 } func (a *Array) Type() string { return strings.TrimSuffix(a.String(), ";") } func (a *Array) String() string { return fmt.Sprintf( "%s%s[%d];", a.Contents(), a.Name(), a.NumElements, ) } func (a *Array) Dump() string { return fmt.Sprintf("%s %s %s content: %d index: %d nelems: %d", isRootIDFmtStr(a.info.IsRoot(), a.id), a.info.Kind(), a.name, a.array.Contents, a.array.Index, a.array.NumElements, ) } func (a *Array) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` ContentsID uint `json:"contents_id,omitempty"` /* reference to type of array contents */ Contents string `json:"contents,omitempty"` /* reference to type of array contents */ IndexID uint `json:"index_id,omitempty"` /* reference to type of array index */ IndexType string `json:"index_type,omitempty"` /* reference to type of array index */ NumElements uint32 `json:"num_elements,omitempty"` /* number of elements */ }{ ID: a.id, Name: a.name, Info: a.info, ContentsID: uint(a.array.Contents), Contents: a.Contents(), IndexID: uint(a.array.Index), IndexType: a.Index(), NumElements: a.array.NumElements, }) } type Function struct { id int name string info Info ret uint args []uint32 lookupFn func(int) Type } type funcArg struct { ID int `json:"id,omitempty"` Type string `json:"type,omitempty"` } func (f *Function) ID() int { return f.id } func (f *Function) Name() string { return f.name } func (f *Function) Info() Info { return f.info } func (f *Function) Return() string { if t := f.lookupFn(int(f.ret)); t != nil { return t.Type() } return "" } func (f *Function) Args() string { var argStrs []string for idx, arg := range f.args { if t := f.lookupFn(int(arg)); t != nil { argStrs = append(argStrs, t.Type()) } else { return fmt.Sprintf("", idx) } } return strings.Join(argStrs, ", ") } func (f *Function) ParentID() int { return 0 } func (f *Function) Type() string { return strings.TrimSuffix(f.String(), ";") } func (f *Function) String() string { return fmt.Sprintf( "%s %s(%s);", f.Return(), f.name, f.Args(), ) } func (f *Function) Dump() string { var fdump string fdump = fmt.Sprintf("%s %s %s returns: %d args: (", isRootIDFmtStr(f.info.IsRoot(), f.id), f.info.Kind(), f.name, f.ret, ) var sargs []string for _, a := range f.args { sargs = append(sargs, strconv.Itoa(int(a))) } fdump += fmt.Sprintf("%s)", strings.Join(sargs, ",")) return fdump } func (f *Function) MarshalJSON() ([]byte, error) { var args []funcArg for idx, arg := range f.args { if t := f.lookupFn(int(arg)); t != nil { args = append(args, funcArg{ ID: int(arg), Type: t.Type(), }) } else { args = append(args, funcArg{ ID: int(arg), Type: fmt.Sprintf("", idx, arg), }) } } return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` ReturnID uint `json:"return_id,omitempty"` Return string `json:"return,omitempty"` Arguments []funcArg `json:"arguments,omitempty"` }{ ID: f.id, Name: f.name, Info: f.info, ReturnID: uint(f.ret), Return: f.Return(), Arguments: args, }) } type Member struct { parent int name string offset uint64 reference uint lookupFn func(int) Type } func (m Member) ParentID() int { return m.parent } func (m Member) Name() string { return m.name } func (m Member) Type() string { if t := m.lookupFn(int(m.reference)); t != nil { return t.Type() } return "" } func (m Member) Offset() uint64 { return m.offset } func (m Member) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Type string `json:"type,omitempty"` Offset uint64 `json:"offset"` }{ ID: int(m.reference), Name: m.name, Type: m.Type(), Offset: m.offset, }) } type Struct struct { id int name string info Info size uint64 Fields []Member lookupFn func(int) Type } func (s *Struct) ID() int { return s.id } func (s *Struct) Name() string { if s.name == "(anon)" { return "" } return s.name } func (s *Struct) Info() Info { return s.info } func (s *Struct) ParentID() int { return 0 } func (s *Struct) Size() uint64 { return s.size } func (s *Struct) Type() string { if s.name == "(anon)" { return strings.TrimSuffix(s.String(), ";") } return "struct " + s.Name() } func (s *Struct) String() string { buf := bytes.NewBufferString("") w := tabwriter.NewWriter(buf, 0, 0, 1, ' ', 0) fmt.Fprintf(w, "struct %s {\t\t// (%d bytes)\n", s.Name(), s.size, ) for _, f := range s.Fields { t := s.lookupFn(int(f.reference)) if t != nil && t.Info().Kind() == ARRAY { fmt.Fprintf(w, " %s\t%s[%d];\t// off=%#x\n", t.(*Array).Contents(), f.name, t.(*Array).NumElements, f.offset, ) } else { fmt.Fprintf(w, " %s\t%s;\t// off=%#x\n", f.Type(), f.name, f.offset, ) } } fmt.Fprintf(w, "};") w.Flush() return buf.String() } func (s *Struct) Dump() string { buf := bytes.NewBufferString("") w := tabwriter.NewWriter(buf, 0, 0, 1, ' ', 0) fmt.Fprintf(w, "%s %s %s (%d bytes)\n", isRootIDFmtStr(s.info.IsRoot(), s.id), s.info.Kind(), s.name, s.size, ) for _, f := range s.Fields { fmt.Fprintf(w, " %s\ttype=%d\toff=%#x\n", f.Name(), f.reference, f.offset, ) } w.Flush() return buf.String() } func (s *Struct) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` Size uint64 `json:"size,omitempty"` Fields []Member `json:"fields,omitempty"` }{ ID: s.id, Name: s.name, Info: s.info, Size: s.size, Fields: s.Fields, }) } type Union struct { id int name string info Info size uint64 Fields []Member lookupFn func(int) Type } func (s *Union) ID() int { return s.id } func (s *Union) Name() string { if s.name == "(anon)" { return "" } return s.name } func (s *Union) Info() Info { return s.info } func (s *Union) ParentID() int { return 0 } func (s *Union) Size() uint64 { return s.size } func (s *Union) Type() string { return "union " + s.name } func (s *Union) String() string { buf := bytes.NewBufferString("") w := tabwriter.NewWriter(buf, 0, 0, 1, ' ', 0) fmt.Fprintf(w, "union %s {\t\t// (%d bytes)\n", s.Name(), s.size, ) for _, f := range s.Fields { t := s.lookupFn(int(f.reference)) if t != nil && t.Info().Kind() == ARRAY { fmt.Fprintf(w, " %s\t%s[%d];\n", t.(*Array).Contents(), f.name, t.(*Array).NumElements, ) } else { fmt.Fprintf(w, " %s\t%s;\n", f.Type(), f.name, ) } } fmt.Fprintf(w, "};") w.Flush() return buf.String() } func (s *Union) Dump() string { var sdump string sdump = fmt.Sprintf("%s %s %s (%d bytes)\n", isRootIDFmtStr(s.info.IsRoot(), s.id), s.info.Kind(), s.name, s.size, ) for _, f := range s.Fields { sdump += fmt.Sprintf("\t%s type=%d off=%#x\n", f.Name(), f.reference, f.offset, ) } return sdump } func (s *Union) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` Size uint64 `json:"size,omitempty"` Fields []Member `json:"fields,omitempty"` }{ ID: s.id, Name: s.name, Info: s.info, Size: s.size, Fields: s.Fields, }) } type enumField struct { Name string `json:"name,omitempty"` Value int32 `json:"value,omitempty"` } type Enum struct { id int name string info Info Fields []enumField } func (e *Enum) ID() int { return e.id } func (e *Enum) Name() string { return e.name } func (e *Enum) Info() Info { return e.info } func (e *Enum) ParentID() int { return 0 } func (e *Enum) Type() string { return "enum " + e.name } func (e *Enum) String() string { var eout strings.Builder eout.WriteString(fmt.Sprintf("enum %s {\n", e.name)) for _, f := range e.Fields { eout.WriteString(fmt.Sprintf("\t%s = %d\n", f.Name, f.Value, )) } eout.WriteString("};") return eout.String() } func (e *Enum) Dump() string { var edump string edump = fmt.Sprintf("%s %s %s\n", isRootIDFmtStr(e.info.IsRoot(), e.id), e.info.Kind(), e.name, ) for _, f := range e.Fields { edump += fmt.Sprintf("\t%s = %d\n", f.Name, f.Value, ) } return edump } func (e *Enum) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` Fields []enumField `json:"fields,omitempty"` }{ ID: e.id, Name: e.name, Info: e.info, Fields: e.Fields, }) } type Forward struct { id int name string info Info } func (f *Forward) ID() int { return f.id } func (f *Forward) Name() string { return f.name } func (f *Forward) Info() Info { return f.info } func (f *Forward) ParentID() int { return 0 } func (f *Forward) Type() string { return f.name } func (f *Forward) String() string { return f.name } func (f *Forward) Dump() string { return fmt.Sprintf("%s %s %s", isRootIDFmtStr(f.info.IsRoot(), f.id), f.info.Kind(), f.name, ) } func (f *Forward) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` }{ ID: f.id, Name: f.name, Info: f.info, }) } type Pointer struct { id int name string info Info reference uint lookupFn func(int) Type } func (p *Pointer) ID() int { return p.id } func (p *Pointer) Name() string { return p.name } func (p *Pointer) Info() Info { return p.info } func (p *Pointer) Reference() string { if t := p.lookupFn(int(p.reference)); t != nil { return t.Type() } return "" } func (p *Pointer) ParentID() int { return int(p.reference) } func (p *Pointer) Type() string { return strings.TrimSuffix(p.String(), ";") } func (p *Pointer) String() string { return fmt.Sprintf("%s *;", p.Reference()) } func (p *Pointer) Dump() string { return fmt.Sprintf("%s %s %s refers to %d", isRootIDFmtStr(p.info.IsRoot(), p.id), p.info.Kind(), p.name, p.reference, ) } func (p *Pointer) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` ReferenceID uint `json:"reference_id,omitempty"` Reference string `json:"reference,omitempty"` }{ ID: p.id, Name: p.name, Info: p.info, ReferenceID: uint(p.reference), Reference: p.Reference(), }) } type Typedef struct { id int name string info Info reference uint lookupFn func(int) Type } func (p *Typedef) ID() int { return p.id } func (p *Typedef) Name() string { return p.name } func (p *Typedef) Info() Info { return p.info } func (p *Typedef) Reference() string { if t := p.lookupFn(int(p.reference)); t != nil { return t.Type() } return "" } func (p *Typedef) ParentID() int { return int(p.reference) } func (p *Typedef) Type() string { return p.Name() } func (p *Typedef) String() string { return fmt.Sprintf("typedef %s %s", p.name, p.Reference()) } func (p *Typedef) Dump() string { return fmt.Sprintf("%s %s %s refers to %d", isRootIDFmtStr(p.info.IsRoot(), p.id), p.info.Kind(), p.name, p.reference, ) } func (p *Typedef) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` ReferenceID uint `json:"reference_id,omitempty"` Reference string `json:"reference,omitempty"` }{ ID: p.id, Name: p.name, Info: p.info, ReferenceID: uint(p.reference), Reference: p.Reference(), }) } type Volatile struct { id int name string info Info reference uint lookupFn func(int) Type } func (p *Volatile) ID() int { return p.id } func (p *Volatile) Name() string { return p.name } func (p *Volatile) Info() Info { return p.info } func (p *Volatile) Reference() string { if t := p.lookupFn(int(p.reference)); t != nil { return t.Type() } return "" } func (p *Volatile) ParentID() int { return int(p.reference) } func (p *Volatile) Type() string { return fmt.Sprintf("volatile %s", p.Reference()) } func (p *Volatile) String() string { return fmt.Sprintf("volatile %s", p.Reference()) } func (p *Volatile) Dump() string { return fmt.Sprintf("%s %s %s refers to %d", isRootIDFmtStr(p.info.IsRoot(), p.id), p.info.Kind(), p.name, p.reference, ) } func (p *Volatile) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` ReferenceID uint `json:"reference_id,omitempty"` Reference string `json:"reference,omitempty"` }{ ID: p.id, Name: p.name, Info: p.info, ReferenceID: uint(p.reference), Reference: p.Reference(), }) } type Const struct { id int name string info Info reference uint lookupFn func(int) Type } func (p *Const) ID() int { return p.id } func (p *Const) Name() string { return p.name } func (p *Const) Info() Info { return p.info } func (p *Const) Reference() string { if t := p.lookupFn(int(p.reference)); t != nil { return t.Type() } return "" } func (p *Const) ParentID() int { return int(p.reference) } func (p *Const) Type() string { return fmt.Sprintf("const %s", p.Reference()) } func (p *Const) String() string { return fmt.Sprintf("const %s", p.Reference()) } func (p *Const) Dump() string { return fmt.Sprintf("%s %s %s refers to %d", isRootIDFmtStr(p.info.IsRoot(), p.id), p.info.Kind(), p.name, p.reference, ) } func (p *Const) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` ReferenceID uint `json:"reference_id,omitempty"` Reference string `json:"reference,omitempty"` }{ ID: p.id, Name: p.name, Info: p.info, ReferenceID: uint(p.reference), Reference: p.Reference(), }) } type Restrict struct { id int name string info Info reference uint lookupFn func(int) Type } func (p *Restrict) ID() int { return p.id } func (p *Restrict) Name() string { return p.name } func (p *Restrict) Info() Info { return p.info } func (p *Restrict) Reference() string { if t := p.lookupFn(int(p.reference)); t != nil { return t.Type() } return "" } func (p *Restrict) ParentID() int { return int(p.reference) } func (p *Restrict) Type() string { return fmt.Sprintf("restrict %s", p.Reference()) } func (p *Restrict) String() string { return fmt.Sprintf("restrict %s", p.Reference()) } func (p *Restrict) Dump() string { return fmt.Sprintf("%s %s %s refers to %d", isRootIDFmtStr(p.info.IsRoot(), p.id), p.info.Kind(), p.name, p.reference, ) } func (p *Restrict) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` ReferenceID uint `json:"reference_id,omitempty"` Reference string `json:"reference,omitempty"` }{ ID: p.id, Name: p.name, Info: p.info, ReferenceID: uint(p.reference), Reference: p.Reference(), }) } type PtrAuth struct { id int name string info Info data ptrAuthData reference uint lookupFn func(int) Type } func (p *PtrAuth) ID() int { return p.id } func (p *PtrAuth) Name() string { return p.name } func (p *PtrAuth) Info() Info { return p.info } func (p *PtrAuth) Reference() string { if t := p.lookupFn(int(p.reference)); t != nil { return t.Type() } return "" } func (p *PtrAuth) ParentID() int { return int(p.reference) } func (p *PtrAuth) Type() string { return p.String() } func (p *PtrAuth) String() string { return fmt.Sprintf( "%s __ptrauth(%s, %t, %04x)", p.Reference(), p.data.Key(), p.data.Discriminated(), p.data.Discriminator(), ) } func (p *PtrAuth) Dump() string { return fmt.Sprintf("%s %s %s refers to %d (key=%s, addr_div=%t, div=%04x)", isRootIDFmtStr(p.info.IsRoot(), p.id), p.info.Kind(), p.name, p.reference, p.data.Key(), p.data.Discriminated(), p.data.Discriminator(), ) } func (p *PtrAuth) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` Info Info `json:"info,omitempty"` Data ptrAuthData `json:"data,omitempty"` ReferenceID uint `json:"reference_id,omitempty"` Reference string `json:"reference,omitempty"` }{ ID: p.id, Name: p.name, Info: p.info, Data: p.data, ReferenceID: uint(p.reference), Reference: p.Reference(), }) } type global struct { Address uint64 `json:"address,omitempty"` Name string `json:"name,omitempty"` Reference int `json:"reference,omitempty"` Type Type `json:"type,omitempty"` } func (g global) String() string { if g.Type != nil { return fmt.Sprintf("%#x: %s %s", g.Address, g.Type.Type(), g.Name) } if g.Reference == 0 { return fmt.Sprintf("%#x: %s", g.Address, g.Name) } return fmt.Sprintf("%#x: %d %s", g.Address, g.Reference, g.Name) } type function struct { Address uint64 `json:"address,omitempty"` Name string `json:"name,omitempty"` Return Type `json:"return,omitempty"` Arguments []Type `json:"arguments,omitempty"` } func (f function) String() string { var args []string for _, arg := range f.Arguments { args = append(args, arg.Type()) } return fmt.Sprintf("%#x: %s %s(%s);", f.Address, f.Return.Type(), f.Name, strings.Join(args, ", ")) } func isRootIDFmtStr(isroot bool, id int) string { if isroot { return fmt.Sprintf("<%d>", id) } return fmt.Sprintf("[%d]", id) } func (h header) String() string { return fmt.Sprintf( "Magic = %#x\n"+ "Version = %d\n"+ "Flags = %#x\n"+ "Parent Label = %s\n"+ "Parent Name = %s\n"+ "Label Offset = %d\n"+ "Obj Offset = %d\n"+ "Func Offset = %#x\n"+ "Type Offset = %#x\n"+ "Str Offset = %#x\n"+ "Str Len = %#x\n", h.Preamble.Magic, h.Preamble.Version, h.Preamble.Flags, h.ParentLabel, h.ParentName, h.LabelOffset, h.ObjOffset, h.FuncOffset, h.TypeOffset, h.StrOffset, h.StrLen, ) }