795 lines
17 KiB
C
795 lines
17 KiB
C
/* $OpenBSD: db_ctf.c,v 1.34 2024/02/22 13:49:17 claudio Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2016-2017 Martin Pieuchot
|
|
* Copyright (c) 2016 Jasper Lievisse Adriaanse <jasper@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/stdint.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/exec.h>
|
|
|
|
#include <machine/db_machdep.h>
|
|
|
|
#include <ddb/db_extern.h>
|
|
#include <ddb/db_command.h>
|
|
#include <ddb/db_elf.h>
|
|
#include <ddb/db_lex.h>
|
|
#include <ddb/db_output.h>
|
|
#include <ddb/db_sym.h>
|
|
#include <ddb/db_access.h>
|
|
|
|
#include <sys/exec_elf.h>
|
|
#include <sys/ctf.h>
|
|
#include <sys/malloc.h>
|
|
#include <lib/libz/zlib.h>
|
|
|
|
extern db_symtab_t db_symtab;
|
|
|
|
struct ddb_ctf {
|
|
struct ctf_header *cth;
|
|
const char *rawctf; /* raw .SUNW_ctf section */
|
|
size_t rawctflen; /* raw .SUNW_ctf section size */
|
|
const char *data; /* decompressed CTF data */
|
|
size_t dlen; /* decompressed CTF data size */
|
|
char *strtab; /* ELF string table */
|
|
uint32_t ctf_found;
|
|
};
|
|
|
|
struct ddb_ctf db_ctf;
|
|
|
|
static const char *db_ctf_off2name(uint32_t);
|
|
static Elf_Sym *db_ctf_idx2sym(size_t *, uint8_t);
|
|
static char *db_ctf_decompress(const char *, size_t, size_t);
|
|
|
|
uint32_t db_ctf_type_len(const struct ctf_type *);
|
|
size_t db_ctf_type_size(const struct ctf_type *);
|
|
const struct ctf_type *db_ctf_type_by_name(const char *, unsigned int);
|
|
const struct ctf_type *db_ctf_type_by_symbol(Elf_Sym *);
|
|
const struct ctf_type *db_ctf_type_by_index(uint16_t);
|
|
void db_ctf_pprint(const struct ctf_type *, vaddr_t);
|
|
void db_ctf_pprint_struct(const struct ctf_type *, vaddr_t);
|
|
void db_ctf_pprint_enum(const struct ctf_type *, vaddr_t);
|
|
void db_ctf_pprint_ptr(const struct ctf_type *, vaddr_t);
|
|
|
|
/*
|
|
* Entrypoint to verify CTF presence, initialize the header, decompress
|
|
* the data, etc.
|
|
*/
|
|
void
|
|
db_ctf_init(void)
|
|
{
|
|
db_symtab_t *stab = &db_symtab;
|
|
size_t rawctflen;
|
|
|
|
/* Assume nothing was correct found until proven otherwise. */
|
|
db_ctf.ctf_found = 0;
|
|
|
|
if (stab->private == NULL)
|
|
return;
|
|
|
|
db_ctf.strtab = db_elf_find_strtab(stab);
|
|
if (db_ctf.strtab == NULL)
|
|
return;
|
|
|
|
db_ctf.rawctf = db_elf_find_section(stab, &rawctflen, ELF_CTF);
|
|
if (db_ctf.rawctf == NULL)
|
|
return;
|
|
|
|
db_ctf.rawctflen = rawctflen;
|
|
db_ctf.cth = (struct ctf_header *)db_ctf.rawctf;
|
|
db_ctf.dlen = db_ctf.cth->cth_stroff + db_ctf.cth->cth_strlen;
|
|
|
|
if ((db_ctf.cth->cth_flags & CTF_F_COMPRESS) == 0) {
|
|
db_printf("unsupported non-compressed CTF section\n");
|
|
return;
|
|
}
|
|
|
|
/* Now decompress the section, take into account to skip the header */
|
|
db_ctf.data = db_ctf_decompress(db_ctf.rawctf + sizeof(*db_ctf.cth),
|
|
db_ctf.rawctflen - sizeof(*db_ctf.cth), db_ctf.dlen);
|
|
if (db_ctf.data == NULL)
|
|
return;
|
|
|
|
/* We made it this far, everything seems fine. */
|
|
db_ctf.ctf_found = 1;
|
|
}
|
|
|
|
/*
|
|
* Convert an index to a symbol name while ensuring the type is matched.
|
|
* It must be noted this only works if the CTF table has the same order
|
|
* as the symbol table.
|
|
*/
|
|
Elf_Sym *
|
|
db_ctf_idx2sym(size_t *idx, uint8_t type)
|
|
{
|
|
Elf_Sym *symp, *symtab_start, *symtab_end;
|
|
size_t i = *idx + 1;
|
|
|
|
symtab_start = STAB_TO_SYMSTART(&db_symtab);
|
|
symtab_end = STAB_TO_SYMEND(&db_symtab);
|
|
|
|
for (symp = &symtab_start[i]; symp < symtab_end; i++, symp++) {
|
|
if (ELF_ST_TYPE(symp->st_info) != type)
|
|
continue;
|
|
|
|
*idx = i;
|
|
return symp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* For a given function name, return the number of arguments.
|
|
*/
|
|
int
|
|
db_ctf_func_numargs(Elf_Sym *st)
|
|
{
|
|
Elf_Sym *symp;
|
|
uint16_t *fstart, *fend;
|
|
uint16_t *fsp, kind, vlen;
|
|
size_t i, idx = 0;
|
|
|
|
if (!db_ctf.ctf_found || st == NULL)
|
|
return -1;
|
|
|
|
fstart = (uint16_t *)(db_ctf.data + db_ctf.cth->cth_funcoff);
|
|
fend = (uint16_t *)(db_ctf.data + db_ctf.cth->cth_typeoff);
|
|
|
|
fsp = fstart;
|
|
while (fsp < fend) {
|
|
symp = db_ctf_idx2sym(&idx, STT_FUNC);
|
|
if (symp == NULL)
|
|
break;
|
|
|
|
kind = CTF_INFO_KIND(*fsp);
|
|
vlen = CTF_INFO_VLEN(*fsp);
|
|
fsp++;
|
|
|
|
if (kind == CTF_K_UNKNOWN && vlen == 0)
|
|
continue;
|
|
|
|
/* Skip return type */
|
|
fsp++;
|
|
|
|
/* Skip argument types */
|
|
for (i = 0; i < vlen; i++)
|
|
fsp++;
|
|
|
|
if (symp == st)
|
|
return vlen;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Return the length of the type record in the CTF section.
|
|
*/
|
|
uint32_t
|
|
db_ctf_type_len(const struct ctf_type *ctt)
|
|
{
|
|
uint16_t kind, vlen, i;
|
|
uint32_t tlen;
|
|
uint64_t size;
|
|
|
|
kind = CTF_INFO_KIND(ctt->ctt_info);
|
|
vlen = CTF_INFO_VLEN(ctt->ctt_info);
|
|
|
|
if (ctt->ctt_size <= CTF_MAX_SIZE) {
|
|
size = ctt->ctt_size;
|
|
tlen = sizeof(struct ctf_stype);
|
|
} else {
|
|
size = CTF_TYPE_LSIZE(ctt);
|
|
tlen = sizeof(struct ctf_type);
|
|
}
|
|
|
|
switch (kind) {
|
|
case CTF_K_UNKNOWN:
|
|
case CTF_K_FORWARD:
|
|
break;
|
|
case CTF_K_INTEGER:
|
|
tlen += sizeof(uint32_t);
|
|
break;
|
|
case CTF_K_FLOAT:
|
|
tlen += sizeof(uint32_t);
|
|
break;
|
|
case CTF_K_ARRAY:
|
|
tlen += sizeof(struct ctf_array);
|
|
break;
|
|
case CTF_K_FUNCTION:
|
|
tlen += (vlen + (vlen & 1)) * sizeof(uint16_t);
|
|
break;
|
|
case CTF_K_STRUCT:
|
|
case CTF_K_UNION:
|
|
if (size < CTF_LSTRUCT_THRESH) {
|
|
for (i = 0; i < vlen; i++) {
|
|
tlen += sizeof(struct ctf_member);
|
|
}
|
|
} else {
|
|
for (i = 0; i < vlen; i++) {
|
|
tlen += sizeof(struct ctf_lmember);
|
|
}
|
|
}
|
|
break;
|
|
case CTF_K_ENUM:
|
|
for (i = 0; i < vlen; i++) {
|
|
tlen += sizeof(struct ctf_enum);
|
|
}
|
|
break;
|
|
case CTF_K_POINTER:
|
|
case CTF_K_TYPEDEF:
|
|
case CTF_K_VOLATILE:
|
|
case CTF_K_CONST:
|
|
case CTF_K_RESTRICT:
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
return tlen;
|
|
}
|
|
|
|
/*
|
|
* Return the size of the type.
|
|
*/
|
|
size_t
|
|
db_ctf_type_size(const struct ctf_type *ctt)
|
|
{
|
|
vaddr_t taddr = (vaddr_t)ctt;
|
|
const struct ctf_type *ref;
|
|
const struct ctf_array *arr;
|
|
size_t tlen = 0;
|
|
uint16_t kind;
|
|
uint32_t toff;
|
|
uint64_t size;
|
|
|
|
kind = CTF_INFO_KIND(ctt->ctt_info);
|
|
|
|
if (ctt->ctt_size <= CTF_MAX_SIZE) {
|
|
size = ctt->ctt_size;
|
|
toff = sizeof(struct ctf_stype);
|
|
} else {
|
|
size = CTF_TYPE_LSIZE(ctt);
|
|
toff = sizeof(struct ctf_type);
|
|
}
|
|
|
|
switch (kind) {
|
|
case CTF_K_UNKNOWN:
|
|
case CTF_K_FORWARD:
|
|
break;
|
|
case CTF_K_INTEGER:
|
|
case CTF_K_FLOAT:
|
|
tlen = size;
|
|
break;
|
|
case CTF_K_ARRAY:
|
|
arr = (struct ctf_array *)(taddr + toff);
|
|
ref = db_ctf_type_by_index(arr->cta_contents);
|
|
tlen = arr->cta_nelems * db_ctf_type_size(ref);
|
|
break;
|
|
case CTF_K_FUNCTION:
|
|
tlen = 0;
|
|
break;
|
|
case CTF_K_STRUCT:
|
|
case CTF_K_UNION:
|
|
case CTF_K_ENUM:
|
|
tlen = size;
|
|
break;
|
|
case CTF_K_POINTER:
|
|
tlen = sizeof(void *);
|
|
break;
|
|
case CTF_K_TYPEDEF:
|
|
case CTF_K_VOLATILE:
|
|
case CTF_K_CONST:
|
|
case CTF_K_RESTRICT:
|
|
ref = db_ctf_type_by_index(ctt->ctt_type);
|
|
tlen = db_ctf_type_size(ref);
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
return tlen;
|
|
}
|
|
|
|
/*
|
|
* Return the CTF type associated to an ELF symbol.
|
|
*/
|
|
const struct ctf_type *
|
|
db_ctf_type_by_symbol(Elf_Sym *st)
|
|
{
|
|
Elf_Sym *symp;
|
|
uint32_t objtoff;
|
|
uint16_t *dsp;
|
|
size_t idx = 0;
|
|
|
|
if (!db_ctf.ctf_found || st == NULL)
|
|
return NULL;
|
|
|
|
objtoff = db_ctf.cth->cth_objtoff;
|
|
|
|
while (objtoff < db_ctf.cth->cth_funcoff) {
|
|
dsp = (uint16_t *)(db_ctf.data + objtoff);
|
|
|
|
symp = db_ctf_idx2sym(&idx, STT_OBJECT);
|
|
if (symp == NULL)
|
|
break;
|
|
if (symp == st)
|
|
return db_ctf_type_by_index(*dsp);
|
|
|
|
objtoff += sizeof(*dsp);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const struct ctf_type *
|
|
db_ctf_type_by_name(const char *name, unsigned int kind)
|
|
{
|
|
struct ctf_header *cth;
|
|
const struct ctf_type *ctt;
|
|
const char *tname;
|
|
uint32_t off, toff;
|
|
|
|
if (!db_ctf.ctf_found)
|
|
return (NULL);
|
|
|
|
cth = db_ctf.cth;
|
|
|
|
for (off = cth->cth_typeoff; off < cth->cth_stroff; off += toff) {
|
|
ctt = (struct ctf_type *)(db_ctf.data + off);
|
|
toff = db_ctf_type_len(ctt);
|
|
if (toff == 0) {
|
|
db_printf("incorrect type at offset %u", off);
|
|
break;
|
|
}
|
|
|
|
if (CTF_INFO_KIND(ctt->ctt_info) != kind)
|
|
continue;
|
|
|
|
tname = db_ctf_off2name(ctt->ctt_name);
|
|
if (tname == NULL)
|
|
continue;
|
|
|
|
if (strcmp(name, tname) == 0)
|
|
return (ctt);
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Return the CTF type corresponding to a given index in the type section.
|
|
*/
|
|
const struct ctf_type *
|
|
db_ctf_type_by_index(uint16_t index)
|
|
{
|
|
uint32_t offset = db_ctf.cth->cth_typeoff;
|
|
uint16_t idx = 1;
|
|
|
|
if (!db_ctf.ctf_found)
|
|
return NULL;
|
|
|
|
while (offset < db_ctf.cth->cth_stroff) {
|
|
const struct ctf_type *ctt;
|
|
uint32_t toff;
|
|
|
|
ctt = (struct ctf_type *)(db_ctf.data + offset);
|
|
if (idx == index)
|
|
return ctt;
|
|
|
|
toff = db_ctf_type_len(ctt);
|
|
if (toff == 0) {
|
|
db_printf("incorrect type at offset %u", offset);
|
|
break;
|
|
}
|
|
offset += toff;
|
|
idx++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Pretty print `addr'.
|
|
*/
|
|
void
|
|
db_ctf_pprint(const struct ctf_type *ctt, vaddr_t addr)
|
|
{
|
|
vaddr_t taddr = (vaddr_t)ctt;
|
|
const struct ctf_type *ref;
|
|
const struct ctf_array *arr;
|
|
uint16_t kind;
|
|
uint32_t eob, toff, i;
|
|
db_expr_t val;
|
|
size_t elm_size;
|
|
|
|
if (ctt == NULL)
|
|
return;
|
|
|
|
kind = CTF_INFO_KIND(ctt->ctt_info);
|
|
if (ctt->ctt_size <= CTF_MAX_SIZE)
|
|
toff = sizeof(struct ctf_stype);
|
|
else
|
|
toff = sizeof(struct ctf_type);
|
|
|
|
switch (kind) {
|
|
case CTF_K_ARRAY:
|
|
arr = (struct ctf_array *)(taddr + toff);
|
|
ref = db_ctf_type_by_index(arr->cta_contents);
|
|
elm_size = db_ctf_type_size(ref);
|
|
db_printf("[");
|
|
for (i = 0; i < arr->cta_nelems; i++) {
|
|
db_ctf_pprint(ref, addr + i * elm_size);
|
|
if (i + 1 < arr->cta_nelems)
|
|
db_printf(",");
|
|
}
|
|
db_printf("]");
|
|
break;
|
|
case CTF_K_ENUM:
|
|
db_ctf_pprint_enum(ctt, addr);
|
|
break;
|
|
case CTF_K_FLOAT:
|
|
case CTF_K_FUNCTION:
|
|
val = db_get_value(addr, sizeof(val), 0);
|
|
db_printf("%lx", (unsigned long)val);
|
|
break;
|
|
case CTF_K_INTEGER:
|
|
eob = db_get_value((taddr + toff), sizeof(eob), 0);
|
|
switch (CTF_INT_BITS(eob)) {
|
|
#ifndef __LP64__
|
|
case 64: {
|
|
uint64_t val64;
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
val64 = db_get_value(addr + 4, CTF_INT_BITS(eob) / 8,
|
|
CTF_INT_ENCODING(eob) & CTF_INT_SIGNED);
|
|
val64 <<= 32;
|
|
val64 |= db_get_value(addr, CTF_INT_BITS(eob) / 8, 0);
|
|
#else
|
|
val64 = db_get_value(addr, CTF_INT_BITS(eob) / 8,
|
|
CTF_INT_ENCODING(eob) & CTF_INT_SIGNED);
|
|
val64 <<= 32;
|
|
val64 |= db_get_value(addr + 4, CTF_INT_BITS(eob) / 8,
|
|
0);
|
|
#endif
|
|
if (CTF_INT_ENCODING(eob) & CTF_INT_SIGNED)
|
|
db_printf("%lld", val64);
|
|
else
|
|
db_printf("%llu", val64);
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
val = db_get_value(addr, CTF_INT_BITS(eob) / 8,
|
|
CTF_INT_ENCODING(eob) & CTF_INT_SIGNED);
|
|
if (CTF_INT_ENCODING(eob) & CTF_INT_SIGNED)
|
|
db_printf("%ld", val);
|
|
else
|
|
db_printf("%lu", val);
|
|
break;
|
|
}
|
|
break;
|
|
case CTF_K_STRUCT:
|
|
case CTF_K_UNION:
|
|
db_ctf_pprint_struct(ctt, addr);
|
|
break;
|
|
case CTF_K_POINTER:
|
|
db_ctf_pprint_ptr(ctt, addr);
|
|
break;
|
|
case CTF_K_TYPEDEF:
|
|
case CTF_K_VOLATILE:
|
|
case CTF_K_CONST:
|
|
case CTF_K_RESTRICT:
|
|
ref = db_ctf_type_by_index(ctt->ctt_type);
|
|
db_ctf_pprint(ref, addr);
|
|
break;
|
|
case CTF_K_UNKNOWN:
|
|
case CTF_K_FORWARD:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
db_ctf_pprint_struct(const struct ctf_type *ctt, vaddr_t addr)
|
|
{
|
|
const char *name, *p = (const char *)ctt;
|
|
const struct ctf_type *ref;
|
|
uint32_t toff;
|
|
uint64_t size;
|
|
uint16_t i, vlen;
|
|
|
|
vlen = CTF_INFO_VLEN(ctt->ctt_info);
|
|
|
|
if (ctt->ctt_size <= CTF_MAX_SIZE) {
|
|
size = ctt->ctt_size;
|
|
toff = sizeof(struct ctf_stype);
|
|
} else {
|
|
size = CTF_TYPE_LSIZE(ctt);
|
|
toff = sizeof(struct ctf_type);
|
|
}
|
|
|
|
db_printf("{");
|
|
if (size < CTF_LSTRUCT_THRESH) {
|
|
for (i = 0; i < vlen; i++) {
|
|
struct ctf_member *ctm;
|
|
|
|
ctm = (struct ctf_member *)(p + toff);
|
|
toff += sizeof(struct ctf_member);
|
|
|
|
name = db_ctf_off2name(ctm->ctm_name);
|
|
if (name != NULL)
|
|
db_printf("%s = ", name);
|
|
ref = db_ctf_type_by_index(ctm->ctm_type);
|
|
db_ctf_pprint(ref, addr + ctm->ctm_offset / 8);
|
|
if (i < vlen - 1)
|
|
db_printf(", ");
|
|
}
|
|
} else {
|
|
for (i = 0; i < vlen; i++) {
|
|
struct ctf_lmember *ctlm;
|
|
|
|
ctlm = (struct ctf_lmember *)(p + toff);
|
|
toff += sizeof(struct ctf_lmember);
|
|
|
|
name = db_ctf_off2name(ctlm->ctlm_name);
|
|
if (name != NULL)
|
|
db_printf("%s = ", name);
|
|
ref = db_ctf_type_by_index(ctlm->ctlm_type);
|
|
db_ctf_pprint(ref, addr +
|
|
CTF_LMEM_OFFSET(ctlm) / 8);
|
|
if (i < vlen - 1)
|
|
db_printf(", ");
|
|
}
|
|
}
|
|
db_printf("}");
|
|
}
|
|
|
|
void
|
|
db_ctf_pprint_enum(const struct ctf_type *ctt, vaddr_t addr)
|
|
{
|
|
const char *name = NULL, *p = (const char *)ctt;
|
|
struct ctf_enum *cte;
|
|
uint32_t toff;
|
|
int32_t val;
|
|
uint16_t i, vlen;
|
|
|
|
vlen = CTF_INFO_VLEN(ctt->ctt_info);
|
|
toff = sizeof(struct ctf_stype);
|
|
|
|
val = (int32_t)db_get_value(addr, sizeof(val), 1);
|
|
for (i = 0; i < vlen; i++) {
|
|
cte = (struct ctf_enum *)(p + toff);
|
|
toff += sizeof(*cte);
|
|
|
|
if (val == cte->cte_value) {
|
|
name = db_ctf_off2name(cte->cte_name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (name != NULL)
|
|
db_printf("%s", name);
|
|
else
|
|
db_printf("#%d", val);
|
|
}
|
|
|
|
void
|
|
db_ctf_pprint_ptr(const struct ctf_type *ctt, vaddr_t addr)
|
|
{
|
|
const char *name, *modif = "";
|
|
const struct ctf_type *ref;
|
|
uint16_t kind;
|
|
unsigned long ptr;
|
|
|
|
ref = db_ctf_type_by_index(ctt->ctt_type);
|
|
kind = CTF_INFO_KIND(ref->ctt_info);
|
|
|
|
switch (kind) {
|
|
case CTF_K_VOLATILE:
|
|
modif = "volatile ";
|
|
ref = db_ctf_type_by_index(ref->ctt_type);
|
|
break;
|
|
case CTF_K_CONST:
|
|
modif = "const ";
|
|
ref = db_ctf_type_by_index(ref->ctt_type);
|
|
break;
|
|
case CTF_K_STRUCT:
|
|
modif = "struct ";
|
|
break;
|
|
case CTF_K_UNION:
|
|
modif = "union ";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
name = db_ctf_off2name(ref->ctt_name);
|
|
if (name != NULL)
|
|
db_printf("(%s%s *)", modif, name);
|
|
|
|
ptr = (unsigned long)db_get_value(addr, sizeof(ptr), 0);
|
|
|
|
db_printf("0x%lx", ptr);
|
|
}
|
|
|
|
static const char *
|
|
db_ctf_off2name(uint32_t offset)
|
|
{
|
|
const char *name;
|
|
|
|
if (!db_ctf.ctf_found)
|
|
return NULL;
|
|
|
|
if (CTF_NAME_STID(offset) != CTF_STRTAB_0)
|
|
return "external";
|
|
|
|
if (CTF_NAME_OFFSET(offset) >= db_ctf.cth->cth_strlen)
|
|
return "exceeds strlab";
|
|
|
|
if (db_ctf.cth->cth_stroff + CTF_NAME_OFFSET(offset) >= db_ctf.dlen)
|
|
return "invalid";
|
|
|
|
name = db_ctf.data + db_ctf.cth->cth_stroff + CTF_NAME_OFFSET(offset);
|
|
if (*name == '\0')
|
|
return NULL;
|
|
|
|
return name;
|
|
}
|
|
|
|
static char *
|
|
db_ctf_decompress(const char *buf, size_t size, size_t len)
|
|
{
|
|
z_stream stream;
|
|
char *data;
|
|
int error;
|
|
|
|
data = malloc(len, M_TEMP, M_WAITOK|M_ZERO|M_CANFAIL);
|
|
if (data == NULL)
|
|
return NULL;
|
|
|
|
memset(&stream, 0, sizeof(stream));
|
|
stream.next_in = (void *)buf;
|
|
stream.avail_in = size;
|
|
stream.next_out = data;
|
|
stream.avail_out = len;
|
|
|
|
if ((error = inflateInit(&stream)) != Z_OK) {
|
|
db_printf("zlib inflateInit failed: %s", zError(error));
|
|
goto exit;
|
|
}
|
|
|
|
if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
|
|
db_printf("zlib inflate failed: %s", zError(error));
|
|
inflateEnd(&stream);
|
|
goto exit;
|
|
}
|
|
|
|
if ((error = inflateEnd(&stream)) != Z_OK) {
|
|
db_printf("zlib inflateEnd failed: %s", zError(error));
|
|
goto exit;
|
|
}
|
|
|
|
if (stream.total_out != len) {
|
|
db_printf("decompression failed: %lu != %zu",
|
|
stream.total_out, len);
|
|
goto exit;
|
|
}
|
|
|
|
return data;
|
|
|
|
exit:
|
|
free(data, M_DEVBUF, len);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* pprint <symbol name>
|
|
*/
|
|
void
|
|
db_ctf_pprint_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
|
|
{
|
|
Elf_Sym *st;
|
|
const struct ctf_type *ctt;
|
|
int t;
|
|
|
|
if (!db_ctf.ctf_found) {
|
|
db_printf("No CTF data found\n");
|
|
db_flush_lex();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Read the struct name from the debugger input.
|
|
*/
|
|
t = db_read_token();
|
|
if (t != tIDENT) {
|
|
db_printf("Bad symbol name\n");
|
|
db_flush_lex();
|
|
return;
|
|
}
|
|
|
|
if ((st = db_symbol_by_name(db_tok_string, &addr)) == NULL) {
|
|
db_printf("Symbol not found %s\n", db_tok_string);
|
|
db_flush_lex();
|
|
return;
|
|
}
|
|
|
|
if ((ctt = db_ctf_type_by_symbol(st)) == NULL) {
|
|
modif[0] = '\0';
|
|
db_print_cmd(addr, 0, 0, modif);
|
|
db_flush_lex();
|
|
return;
|
|
}
|
|
|
|
db_printf("%s:\t", db_tok_string);
|
|
db_ctf_pprint(ctt, addr);
|
|
db_printf("\n");
|
|
}
|
|
|
|
/*
|
|
* show struct <struct name> [addr]: displays the data starting at addr
|
|
* (`dot' if unspecified) as a struct of the given type.
|
|
*/
|
|
void
|
|
db_ctf_show_struct(db_expr_t addr, int have_addr, db_expr_t count,
|
|
char *modifiers)
|
|
{
|
|
const struct ctf_type *ctt;
|
|
const char *name;
|
|
uint64_t sz;
|
|
int t;
|
|
|
|
/*
|
|
* Read the struct name from the debugger input.
|
|
*/
|
|
t = db_read_token();
|
|
if (t != tIDENT) {
|
|
db_printf("Bad struct name\n");
|
|
db_flush_lex();
|
|
return;
|
|
}
|
|
name = db_tok_string;
|
|
|
|
ctt = db_ctf_type_by_name(name, CTF_K_STRUCT);
|
|
if (ctt == NULL) {
|
|
db_printf("unknown struct %s\n", name);
|
|
db_flush_lex();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Read the address, if any, from the debugger input.
|
|
* In that case, update `dot' value.
|
|
*/
|
|
if (db_expression(&addr)) {
|
|
db_dot = (vaddr_t)addr;
|
|
db_last_addr = db_dot;
|
|
} else
|
|
addr = (db_expr_t)db_dot;
|
|
|
|
db_skip_to_eol();
|
|
|
|
/*
|
|
* Display the structure contents.
|
|
*/
|
|
sz = (ctt->ctt_size <= CTF_MAX_SIZE) ?
|
|
ctt->ctt_size : CTF_TYPE_LSIZE(ctt);
|
|
db_printf("struct %s at %p (%llu bytes) ", name, (void *)addr, sz);
|
|
db_ctf_pprint_struct(ctt, addr);
|
|
}
|