aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBojan Novković <bnovkov@FreeBSD.org>2024-03-22 03:01:34 +0000
committerBojan Novković <bnovkov@FreeBSD.org>2024-03-22 03:03:33 +0000
commitc21bc6f3c2425de74141bfee07b609bf65b5a6b3 (patch)
tree06b3412042dd10e0edb1f6f29140856aeaf65a2a
parent312a05c39e5fd79b37ee6f922462232797f56a2a (diff)
downloadsrc-c21bc6f3c2425de74141bfee07b609bf65b5a6b3.tar.gz
src-c21bc6f3c2425de74141bfee07b609bf65b5a6b3.zip
ddb: Add CTF-based pretty printing
Add basic CTF support and a CTF-powered pretty-printer to ddb. The db_ctf.* files expose a basic interface for fetching type data for ELF symbols, interacting with the CTF string table, and translating type identifiers to type data. The db_pprint.c file uses those interfaces to implement a pretty-printer for all kernel ELF symbols. The pretty-printer works with symbol names and arbitrary addresses: pprint struct thread 0xffffffff8194ad90 Pretty-printing currently only works after the root filesystem gets mounted because the CTF info is not available during early boot. Differential Revision: https://reviews.freebsd.org/D37899 Approved by: markj (mentor)
-rw-r--r--share/man/man4/ddb.426
-rw-r--r--sys/conf/files2
-rw-r--r--sys/ddb/db_command.c1
-rw-r--r--sys/ddb/db_ctf.c326
-rw-r--r--sys/ddb/db_ctf.h64
-rw-r--r--sys/ddb/db_pprint.c450
-rw-r--r--sys/ddb/ddb.h1
-rw-r--r--sys/kern/kern_ctf.c40
-rw-r--r--sys/kern/kern_linker.c68
-rw-r--r--sys/kern/link_elf.c37
-rw-r--r--sys/kern/link_elf_obj.c14
-rw-r--r--sys/kern/linker_if.m23
-rw-r--r--sys/sys/linker.h3
13 files changed, 1054 insertions, 1 deletions
diff --git a/share/man/man4/ddb.4 b/share/man/man4/ddb.4
index 3648c9ca58cb..f3443cbac127 100644
--- a/share/man/man4/ddb.4
+++ b/share/man/man4/ddb.4
@@ -289,6 +289,32 @@ eax = xxxxxx
ecx = yyyyyy
.Ed
.Pp
+.It Ic pprint Ns Oo Li / Ns Cm d depth Oc Oo Ar name Oc
+Pretty-print symbol specified by
+.Ar name
+using CTF debugging data. Works for all symbols exported by the kernel and loaded kernel modules.
+.Pp
+If the
+.Cm d
+modifier has been specified, contents of structs nested up to
+.Ar depth
+levels deep will also be included in the output.
+.Ed
+.Pp
+.It Ic pprint struct Ns Oo Li / Ns Cm d depth Ic Oc Oo Ar name Oc Ns Op Ns Ar addr
+Print memory at
+.Ar addr
+as struct
+.Ar name Ns .
+Works for all structs defined by the kernel and loaded kernel modules.
+.Pp
+If the
+.Cm d
+modifier has been specified, contents of structs nested up to
+.Ar depth
+levels deep will also be included in the output.
+.Ed
+.Pp
.It Xo
.Ic write Ns Op Li / Ns Cm bhl
.Ar addr expr1 Op Ar expr2 ...
diff --git a/sys/conf/files b/sys/conf/files
index c902bcfdbd52..021829408c0f 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -718,12 +718,14 @@ ddb/db_access.c optional ddb
ddb/db_break.c optional ddb
ddb/db_capture.c optional ddb
ddb/db_command.c optional ddb
+ddb/db_ctf.c optional ddb
ddb/db_examine.c optional ddb
ddb/db_expr.c optional ddb
ddb/db_input.c optional ddb
ddb/db_lex.c optional ddb
ddb/db_main.c optional ddb
ddb/db_output.c optional ddb
+ddb/db_pprint.c optional ddb
ddb/db_print.c optional ddb
ddb/db_ps.c optional ddb
ddb/db_run.c optional ddb
diff --git a/sys/ddb/db_command.c b/sys/ddb/db_command.c
index 9d79e3b2a6d3..0c88d496f6b8 100644
--- a/sys/ddb/db_command.c
+++ b/sys/ddb/db_command.c
@@ -163,6 +163,7 @@ static struct db_command db_cmds[] = {
DB_CMD("capture", db_capture_cmd, CS_OWN|DB_CMD_MEMSAFE),
DB_CMD("textdump", db_textdump_cmd, CS_OWN|DB_CMD_MEMSAFE),
DB_CMD("findstack", db_findstack_cmd, 0),
+ DB_CMD("pprint", db_pprint_cmd, CS_OWN),
};
struct db_command_table db_cmd_table = LIST_HEAD_INITIALIZER(db_cmd_table);
diff --git a/sys/ddb/db_ctf.c b/sys/ddb/db_ctf.c
new file mode 100644
index 000000000000..03145064885c
--- /dev/null
+++ b/sys/ddb/db_ctf.c
@@ -0,0 +1,326 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2023 Bojan Novković <bnovkov@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ctype.h>
+#include <sys/linker.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_ctf.h>
+
+static const ctf_header_t *
+db_ctf_fetch_cth(linker_ctf_t *lc)
+{
+ return (const ctf_header_t *)lc->ctftab;
+}
+
+/*
+ * Tries to look up the ELF symbol -> CTF type identifier mapping by scanning
+ * the CTF object section.
+ */
+static uint32_t
+sym_to_objtoff(linker_ctf_t *lc, const Elf_Sym *sym, const Elf_Sym *symtab,
+ const Elf_Sym *symtab_end)
+{
+ const ctf_header_t *hp = db_ctf_fetch_cth(lc);
+ uint32_t objtoff = hp->cth_objtoff;
+ const size_t idwidth = 4;
+
+ /* Ignore non-object symbols */
+ if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT) {
+ return (DB_CTF_INVALID_OFF);
+ }
+ /* Sanity check */
+ if (!(sym >= symtab && sym <= symtab_end)) {
+ return (DB_CTF_INVALID_OFF);
+ }
+
+ for (const Elf_Sym *symp = symtab; symp < symtab_end; symp++) {
+ /* Make sure we do not go beyond the objtoff section */
+ if (objtoff >= hp->cth_funcoff) {
+ objtoff = DB_CTF_INVALID_OFF;
+ break;
+ }
+ if (symp->st_name == 0 || symp->st_shndx == SHN_UNDEF) {
+ continue;
+ }
+ if (symp->st_shndx == SHN_ABS && symp->st_value == 0) {
+ continue;
+ }
+
+ /* Skip non-object symbols */
+ if (ELF_ST_TYPE(symp->st_info) != STT_OBJECT) {
+ continue;
+ }
+ if (symp == sym) {
+ break;
+ }
+ objtoff += idwidth;
+ }
+
+ return (objtoff);
+}
+
+/*
+ * Returns the size of CTF type 't'.
+ */
+static u_int
+db_ctf_type_size(struct ctf_type_v3 *t)
+{
+ u_int vlen, kind, ssize;
+ u_int type_struct_size, kind_size;
+
+ vlen = CTF_V3_INFO_VLEN(t->ctt_info);
+ kind = CTF_V3_INFO_KIND(t->ctt_info);
+ ssize = ((t->ctt_size == CTF_V3_LSIZE_SENT) ? CTF_TYPE_LSIZE(t) :
+ t->ctt_size);
+ type_struct_size = ((t->ctt_size == CTF_V3_LSIZE_SENT) ?
+ sizeof(struct ctf_type_v3) :
+ sizeof(struct ctf_stype_v3));
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ kind_size = sizeof(uint32_t);
+ break;
+ case CTF_K_ARRAY:
+ kind_size = sizeof(struct ctf_array_v3);
+ break;
+ case CTF_K_UNION:
+ case CTF_K_STRUCT:
+ kind_size = vlen *
+ ((ssize < CTF_V3_LSTRUCT_THRESH) ?
+ sizeof(struct ctf_member_v3) :
+ sizeof(struct ctf_lmember_v3));
+ break;
+ case CTF_K_ENUM:
+ kind_size = vlen * sizeof(struct ctf_enum);
+ break;
+ case CTF_K_FUNCTION:
+ kind_size = vlen * sizeof(uint32_t);
+ break;
+ case CTF_K_UNKNOWN:
+ case CTF_K_FORWARD:
+ case CTF_K_POINTER:
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ kind_size = 0;
+ break;
+ default:
+ db_printf("Error: invalid CTF type kind encountered\n");
+ return (-1);
+ }
+
+ return (type_struct_size + kind_size);
+}
+
+/*
+ * Looks up type name 'name' in the CTF string table and returns the
+ * corresponding CTF type struct, if any.
+ */
+struct ctf_type_v3 *
+db_ctf_typename_to_type(linker_ctf_t *lc, const char *name)
+{
+ const ctf_header_t *hp = db_ctf_fetch_cth(lc);
+ char *start, *cur, *end;
+ uint32_t stroff = hp->cth_stroff;
+ uint32_t typeoff = hp->cth_typeoff;
+ uint32_t name_stroff;
+ const uint8_t *ctfstart = (const uint8_t *)hp + sizeof(ctf_header_t);
+
+ u_int skiplen;
+
+ /* Scan ctf strtab for typename. */
+ start = cur = __DECONST(char *, hp) + sizeof(ctf_header_t) +
+ hp->cth_stroff;
+ end = cur + hp->cth_strlen;
+ while (cur < end) {
+ if (strcmp(cur, name) == 0)
+ break;
+ cur += strlen(cur) + 1;
+ }
+ if (cur >= end)
+ return (NULL);
+ name_stroff = (uint32_t)(cur - start);
+
+ /* Scan for type containing the found stroff. */
+ while (typeoff < stroff) {
+ struct ctf_type_v3 *t =
+ (struct ctf_type_v3 *)(__DECONST(uint8_t *, ctfstart) +
+ typeoff);
+ /* We found the type struct */
+ if (t->ctt_name == name_stroff) {
+ break;
+ }
+ if ((skiplen = db_ctf_type_size(t)) == -1) {
+ return (NULL);
+ }
+ typeoff += skiplen;
+ }
+ if (typeoff < stroff) {
+ return (struct ctf_type_v3 *)(__DECONST(uint8_t *, ctfstart) +
+ typeoff);
+ } else { /* A type struct was not found */
+ return (NULL);
+ }
+}
+
+/*
+ * Wrapper used by the kernel linker CTF routines.
+ * Currently used to implement lookup of CTF types accross all loaded kernel
+ * modules.
+ */
+bool
+db_ctf_lookup_typename(linker_ctf_t *lc, const char *typename)
+{
+ return (db_ctf_typename_to_type(lc, typename) != NULL);
+}
+
+/*
+ * Returns the type corresponding to the 'typeid' parameter from the CTF type
+ * section.
+ */
+struct ctf_type_v3 *
+db_ctf_typeid_to_type(db_ctf_sym_data_t sd, uint32_t typeid)
+{
+ const ctf_header_t *hp = db_ctf_fetch_cth(&sd->lc);
+ const uint8_t *ctfstart = (const uint8_t *)hp + sizeof(ctf_header_t);
+ uint32_t typeoff = hp->cth_typeoff;
+ uint32_t stroff = hp->cth_stroff;
+ /* CTF typeids start at 0x1 */
+ size_t cur_typeid = 1;
+ u_int skiplen;
+
+ /* Find corresponding type */
+ while (typeoff < stroff) {
+ struct ctf_type_v3 *t =
+ (struct ctf_type_v3 *)(__DECONST(uint8_t *, ctfstart) +
+ typeoff);
+
+ /* We found the type struct */
+ if (cur_typeid == typeid) {
+ break;
+ }
+ cur_typeid++;
+ if ((skiplen = db_ctf_type_size(t)) == -1) {
+ return (NULL);
+ }
+ typeoff += skiplen;
+ }
+ if (typeoff < stroff) {
+ return (struct ctf_type_v3 *)(__DECONST(uint8_t *, ctfstart) +
+ typeoff);
+ } else { /* A type struct was not found */
+ return (NULL);
+ }
+}
+
+const char *
+db_ctf_stroff_to_str(db_ctf_sym_data_t sd, uint32_t off)
+{
+ const ctf_header_t *hp = db_ctf_fetch_cth(&sd->lc);
+ uint32_t stroff = hp->cth_stroff + off;
+ const char *ret;
+
+ if (stroff >= (hp->cth_stroff + hp->cth_strlen)) {
+ return ("invalid");
+ }
+ ret = ((const char *)hp + sizeof(ctf_header_t)) + stroff;
+ if (*ret == '\0') {
+ return (NULL);
+ }
+
+ return (ret);
+}
+
+/*
+ * Tries to find the type of the symbol specified in 'sd->sym'.
+ */
+struct ctf_type_v3 *
+db_ctf_sym_to_type(db_ctf_sym_data_t sd)
+{
+ uint32_t objtoff, typeid;
+ const Elf_Sym *symtab, *symtab_end;
+
+ if (sd->sym == NULL) {
+ return (NULL);
+ }
+ symtab = sd->lc.symtab;
+ symtab_end = symtab + sd->lc.nsym;
+
+ objtoff = sym_to_objtoff(&sd->lc, sd->sym, symtab, symtab_end);
+ /* Sanity check - should not happen */
+ if (objtoff == DB_CTF_INVALID_OFF) {
+ db_printf("Could not find CTF object offset.\n");
+ return (NULL);
+ }
+
+ typeid = *(
+ const uint32_t *)(sd->lc.ctftab + sizeof(ctf_header_t) + objtoff);
+
+ return (db_ctf_typeid_to_type(sd, typeid));
+}
+
+/*
+ * Scans the kernel file and all loaded module for symbol 'name'.
+ */
+int
+db_ctf_find_symbol(const char *name, db_ctf_sym_data_t sd)
+{
+ int error;
+ c_linker_sym_t lsym = NULL;
+
+ error = linker_ctf_lookup_sym_ddb(name, &lsym, &sd->lc);
+ if (error != 0) {
+ db_printf(
+ "failed to look up symbol and CTF info for %s: error %d\n",
+ name, error);
+ return (error);
+ }
+ sd->sym = __DECONST(Elf_Sym *, lsym);
+
+ return (0);
+}
+
+/*
+ * Scans the kernel file and all loaded module for type specified by 'typename'.
+ */
+struct ctf_type_v3 *
+db_ctf_find_typename(db_ctf_sym_data_t sd, const char *typename)
+{
+ if (linker_ctf_lookup_typename_ddb(&sd->lc, typename) != 0) {
+ return (NULL);
+ }
+ return (db_ctf_typename_to_type(&sd->lc, typename));
+}
diff --git a/sys/ddb/db_ctf.h b/sys/ddb/db_ctf.h
new file mode 100644
index 000000000000..6da5f76b6cf6
--- /dev/null
+++ b/sys/ddb/db_ctf.h
@@ -0,0 +1,64 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2023 Bojan Novković <bnovkov@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DDB_DB_CTF_H_
+#define _DDB_DB_CTF_H_
+
+#include <sys/types.h>
+#include <sys/ctf.h>
+#include <sys/linker.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_sym.h>
+
+#define DB_CTF_INVALID_OFF 0xffffffff
+
+struct db_ctf_sym_data {
+ linker_ctf_t lc;
+ Elf_Sym *sym;
+};
+
+typedef struct db_ctf_sym_data *db_ctf_sym_data_t;
+
+/*
+ * Routines for finding symbols and CTF info accross all loaded linker files.
+ */
+int db_ctf_find_symbol(const char *name, db_ctf_sym_data_t sd);
+struct ctf_type_v3 *db_ctf_find_typename(db_ctf_sym_data_t sd,
+ const char *typename);
+bool db_ctf_lookup_typename(linker_ctf_t *lc, const char *typename);
+
+/*
+ * Routines for working with CTF data.
+ */
+struct ctf_type_v3 *db_ctf_sym_to_type(db_ctf_sym_data_t sd);
+const char *db_ctf_stroff_to_str(db_ctf_sym_data_t sd, uint32_t off);
+struct ctf_type_v3 *db_ctf_typename_to_type(linker_ctf_t *lc, const char *name);
+struct ctf_type_v3 *db_ctf_typeid_to_type(db_ctf_sym_data_t sd,
+ uint32_t typeid);
+
+#endif /* !_DDB_DB_CTF_H_ */
diff --git a/sys/ddb/db_pprint.c b/sys/ddb/db_pprint.c
new file mode 100644
index 000000000000..dc7582864957
--- /dev/null
+++ b/sys/ddb/db_pprint.c
@@ -0,0 +1,450 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 Bojan Novković <bnovkov@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ctype.h>
+#include <sys/linker.h>
+
+#include <machine/stdarg.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_ctf.h>
+#include <ddb/db_lex.h>
+#include <ddb/db_sym.h>
+#include <ddb/db_access.h>
+
+#define DB_PPRINT_DEFAULT_DEPTH 1
+
+static void db_pprint_type(db_addr_t addr, struct ctf_type_v3 *type,
+ u_int depth);
+
+static u_int max_depth = DB_PPRINT_DEFAULT_DEPTH;
+static struct db_ctf_sym_data sym_data;
+
+/*
+ * Pretty-prints a CTF_INT type.
+ */
+static inline void
+db_pprint_int(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
+{
+ uint32_t data;
+ size_t type_struct_size;
+
+ type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
+ sizeof(struct ctf_type_v3) :
+ sizeof(struct ctf_stype_v3);
+
+ data = db_get_value((db_expr_t)type + type_struct_size,
+ sizeof(uint32_t), 0);
+ u_int bits = CTF_INT_BITS(data);
+ boolean_t sign = !!(CTF_INT_ENCODING(data) & CTF_INT_SIGNED);
+
+ if (db_pager_quit) {
+ return;
+ }
+ if (bits > 64) {
+ db_printf("Invalid size '%d' found for integer type\n", bits);
+ return;
+ }
+ db_printf("0x%lx",
+ db_get_value(addr, (bits / 8) ? (bits / 8) : 1, sign));
+}
+
+/*
+ * Pretty-prints a struct. Nested structs are pretty-printed up 'depth' nested
+ * levels.
+ */
+static inline void
+db_pprint_struct(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
+{
+ size_t type_struct_size;
+ size_t struct_size;
+ struct ctf_type_v3 *mtype;
+ const char *mname;
+ db_addr_t maddr;
+ u_int vlen;
+
+ type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
+ sizeof(struct ctf_type_v3) :
+ sizeof(struct ctf_stype_v3);
+ struct_size = ((type->ctt_size == CTF_V3_LSIZE_SENT) ?
+ CTF_TYPE_LSIZE(type) :
+ type->ctt_size);
+ vlen = CTF_V3_INFO_VLEN(type->ctt_info);
+
+ if (db_pager_quit) {
+ return;
+ }
+ if (depth > max_depth) {
+ db_printf("{ ... }");
+ return;
+ }
+ db_printf("{\n");
+
+ if (struct_size < CTF_V3_LSTRUCT_THRESH) {
+ struct ctf_member_v3 *mp, *endp;
+
+ mp = (struct ctf_member_v3 *)((db_addr_t)type +
+ type_struct_size);
+ endp = mp + vlen;
+ for (; mp < endp; mp++) {
+ if (db_pager_quit) {
+ return;
+ }
+ mtype = db_ctf_typeid_to_type(&sym_data, mp->ctm_type);
+ maddr = addr + mp->ctm_offset;
+ mname = db_ctf_stroff_to_str(&sym_data, mp->ctm_name);
+ db_indent = depth;
+ if (mname != NULL) {
+ db_iprintf("%s = ", mname);
+ } else {
+ db_iprintf("");
+ }
+
+ db_pprint_type(maddr, mtype, depth + 1);
+ db_printf(",\n");
+ }
+ } else {
+ struct ctf_lmember_v3 *mp, *endp;
+
+ mp = (struct ctf_lmember_v3 *)((db_addr_t)type +
+ type_struct_size);
+ endp = mp + vlen;
+ for (; mp < endp; mp++) {
+ if (db_pager_quit) {
+ return;
+ }
+ mtype = db_ctf_typeid_to_type(&sym_data, mp->ctlm_type);
+ maddr = addr + CTF_LMEM_OFFSET(mp);
+ mname = db_ctf_stroff_to_str(&sym_data, mp->ctlm_name);
+ db_indent = depth;
+ if (mname != NULL) {
+ db_iprintf("%s = ", mname);
+ } else {
+ db_iprintf("");
+ }
+
+ db_pprint_type(maddr, mtype, depth + 1);
+ db_printf(",");
+ }
+ }
+ db_indent = depth - 1;
+ db_iprintf("}");
+}
+
+/*
+ * Pretty-prints an array. Each array member is printed out in a separate line
+ * indented with 'depth' spaces.
+ */
+static inline void
+db_pprint_arr(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
+{
+ struct ctf_type_v3 *elem_type;
+ struct ctf_array_v3 *arr;
+ db_addr_t elem_addr, end;
+ size_t type_struct_size;
+ size_t elem_size;
+
+ type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
+ sizeof(struct ctf_type_v3) :
+ sizeof(struct ctf_stype_v3);
+ arr = (struct ctf_array_v3 *)((db_addr_t)type + type_struct_size);
+ elem_type = db_ctf_typeid_to_type(&sym_data, arr->cta_contents);
+ elem_size = ((elem_type->ctt_size == CTF_V3_LSIZE_SENT) ?
+ CTF_TYPE_LSIZE(elem_type) :
+ elem_type->ctt_size);
+ elem_addr = addr;
+ end = addr + (arr->cta_nelems * elem_size);
+
+ db_indent = depth;
+ db_printf("[\n");
+ /* Loop through and print individual elements. */
+ for (; elem_addr < end; elem_addr += elem_size) {
+ if (db_pager_quit) {
+ return;
+ }
+ db_iprintf("");
+ db_pprint_type(elem_addr, elem_type, depth);
+ if ((elem_addr + elem_size) < end) {
+ db_printf(",\n");
+ }
+ }
+ db_printf("\n");
+ db_indent = depth - 1;
+ db_iprintf("]");
+}
+
+/*
+ * Pretty-prints an enum value. Also prints out symbolic name of value, if any.
+ */
+static inline void
+db_pprint_enum(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
+{
+ struct ctf_enum *ep, *endp;
+ size_t type_struct_size;
+ const char *valname;
+ db_expr_t val;
+ u_int vlen;
+
+ type_struct_size = (type->ctt_size == CTF_V3_LSIZE_SENT) ?
+ sizeof(struct ctf_type_v3) :
+ sizeof(struct ctf_stype_v3);
+ vlen = CTF_V3_INFO_VLEN(type->ctt_info);
+ val = db_get_value(addr, sizeof(int), 0);
+
+ if (db_pager_quit) {
+ return;
+ }
+ ep = (struct ctf_enum *)((db_addr_t)type + type_struct_size);
+ endp = ep + vlen;
+ for (; ep < endp; ep++) {
+ if (val == ep->cte_value) {
+ valname = db_ctf_stroff_to_str(&sym_data, ep->cte_name);
+ if (valname != NULL)
+ db_printf("%s (0x%lx)", valname, val);
+ else
+ db_printf("(0x%lx)", val);
+ break;
+ }
+ }
+}
+
+/*
+ * Pretty-prints a pointer. If the 'depth' parameter is less than the
+ * 'max_depth' global var, the pointer is "dereference", i.e. the contents of
+ * the memory it points to are also printed. The value of the pointer is printed
+ * otherwise.
+ */
+static inline void
+db_pprint_ptr(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
+{
+ struct ctf_type_v3 *ref_type;
+ const char *qual = "";
+ const char *name;
+ db_addr_t val;
+ u_int kind;
+
+ ref_type = db_ctf_typeid_to_type(&sym_data, type->ctt_type);
+ kind = CTF_V3_INFO_KIND(ref_type->ctt_info);
+ switch (kind) {
+ case CTF_K_STRUCT:
+ qual = "struct ";
+ break;
+ case CTF_K_VOLATILE:
+ qual = "volatile ";
+ break;
+ case CTF_K_CONST:
+ qual = "const ";
+ break;
+ default:
+ break;
+ }
+
+ val = db_get_value(addr, sizeof(db_addr_t), false);
+ if (depth < max_depth) {
+ /* Print contents of memory pointed to by this pointer. */
+ db_pprint_type(addr, ref_type, depth + 1);
+ } else {
+ name = db_ctf_stroff_to_str(&sym_data, ref_type->ctt_name);
+ db_indent = depth;
+ if (name != NULL)
+ db_printf("(%s%s *) 0x%lx", qual, name, val);
+ else
+ db_printf("0x%lx", val);
+ }
+}
+
+/*
+ * Pretty-print dispatching function.
+ */
+static void
+db_pprint_type(db_addr_t addr, struct ctf_type_v3 *type, u_int depth)
+{
+
+ if (db_pager_quit) {
+ return;
+ }
+ if (type == NULL) {
+ db_printf("unknown type");
+ return;
+ }
+
+ switch (CTF_V3_INFO_KIND(type->ctt_info)) {
+ case CTF_K_INTEGER:
+ db_pprint_int(addr, type, depth);
+ break;
+ case CTF_K_UNION:
+ case CTF_K_STRUCT:
+ db_pprint_struct(addr, type, depth);
+ break;
+ case CTF_K_FUNCTION:
+ case CTF_K_FLOAT:
+ db_indent = depth;
+ db_iprintf("0x%lx", addr);
+ break;
+ case CTF_K_POINTER:
+ db_pprint_ptr(addr, type, depth);
+ break;
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_RESTRICT:
+ case CTF_K_CONST: {
+ struct ctf_type_v3 *ref_type = db_ctf_typeid_to_type(&sym_data,
+ type->ctt_type);
+ db_pprint_type(addr, ref_type, depth);
+ break;
+ }
+ case CTF_K_ENUM:
+ db_pprint_enum(addr, type, depth);
+ break;
+ case CTF_K_ARRAY:
+ db_pprint_arr(addr, type, depth);
+ break;
+ case CTF_K_UNKNOWN:
+ case CTF_K_FORWARD:
+ default:
+ break;
+ }
+}
+
+/*
+ * Symbol pretty-printing command.
+ * Syntax: pprint [/d depth] <sym_name>
+ */
+static void
+db_pprint_symbol_cmd(const char *name)
+{
+ db_addr_t addr;
+ int db_indent_old;
+ const char *type_name = NULL;
+ struct ctf_type_v3 *type = NULL;
+
+ if (db_pager_quit) {
+ return;
+ }
+ /* Clear symbol and CTF info */
+ memset(&sym_data, 0, sizeof(struct db_ctf_sym_data));
+ if (db_ctf_find_symbol(name, &sym_data) != 0) {
+ db_error("Symbol not found\n");
+ }
+ if (ELF_ST_TYPE(sym_data.sym->st_info) != STT_OBJECT) {
+ db_error("Symbol is not a variable\n");
+ }
+ addr = sym_data.sym->st_value;
+ type = db_ctf_sym_to_type(&sym_data);
+ if (type == NULL) {
+ db_error("Can't find CTF type info\n");
+ }
+ type_name = db_ctf_stroff_to_str(&sym_data, type->ctt_name);
+ if (type_name != NULL)
+ db_printf("%s ", type_name);
+ db_printf("%s = ", name);
+
+ db_indent_old = db_indent;
+ db_pprint_type(addr, type, 0);
+ db_indent = db_indent_old;
+}
+
+/*
+ * Command for pretty-printing arbitrary addresses.
+ * Syntax: pprint [/d depth] struct <struct_name> <addr>
+ */
+static void
+db_pprint_struct_cmd(db_expr_t addr, const char *struct_name)
+{
+ int db_indent_old;
+ struct ctf_type_v3 *type = NULL;
+
+ type = db_ctf_find_typename(&sym_data, struct_name);
+ if (type == NULL) {
+ db_error("Can't find CTF type info\n");
+ return;
+ }
+
+ db_printf("struct %s ", struct_name);
+ db_printf("%p = ", (void *)addr);
+
+ db_indent_old = db_indent;
+ db_pprint_type(addr, type, 0);
+ db_indent = db_indent_old;
+}
+
+/*
+ * Pretty print an address or a symbol.
+ */
+void
+db_pprint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
+{
+ int t = 0;
+ const char *name;
+
+ /* Set default depth */
+ max_depth = DB_PPRINT_DEFAULT_DEPTH;
+ /* Parse print modifiers */
+ t = db_read_token();
+ if (t == tSLASH) {
+ t = db_read_token();
+ if (t != tIDENT) {
+ db_error("Invalid flag passed\n");
+ }
+ /* Parse desired depth level */
+ if (strcmp(db_tok_string, "d") == 0) {
+ t = db_read_token();
+ if (t != tNUMBER) {
+ db_error("Invalid depth provided\n");
+ }
+ max_depth = db_tok_number;
+ } else {
+ db_error("Invalid flag passed\n");
+ }
+ /* Fetch next token */
+ t = db_read_token();
+ }
+ /* Parse subcomannd */
+ if (t == tIDENT) {
+ if (strcmp(db_tok_string, "struct") == 0) {
+ t = db_read_token();
+
+ if (t != tIDENT) {
+ db_error("Invalid struct type name provided\n");
+ }
+ name = db_tok_string;
+
+ if (db_expression(&addr) == 0) {
+ db_error("Address not provided\n");
+ }
+ db_pprint_struct_cmd(addr, name);
+ } else {
+ name = db_tok_string;
+ db_pprint_symbol_cmd(name);
+ }
+ } else {
+ db_error("Invalid subcommand\n");
+ }
+ db_skip_to_eol();
+}
diff --git a/sys/ddb/ddb.h b/sys/ddb/ddb.h
index 1f388efcb389..bb92fef63e94 100644
--- a/sys/ddb/ddb.h
+++ b/sys/ddb/ddb.h
@@ -297,6 +297,7 @@ db_cmdfcn_t db_trace_until_matching_cmd;
db_cmdfcn_t db_unscript_cmd;
db_cmdfcn_t db_watchpoint_cmd;
db_cmdfcn_t db_write_cmd;
+db_cmdfcn_t db_pprint_cmd;
/*
* Interface between DDB and the DDB output capture facility.
diff --git a/sys/kern/kern_ctf.c b/sys/kern/kern_ctf.c
index 748622653eb3..8a2106a15308 100644
--- a/sys/kern/kern_ctf.c
+++ b/sys/kern/kern_ctf.c
@@ -27,6 +27,10 @@
*/
#include <sys/ctf.h>
+#include <sys/kdb.h>
+#include <sys/linker.h>
+
+#include <ddb/db_ctf.h>
/*
* Note this file is included by both link_elf.c and link_elf_obj.c.
@@ -86,6 +90,9 @@ link_elf_ctf_get(linker_file_t lf, linker_ctf_t *lc)
return (0);
}
+ if (panicstr != NULL || kdb_active)
+ return (ENXIO);
+
/*
* We need to try reading the CTF data. Flag no CTF data present
* by default and if we actually succeed in reading it, we'll
@@ -288,3 +295,36 @@ out:
return (error);
}
+
+static int
+link_elf_ctf_get_ddb(linker_file_t lf, linker_ctf_t *lc)
+{
+ elf_file_t ef = (elf_file_t)lf;
+
+ /*
+ * Check whether CTF data was loaded or if a
+ * previous loading attempt failed (ctfcnt == -1).
+ */
+ if (ef->ctfcnt <= 0) {
+ return (ENOENT);
+ }
+
+ lc->ctftab = ef->ctftab;
+ lc->ctfcnt = ef->ctfcnt;
+ lc->symtab = ef->ddbsymtab;
+ lc->strtab = ef->ddbstrtab;
+ lc->strcnt = ef->ddbstrcnt;
+ lc->nsym = ef->ddbsymcnt;
+
+ return (0);
+}
+
+static int
+link_elf_ctf_lookup_typename(linker_file_t lf, linker_ctf_t *lc,
+ const char *typename)
+{
+ if (link_elf_ctf_get_ddb(lf, lc))
+ return (ENOENT);
+
+ return (db_ctf_lookup_typename(lc, typename) ? 0 : ENOENT);
+}
diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c
index c6eb0273c39d..1126bf967966 100644
--- a/sys/kern/kern_linker.c
+++ b/sys/kern/kern_linker.c
@@ -328,6 +328,26 @@ linker_file_register_sysctls(linker_file_t lf, bool enable)
sx_xlock(&kld_sx);
}
+/*
+ * Invoke the LINKER_CTF_GET implementation for this file. Existing
+ * implementations will load CTF info from the filesystem upon the first call
+ * and cache it in the kernel thereafter.
+ */
+static void
+linker_ctf_load_file(linker_file_t file)
+{
+ linker_ctf_t lc;
+ int error;
+
+ error = linker_ctf_get(file, &lc);
+ if (error == 0)
+ return;
+ if (bootverbose) {
+ printf("failed to load CTF for %s: %d\n", file->filename,
+ error);
+ }
+}
+
static void
linker_file_enable_sysctls(linker_file_t lf)
{
@@ -490,6 +510,11 @@ linker_load_file(const char *filename, linker_file_t *result)
return (ENOEXEC);
}
linker_file_enable_sysctls(lf);
+
+ /*
+ * Ask the linker to load CTF data for this file.
+ */
+ linker_ctf_load_file(lf);
EVENTHANDLER_INVOKE(kld_load, lf);
*result = lf;
return (0);
@@ -782,6 +807,35 @@ linker_ctf_get(linker_file_t file, linker_ctf_t *lc)
return (LINKER_CTF_GET(file, lc));
}
+int
+linker_ctf_lookup_typename_ddb(linker_ctf_t *lc, const char *typename)
+{
+#ifdef DDB
+ linker_file_t lf;
+
+ TAILQ_FOREACH (lf, &linker_files, link){
+ if (LINKER_CTF_LOOKUP_TYPENAME(lf, lc, typename) == 0)
+ return (0);
+ }
+#endif
+ return (ENOENT);
+}
+
+int
+linker_ctf_lookup_sym_ddb(const char *symname, c_linker_sym_t *sym,
+ linker_ctf_t *lc)
+{
+#ifdef DDB
+ linker_file_t lf;
+
+ TAILQ_FOREACH (lf, &linker_files, link){
+ if (LINKER_LOOKUP_DEBUG_SYMBOL_CTF(lf, symname, sym, lc) == 0)
+ return (0);
+ }
+#endif
+ return (ENOENT);
+}
+
static int
linker_file_add_dependency(linker_file_t file, linker_file_t dep)
{
@@ -1781,9 +1835,21 @@ fail:
sx_xunlock(&kld_sx);
/* woohoo! we made it! */
}
-
SYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, NULL);
+static void
+linker_mountroot(void *arg __unused)
+{
+ linker_file_t lf;
+
+ sx_xlock(&kld_sx);
+ TAILQ_FOREACH (lf, &linker_files, link) {
+ linker_ctf_load_file(lf);
+ }
+ sx_xunlock(&kld_sx);
+}
+EVENTHANDLER_DEFINE(mountroot, linker_mountroot, NULL, 0);
+
/*
* Handle preload files that failed to load any modules.
*/
diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c
index 4c7af262addb..dddead849dc9 100644
--- a/sys/kern/link_elf.c
+++ b/sys/kern/link_elf.c
@@ -68,6 +68,10 @@
#include "linker_if.h"
+#ifdef DDB_CTF
+#include <ddb/db_ctf.h>
+#endif
+
#define MAXSEGS 4
typedef struct elf_file {
@@ -141,6 +145,9 @@ static int link_elf_lookup_symbol(linker_file_t, const char *,
c_linker_sym_t *);
static int link_elf_lookup_debug_symbol(linker_file_t, const char *,
c_linker_sym_t *);
+static int link_elf_lookup_debug_symbol_ctf(linker_file_t lf,
+ const char *name, c_linker_sym_t *sym, linker_ctf_t *lc);
+
static int link_elf_symbol_values(linker_file_t, c_linker_sym_t,
linker_symval_t *);
static int link_elf_debug_symbol_values(linker_file_t, c_linker_sym_t,
@@ -167,6 +174,7 @@ static int elf_lookup(linker_file_t, Elf_Size, int, Elf_Addr *);
static kobj_method_t link_elf_methods[] = {
KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol),
KOBJMETHOD(linker_lookup_debug_symbol, link_elf_lookup_debug_symbol),
+ KOBJMETHOD(linker_lookup_debug_symbol_ctf, link_elf_lookup_debug_symbol_ctf),
KOBJMETHOD(linker_symbol_values, link_elf_symbol_values),
KOBJMETHOD(linker_debug_symbol_values, link_elf_debug_symbol_values),
KOBJMETHOD(linker_search_symbol, link_elf_search_symbol),
@@ -178,6 +186,7 @@ static kobj_method_t link_elf_methods[] = {
KOBJMETHOD(linker_each_function_name, link_elf_each_function_name),
KOBJMETHOD(linker_each_function_nameval, link_elf_each_function_nameval),
KOBJMETHOD(linker_ctf_get, link_elf_ctf_get),
+ KOBJMETHOD(linker_ctf_lookup_typename, link_elf_ctf_lookup_typename),
KOBJMETHOD(linker_symtab_get, link_elf_symtab_get),
KOBJMETHOD(linker_strtab_get, link_elf_strtab_get),
#ifdef VIMAGE
@@ -1588,6 +1597,34 @@ link_elf_lookup_debug_symbol(linker_file_t lf, const char *name,
}
static int
+link_elf_lookup_debug_symbol_ctf(linker_file_t lf, const char *name,
+ c_linker_sym_t *sym, linker_ctf_t *lc)
+{
+ elf_file_t ef = (elf_file_t)lf;
+ const Elf_Sym *symp;
+ const char *strp;
+ int i;
+
+ for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
+ strp = ef->ddbstrtab + symp->st_name;
+ if (strcmp(name, strp) == 0) {
+ if (symp->st_shndx != SHN_UNDEF ||
+ (symp->st_value != 0 &&
+ (ELF_ST_TYPE(symp->st_info) == STT_FUNC ||
+ ELF_ST_TYPE(symp->st_info) ==
+ STT_GNU_IFUNC))) {
+ *sym = (c_linker_sym_t)symp;
+ break;
+ }
+ return (ENOENT);
+ }
+ }
+
+ /* Populate CTF info structure if symbol was found. */
+ return (i < ef->ddbsymcnt ? link_elf_ctf_get_ddb(lf, lc) : ENOENT);
+}
+
+static int
link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym,
linker_symval_t *symval, bool see_local)
{
diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c
index 1001cb53ba49..c8ccebea0832 100644
--- a/sys/kern/link_elf_obj.c
+++ b/sys/kern/link_elf_obj.c
@@ -131,6 +131,8 @@ static int link_elf_lookup_symbol(linker_file_t, const char *,
c_linker_sym_t *);
static int link_elf_lookup_debug_symbol(linker_file_t, const char *,
c_linker_sym_t *);
+static int link_elf_lookup_debug_symbol_ctf(linker_file_t lf,
+ const char *name, c_linker_sym_t *sym, linker_ctf_t *lc);
static int link_elf_symbol_values(linker_file_t, c_linker_sym_t,
linker_symval_t *);
static int link_elf_debug_symbol_values(linker_file_t, c_linker_sym_t,
@@ -159,6 +161,7 @@ static int elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps,
static kobj_method_t link_elf_methods[] = {
KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol),
KOBJMETHOD(linker_lookup_debug_symbol, link_elf_lookup_debug_symbol),
+ KOBJMETHOD(linker_lookup_debug_symbol_ctf, link_elf_lookup_debug_symbol_ctf),
KOBJMETHOD(linker_symbol_values, link_elf_symbol_values),
KOBJMETHOD(linker_debug_symbol_values, link_elf_debug_symbol_values),
KOBJMETHOD(linker_search_symbol, link_elf_search_symbol),
@@ -170,6 +173,7 @@ static kobj_method_t link_elf_methods[] = {
KOBJMETHOD(linker_each_function_name, link_elf_each_function_name),
KOBJMETHOD(linker_each_function_nameval, link_elf_each_function_nameval),
KOBJMETHOD(linker_ctf_get, link_elf_ctf_get),
+ KOBJMETHOD(linker_ctf_lookup_typename, link_elf_ctf_lookup_typename),
KOBJMETHOD(linker_symtab_get, link_elf_symtab_get),
KOBJMETHOD(linker_strtab_get, link_elf_strtab_get),
#ifdef VIMAGE
@@ -1476,6 +1480,16 @@ link_elf_lookup_debug_symbol(linker_file_t lf, const char *name,
}
static int
+link_elf_lookup_debug_symbol_ctf(linker_file_t lf, const char *name,
+ c_linker_sym_t *sym, linker_ctf_t *lc)
+{
+ if (link_elf_lookup_debug_symbol(lf, name, sym))
+ return (ENOENT);
+
+ return (link_elf_ctf_get_ddb(lf, lc));
+}
+
+static int
link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym,
linker_symval_t *symval, bool see_local)
{
diff --git a/sys/kern/linker_if.m b/sys/kern/linker_if.m
index a50ed1ea84a3..ce08b3040b9c 100644
--- a/sys/kern/linker_if.m
+++ b/sys/kern/linker_if.m
@@ -116,6 +116,29 @@ METHOD int ctf_get {
};
#
+# Look up a CTF type in the file's CTF section
+# and return CTF info in the linker CTF structure.
+# Return ENOENT if typename is not found, otherwise zero.
+#
+METHOD int ctf_lookup_typename {
+ linker_file_t file;
+ linker_ctf_t *lc;
+ const char *typename;
+};
+
+#
+# Lookup a symbol in the file's symbol table and the file's CTF info.
+# Return ENOENT if either the symbol or its CTF
+# data is not loaded, otherwise return zero.
+#
+METHOD int lookup_debug_symbol_ctf {
+ linker_file_t file;
+ const char *name;
+ c_linker_sym_t *sym;
+ linker_ctf_t *lc;
+};
+
+#
# Get the symbol table, returning it in **symtab. Return the
# number of symbols, otherwise zero.
#
diff --git a/sys/sys/linker.h b/sys/sys/linker.h
index b94cd9f27405..b6184f4fc876 100644
--- a/sys/sys/linker.h
+++ b/sys/sys/linker.h
@@ -319,6 +319,9 @@ typedef struct linker_ctf {
} linker_ctf_t;
int linker_ctf_get(linker_file_t, linker_ctf_t *);
+int linker_ctf_lookup_sym_ddb(const char *symname, c_linker_sym_t *sym,
+ linker_ctf_t *lc);
+int linker_ctf_lookup_typename_ddb(linker_ctf_t *lc, const char *typename);
int elf_cpu_load_file(linker_file_t);
int elf_cpu_unload_file(linker_file_t);