mirror of
https://github.com/blacktop/ipsw.git
synced 2026-05-08 12:22:26 +00:00
488 lines
17 KiB
Go
488 lines
17 KiB
Go
package ctf
|
|
|
|
//go:generate go tool stringer -type=kind,floatEncoding -output types_string.go
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
)
|
|
|
|
/*
|
|
* CTF - Compact ANSI-C Type Format
|
|
*
|
|
* This file format can be used to compactly represent the information needed
|
|
* by a debugger to interpret the ANSI-C types used by a given program.
|
|
* Traditionally, this kind of information is generated by the compiler when
|
|
* invoked with the -g flag and is stored in "stabs" strings or in the more
|
|
* modern DWARF format. CTF provides a representation of only the information
|
|
* that is relevant to debugging a complex, optimized C program such as the
|
|
* operating system kernel in a form that is significantly more compact than
|
|
* the equivalent stabs or DWARF representation. The format is data-model
|
|
* independent, so consumers do not need different code depending on whether
|
|
* they are 32-bit or 64-bit programs. CTF assumes that a standard ELF symbol
|
|
* table is available for use in the debugger, and uses the structure and data
|
|
* of the symbol table to avoid storing redundant information. The CTF data
|
|
* may be compressed on disk or in memory, indicated by a bit in the header.
|
|
* CTF may be interpreted in a raw disk file, or it may be stored in an ELF
|
|
* section, typically named .SUNW_ctf. Data structures are aligned so that
|
|
* a raw CTF file or CTF ELF section may be manipulated using mmap(2).
|
|
*
|
|
* The CTF file or section itself has the following structure:
|
|
*
|
|
* +--------+--------+---------+----------+-------+--------+
|
|
* | file | type | data | function | data | string |
|
|
* | header | labels | objects | info | types | table |
|
|
* +--------+--------+---------+----------+-------+--------+
|
|
*
|
|
* The file header stores a magic number and version information, encoding
|
|
* flags, and the byte offset of each of the sections relative to the end of the
|
|
* header itself. If the CTF data has been uniquified against another set of
|
|
* CTF data, a reference to that data also appears in the the header. This
|
|
* reference is the name of the label corresponding to the types uniquified
|
|
* against.
|
|
*
|
|
* Following the header is a list of labels, used to group the types included in
|
|
* the data types section. Each label is accompanied by a type ID i. A given
|
|
* label refers to the group of types whose IDs are in the range [0, i].
|
|
*
|
|
* Data object and function records are stored in the same order as they appear
|
|
* in the corresponding symbol table, except that symbols marked SHN_UNDEF are
|
|
* not stored and symbols that have no type data are padded out with zeroes.
|
|
* For each data object, the type ID (a small integer) is recorded. For each
|
|
* function, the type ID of the return type and argument types is recorded.
|
|
*
|
|
* The data types section is a list of variable size records that represent each
|
|
* type, in order by their ID. The types themselves form a directed graph,
|
|
* where each node may contain one or more outgoing edges to other type nodes,
|
|
* denoted by their ID.
|
|
*
|
|
* Strings are recorded as a string table ID (0 or 1) and a byte offset into the
|
|
* string table. String table 0 is the internal CTF string table. String table
|
|
* 1 is the external string table, which is the string table associated with the
|
|
* ELF symbol table for this object. CTF does not record any strings that are
|
|
* already in the symbol table, and the CTF string table does not contain any
|
|
* duplicated strings.
|
|
*
|
|
* If the CTF data has been merged with another parent CTF object, some outgoing
|
|
* edges may refer to type nodes that exist in another CTF object. The debugger
|
|
* and libctf library are responsible for connecting the appropriate objects
|
|
* together so that the full set of types can be explored and manipulated.
|
|
*/
|
|
|
|
const (
|
|
MAGIC = 0xcff1 /* magic number identifying header */
|
|
MAX_TYPE = 0xfffffffe /* max type identifier value */
|
|
MAX_NAME = 0x7fffffff /* max offset into a string table */
|
|
MAX_VLEN = 0x3ff /* max struct, union, enum members or args */
|
|
MAX_INTOFF = 0xff /* max offset of intrinsic value in bits */
|
|
MAX_INTBITS = 0xffff /* max size of an intrinsic in bits */
|
|
/* See ctf_type_t */
|
|
MAX_SIZE = 0xfffffffe /* max size of a type in bytes */
|
|
LSIZE_SENT = 0xffffffff /* sentinel for ctt_size */
|
|
MAX_LSIZE = math.MaxUint64
|
|
/* data format version number */
|
|
VERSION_1 = 1
|
|
VERSION_2 = 2
|
|
VERSION_3 = 3
|
|
VERSION_4 = 4
|
|
VERSION = VERSION_4 /* current version */
|
|
F_COMPRESS = 0x1 /* data buffer is compressed */
|
|
F_NEWFUNCINFO = 0x2 /* New v3 func info section format. */
|
|
)
|
|
|
|
// ctf_preamble
|
|
type preamble struct {
|
|
Magic uint16 `json:"magic,omitempty"` /* magic number (MAGIC) */
|
|
Version uint8 `json:"version,omitempty"` /* data format version number (VERSION) */
|
|
Flags uint8 `json:"flags,omitempty"` /* flags (see below) */
|
|
}
|
|
|
|
// ctf_header_t
|
|
type header_t struct {
|
|
Preamble preamble `json:"preamble"`
|
|
ParentLabelRef uint32 `json:"parent_label_ref,omitempty"` /* ref to name of parent lbl uniq'd against */
|
|
ParentNameRef uint32 `json:"parent_name_ref,omitempty"` /* ref to basename of parent */
|
|
LabelOffset uint32 `json:"label_offset,omitempty"` /* offset of label section */
|
|
ObjOffset uint32 `json:"obj_offset,omitempty"` /* offset of object section */
|
|
FuncOffset uint32 `json:"func_offset,omitempty"` /* offset of function section */
|
|
TypeOffset uint32 `json:"type_offset,omitempty"` /* offset of type section */
|
|
StrOffset uint32 `json:"str_offset,omitempty"` /* offset of string section */
|
|
StrLen uint32 `json:"str_len,omitempty"` /* length of string section in bytes */
|
|
}
|
|
|
|
type header struct {
|
|
header_t
|
|
ParentLabel string `json:"parent_label,omitempty"` /* name of parent lbl uniq'd against */
|
|
ParentName string `json:"parent_name,omitempty"` /* basename of parent */
|
|
}
|
|
|
|
// ctf_lblent_t
|
|
type lblent struct {
|
|
Label uint32 /* ref to name of label */
|
|
TypeIndex uint32 /* last type associated with this label */
|
|
}
|
|
|
|
// ctf_stype
|
|
type stype struct {
|
|
Name uint32 /* reference to name in string table */
|
|
Info info /* encoded kind, variant length (see below) */
|
|
SizeOrType uint32 /* UNION {
|
|
uint32_t _size - size of entire type in bytes
|
|
uint32_t _type - reference to another type
|
|
} */
|
|
}
|
|
|
|
// ctf_stype_v1
|
|
type stypeV1 struct {
|
|
Name uint32 /* reference to name in string table */
|
|
Info infoV1 /* encoded kind, variant length (see below) */
|
|
SizeOrType uint16 /* UNION {
|
|
uint16_t _size - size of entire type in bytes
|
|
uint16_t _type - reference to another type
|
|
} */
|
|
}
|
|
|
|
/*
|
|
* ctf_type_t
|
|
* type sizes, measured in bytes, come in two flavors. 99% of them fit within
|
|
* (USHRT_MAX - 1), and thus can be stored in the ctt_size member of a
|
|
* ctf_stype_t. The maximum value for these sizes is MAX_SIZE. The sizes
|
|
* larger than MAX_SIZE must be stored in the ctt_lsize member of a
|
|
* ctf_type_t. Use of this member is indicated by the presence of
|
|
* LSIZE_SENT in ctt_size.
|
|
*/
|
|
type ctftype struct {
|
|
stype
|
|
LSizeHI uint32 /* high 32 bits of type size in bytes */
|
|
LSizeLO uint32 /* low 32 bits of type size in bytes */
|
|
}
|
|
|
|
func (t ctftype) LSize() uint64 {
|
|
return uint64(t.LSizeHI)<<32 | uint64(t.LSizeLO)
|
|
}
|
|
|
|
type ctftypeV1 struct {
|
|
stypeV1
|
|
LSizeHI uint32 /* high 32 bits of type size in bytes */
|
|
LSizeLO uint32 /* low 32 bits of type size in bytes */
|
|
}
|
|
|
|
func (t ctftypeV1) LSize() uint64 {
|
|
return uint64(t.LSizeHI)<<32 | uint64(t.LSizeLO)
|
|
}
|
|
|
|
type Info interface {
|
|
Kind() kind
|
|
IsRoot() bool
|
|
VarLen() uint16
|
|
String() string
|
|
MarshalJSON() ([]byte, error)
|
|
}
|
|
|
|
/*
|
|
* The following macros compose and decompose values for ctt_info and
|
|
* ctt_name, as well as other structures that contain name references.
|
|
*
|
|
* -----------------------------------
|
|
* ctt_info: | reserved | kind | isroot | vlen |
|
|
* -----------------------------------
|
|
* 31 15 11 10 9 0
|
|
*
|
|
* kind = CTF_INFO_KIND(c.ctt_info); <-- CTF_K_* value (see below)
|
|
* vlen = CTF_INFO_VLEN(c.ctt_info); <-- length of variable data list
|
|
*
|
|
* stid = CTF_NAME_STID(c.ctt_name); <-- string table id number (0 or 1)
|
|
* offset = CTF_NAME_OFFSET(c.ctt_name); <-- string table byte offset
|
|
*
|
|
* c.ctt_info = CTF_TYPE_INFO(kind, vlen);
|
|
* c.ctt_name = CTF_TYPE_NAME(stid, offset);
|
|
*/
|
|
type info uint32
|
|
|
|
func (i info) Kind() kind {
|
|
return kind((i & 0xf800) >> 11)
|
|
}
|
|
func (i info) IsRoot() bool {
|
|
return ((i & 0x0400) >> 10) != 0
|
|
}
|
|
func (i info) VarLen() uint16 {
|
|
return uint16(i) & MAX_VLEN
|
|
}
|
|
func (i info) String() string {
|
|
return fmt.Sprintf("kind: %s, is_root: %t, len: %d", i.Kind(), i.IsRoot(), i.VarLen())
|
|
}
|
|
func (i info) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(&struct {
|
|
Kind string `json:"kind,omitempty"`
|
|
IsRoot bool `json:"is_root,omitempty"`
|
|
VarLen uint16 `json:"var_len,omitempty"`
|
|
}{
|
|
Kind: i.Kind().String(),
|
|
IsRoot: i.IsRoot(),
|
|
VarLen: i.VarLen(),
|
|
})
|
|
}
|
|
|
|
type infoV1 uint16
|
|
|
|
func (i infoV1) Kind() kind {
|
|
return kind((i & 0xf800) >> 11)
|
|
}
|
|
func (i infoV1) IsRoot() bool {
|
|
return ((i & 0x0400) >> 10) != 0
|
|
}
|
|
func (i infoV1) VarLen() uint16 {
|
|
return uint16(i) & MAX_VLEN
|
|
}
|
|
func (i infoV1) String() string {
|
|
return fmt.Sprintf("kind: %s, is_root: %t, len: %d", i.Kind(), i.IsRoot(), i.VarLen())
|
|
}
|
|
func (i infoV1) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(&struct {
|
|
Kind string `json:"kind,omitempty"`
|
|
IsRoot bool `json:"is_root,omitempty"`
|
|
VarLen uint16 `json:"var_len,omitempty"`
|
|
}{
|
|
Kind: i.Kind().String(),
|
|
IsRoot: i.IsRoot(),
|
|
VarLen: i.VarLen(),
|
|
})
|
|
}
|
|
|
|
type name uint32
|
|
|
|
func (n name) StrID() uint32 {
|
|
return uint32(n >> 31)
|
|
}
|
|
func (n name) NameOffset() uint32 {
|
|
return uint32(n & 0x7fffffff)
|
|
}
|
|
|
|
type kind uint16
|
|
|
|
const (
|
|
/*
|
|
* Values for TYPE_KIND(). If the kind has an associated data list,
|
|
* INFO_VLEN() will extract the number of elements in the list, and
|
|
* the type of each element is shown in the comments below.
|
|
*/
|
|
UNKNOWN kind = 0 /* unknown type (used for padding) */
|
|
INTEGER kind = 1 /* variant data is INT_DATA() (see below) */
|
|
FLOAT kind = 2 /* variant data is DATA() (see below) */
|
|
POINTER kind = 3 /* ctt_type is referenced type */
|
|
ARRAY kind = 4 /* variant data is single ctf_array_t */
|
|
FUNCTION kind = 5 /* ctt_type is return type, variant data is */
|
|
/* list of argument types (uint32_t's) */
|
|
STRUCT kind = 6 /* variant data is list of ctf_member_t's */
|
|
UNION kind = 7 /* variant data is list of ctf_member_t's */
|
|
ENUM kind = 8 /* variant data is list of ctf_enum_t's */
|
|
FORWARD kind = 9 /* no additional data; ctt_name is tag */
|
|
TYPEDEF kind = 10 /* ctt_type is referenced type */
|
|
VOLATILE kind = 11 /* ctt_type is base type */
|
|
CONST kind = 12 /* ctt_type is base type */
|
|
RESTRICT kind = 13 /* ctt_type is base type */
|
|
|
|
PTRAUTH kind = 14 /* variant data is PTRAUTH_DATA (see below) */
|
|
|
|
MAX kind = 31 /* Maximum possible K_* value */
|
|
)
|
|
|
|
/*
|
|
* Values for ctt_type when kind is INTEGER. The flags, offset in bits,
|
|
* and size in bits are encoded as a single word using the following macros.
|
|
*/
|
|
type intEncoding uint32
|
|
|
|
func (e intEncoding) Encoding() intEncoding {
|
|
return e & 0xff000000 >> 24
|
|
}
|
|
func (e intEncoding) Offset() uint32 {
|
|
return uint32(e&0x00ff0000) >> 16
|
|
}
|
|
func (e intEncoding) Bits() uint32 {
|
|
return uint32(e & 0x0000ffff)
|
|
}
|
|
func (e intEncoding) String() string {
|
|
var fmtStr string
|
|
if e == 0 || e&^(SIGNED|CHAR|BOOL|VARARGS) != 0 {
|
|
return fmt.Sprintf("%#x", uint32(e))
|
|
}
|
|
if (e & SIGNED) != 0 {
|
|
fmtStr += " SIGNED"
|
|
}
|
|
if (e & CHAR) != 0 {
|
|
fmtStr += " CHAR"
|
|
}
|
|
if (e & BOOL) != 0 {
|
|
fmtStr += " BOOL"
|
|
}
|
|
if (e & VARARGS) != 0 {
|
|
fmtStr += " VARARGS"
|
|
}
|
|
return strings.TrimSpace(fmtStr)
|
|
}
|
|
func (e intEncoding) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(&struct {
|
|
Encoding string `json:"encoding,omitempty"`
|
|
Offset uint32 `json:"offset,omitempty"`
|
|
Bits uint32 `json:"bits,omitempty"`
|
|
}{
|
|
Encoding: e.Encoding().String(),
|
|
Offset: e.Offset(),
|
|
Bits: e.Bits(),
|
|
})
|
|
}
|
|
|
|
const (
|
|
SIGNED intEncoding = 0x01 /* integer is signed (otherwise unsigned) */
|
|
CHAR intEncoding = 0x02 /* character display format */
|
|
BOOL intEncoding = 0x04 /* boolean display format */
|
|
VARARGS intEncoding = 0x08 /* varargs display format */
|
|
)
|
|
|
|
/*
|
|
* Values for ctt_type when kind is K_FLOAT. The encoding, offset in bits,
|
|
* and size in bits are encoded as a single word using the following macros.
|
|
*/
|
|
type floatEncoding uint32
|
|
|
|
func (e floatEncoding) Encoding() floatEncoding {
|
|
return e & 0xff000000 >> 24
|
|
}
|
|
func (e floatEncoding) Offset() uint32 {
|
|
return uint32(e&0x00ff0000) >> 16
|
|
}
|
|
func (e floatEncoding) Bits() uint32 {
|
|
return uint32(e & 0x0000ffff)
|
|
}
|
|
func (e floatEncoding) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(&struct {
|
|
Encoding string `json:"encoding,omitempty"`
|
|
Offset uint32 `json:"offset,omitempty"`
|
|
Bits uint32 `json:"bits,omitempty"`
|
|
}{
|
|
Encoding: e.Encoding().String(),
|
|
Offset: e.Offset(),
|
|
Bits: e.Bits(),
|
|
})
|
|
}
|
|
|
|
const (
|
|
SINGLE floatEncoding = 1 /* IEEE 32-bit float encoding */
|
|
DOUBLE floatEncoding = 2 /* IEEE 64-bit float encoding */
|
|
CPLX floatEncoding = 3 /* Complex encoding */
|
|
DCPLX floatEncoding = 4 /* Double complex encoding */
|
|
LDCPLX floatEncoding = 5 /* Long double complex encoding */
|
|
LDOUBLE floatEncoding = 6 /* Long double encoding */
|
|
INTRVL floatEncoding = 7 /* Interval (2x32-bit) encoding */
|
|
DINTRVL floatEncoding = 8 /* Double interval (2x64-bit) encoding */
|
|
LDINTRVL floatEncoding = 9 /* Long double interval (2x128-bit) encoding */
|
|
IMAGRY floatEncoding = 10 /* Imaginary (32-bit) encoding */
|
|
DIMAGRY floatEncoding = 11 /* Long imaginary (64-bit) encoding */
|
|
LDIMAGRY floatEncoding = 12 /* Long double imaginary (128-bit) encoding */
|
|
)
|
|
|
|
/*
|
|
* Variant data associated with PTRAUTH. The key, discriminator
|
|
* and whether the pointer is discriminated are encoded as a single word
|
|
* using the following macros.
|
|
*/
|
|
type ptrAuthData uint32
|
|
|
|
func (p ptrAuthData) Discriminated() bool {
|
|
return uint32(p&0xff000000>>24) != 0
|
|
}
|
|
func (p ptrAuthData) Key() string {
|
|
name := []string{"IA", "IB", "DA", "DB"}
|
|
key := uint64(p&0x00ff0000) >> 16
|
|
if key >= 4 {
|
|
return "ERROR"
|
|
}
|
|
return name[key]
|
|
}
|
|
func (p ptrAuthData) Discriminator() uint32 {
|
|
return uint32(p & 0x0000ffff)
|
|
}
|
|
func (p ptrAuthData) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(&struct {
|
|
Key string `json:"key,omitempty"`
|
|
AddrDiv bool `json:"addr_div,omitempty"`
|
|
Diversity uint32 `json:"diversity,omitempty"`
|
|
}{
|
|
Key: p.Key(),
|
|
AddrDiv: p.Discriminated(),
|
|
Diversity: p.Discriminator(),
|
|
})
|
|
}
|
|
|
|
// ctf_array_t
|
|
type array struct {
|
|
Contents uint32 `json:"contents,omitempty"` /* reference to type of array contents */
|
|
Index uint32 `json:"index,omitempty"` /* reference to type of array index */
|
|
NumElements uint32 `json:"num_elements,omitempty"` /* number of elements */
|
|
}
|
|
|
|
// ctf_array_v1_t
|
|
type arrayV1 struct {
|
|
Contents uint16 `json:"contents,omitempty"` /* reference to type of array contents */
|
|
Index uint16 `json:"index,omitempty"` /* reference to type of array index */
|
|
NumElements uint32 `json:"num_elements,omitempty"` /* number of elements */
|
|
}
|
|
|
|
/*
|
|
* Most structure members have bit offsets that can be expressed using a
|
|
* short. Some don't. ctf_member_t is used for structs which cannot
|
|
* contain any of these large offsets, whereas ctf_lmember_t is used in the
|
|
* latter case. If ctt_size for a given struct is >= 8192 bytes, all members
|
|
* will be stored as type ctf_lmember_t.
|
|
*/
|
|
const LSTRUCT_THRESH = 8192
|
|
|
|
// ctf_member_t
|
|
type member struct {
|
|
Name uint32 /* reference to name in string table */
|
|
Type uint32 /* reference to type of member */
|
|
Offset uint16 /* offset of this member in bits */
|
|
_ uint16 // padding ?
|
|
}
|
|
|
|
// ctf_member_v1
|
|
type memberV1 struct {
|
|
Name uint32 /* reference to name in string table */
|
|
Type uint16 /* reference to type of member */
|
|
Offset uint16 /* offset of this member in bits */
|
|
}
|
|
|
|
// ctf_lmember_t
|
|
type lmember struct {
|
|
Name uint32 /* reference to name in string table */
|
|
Type uint32 /* reference to type of member */
|
|
OffsetHI uint32 /* high 32 bits of member offset in bits */
|
|
OffsetLO uint32 /* low 32 bits of member offset in bits */
|
|
}
|
|
|
|
func (l lmember) Offset() uint64 {
|
|
return uint64(l.OffsetHI)<<32 | uint64(l.OffsetLO)
|
|
}
|
|
|
|
// ctf_lmember_v1
|
|
type lmemberV1 struct {
|
|
Name uint32 /* reference to name in string table */
|
|
Type uint16 /* reference to type of member */
|
|
Pad uint16 /* padding */
|
|
OffsetHI uint32 /* high 32 bits of member offset in bits */
|
|
OffsetLO uint32 /* low 32 bits of member offset in bits */
|
|
}
|
|
|
|
func (l lmemberV1) Offset() uint64 {
|
|
return uint64(l.OffsetHI)<<32 | uint64(l.OffsetLO)
|
|
}
|
|
|
|
// ctf_enum_t
|
|
type enum struct {
|
|
Name uint32 /* reference to name in string table */
|
|
Value int32 /* value associated with this name */
|
|
}
|