Files

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