2024-03-22 04:01:34 +01:00
|
|
|
/*-
|
2024-03-28 20:32:52 +01:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2024-03-22 04:01:34 +01:00
|
|
|
*
|
|
|
|
* 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));
|
|
|
|
}
|