/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2023 Bojan Novković * * 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 #include #include #include #include #include #include #include #include #include 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)); }