/* $Id: dbgkrnlinfo-r0drv-solaris.c $ */ /** @file * IPRT - Kernel debug information, Ring-0 Driver, Solaris Code. */ /* * Copyright (C) 2012-2023 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included * in the VirtualBox distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. * * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #include "the-solaris-kernel.h" #include "internal/iprt.h" #include #include #include #include #include #include #include #include #include "internal/magics.h" /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ /** * Solaris kernel debug info instance data. */ typedef struct RTDBGKRNLINFOINT { /** Magic value (RTDBGKRNLINFO_MAGIC). */ uint32_t volatile u32Magic; /** The number of threads referencing this object. */ uint32_t volatile cRefs; /** Pointer to the genunix CTF handle. */ ctf_file_t *pGenUnixCTF; /** Pointer to the genunix module handle. */ modctl_t *pGenUnixMod; } RTDBGKRNLINFOINT; /** Pointer to the solaris kernel debug info instance data. */ typedef struct RTDBGKRNLINFOINT *PRTDBGKRNLINFOINT; /** * Retains a kernel module and opens the CTF data associated with it. * * @param pszModule The name of the module to open. * @param ppMod Where to store the module handle. * @param ppCTF Where to store the module's CTF handle. * * @return IPRT status code. */ static int rtR0DbgKrnlInfoModRetain(char *pszModule, modctl_t **ppMod, ctf_file_t **ppCTF) { AssertPtrReturn(pszModule, VERR_INVALID_PARAMETER); AssertPtrReturn(ppMod, VERR_INVALID_PARAMETER); AssertPtrReturn(ppCTF, VERR_INVALID_PARAMETER); int rc = VINF_SUCCESS; modid_t ModId = mod_name_to_modid(pszModule); if (ModId != -1) { *ppMod = mod_hold_by_id(ModId); if (*ppMod) { /* * Hold mod_lock as ctf_modopen may update the module with uncompressed CTF data. */ int err; mutex_enter(&mod_lock); *ppCTF = ctf_modopen(((modctl_t *)*ppMod)->mod_mp, &err); mutex_exit(&mod_lock); mod_release_mod(*ppMod); if (*ppCTF) return VINF_SUCCESS; else { LogRel(("rtR0DbgKrnlInfoModRetain: ctf_modopen failed for '%s' err=%d\n", pszModule, err)); rc = VERR_INTERNAL_ERROR_3; } } else { LogRel(("rtR0DbgKrnlInfoModRetain: mod_hold_by_id failed for '%s'\n", pszModule)); rc = VERR_INTERNAL_ERROR_2; } } else { LogRel(("rtR0DbgKrnlInfoModRetain: mod_name_to_modid failed for '%s'\n", pszModule)); rc = VERR_INTERNAL_ERROR; } return rc; } /** * Releases the kernel module and closes its CTF data. * * @param pMod Pointer to the module handle. * @param pCTF Pointer to the module's CTF handle. */ static void rtR0DbgKrnlInfoModRelease(modctl_t *pMod, ctf_file_t *pCTF) { AssertPtrReturnVoid(pMod); AssertPtrReturnVoid(pCTF); ctf_close(pCTF); } /** * Helper for opening the specified kernel module. * * @param pszModule The name of the module. * @param ppMod Where to store the module handle. * @param ppCtf Where to store the module's CTF handle. * * @returns Pointer to the CTF structure for the module. */ static int rtR0DbgKrnlInfoModRetainEx(const char *pszModule, modctl_t **ppMod, ctf_file_t **ppCtf) { char *pszMod = RTStrDup(pszModule); if (RT_LIKELY(pszMod)) { int rc = rtR0DbgKrnlInfoModRetain(pszMod, ppMod, ppCtf); RTStrFree(pszMod); if (RT_SUCCESS(rc)) { AssertPtrReturn(*ppMod, VERR_INTERNAL_ERROR_2); AssertPtrReturn(*ppCtf, VERR_INTERNAL_ERROR_3); } return rc; } return VERR_NO_MEMORY; } RTR0DECL(int) RTR0DbgKrnlInfoOpen(PRTDBGKRNLINFO phKrnlInfo, uint32_t fFlags) { AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); AssertPtrReturn(phKrnlInfo, VERR_INVALID_POINTER); /* This can be called as part of IPRT init, in which case we have no thread preempt information yet. */ if (g_frtSolInitDone) RT_ASSERT_PREEMPTIBLE(); *phKrnlInfo = NIL_RTDBGKRNLINFO; PRTDBGKRNLINFOINT pThis = (PRTDBGKRNLINFOINT)RTMemAllocZ(sizeof(*pThis)); if (!pThis) return VERR_NO_MEMORY; char szGenUnixModName[] = "genunix"; int rc = rtR0DbgKrnlInfoModRetain(szGenUnixModName, &pThis->pGenUnixMod, &pThis->pGenUnixCTF); if (RT_SUCCESS(rc)) { pThis->u32Magic = RTDBGKRNLINFO_MAGIC; pThis->cRefs = 1; *phKrnlInfo = pThis; return VINF_SUCCESS; } LogRel(("RTR0DbgKrnlInfoOpen: rtR0DbgKrnlInfoModRetain failed rc=%d.\n", rc)); RTMemFree(pThis); return rc; } RTR0DECL(uint32_t) RTR0DbgKrnlInfoRetain(RTDBGKRNLINFO hKrnlInfo) { PRTDBGKRNLINFOINT pThis = hKrnlInfo; AssertPtrReturn(pThis, UINT32_MAX); AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX); uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); Assert(cRefs && cRefs < 100000); return cRefs; } RTR0DECL(uint32_t) RTR0DbgKrnlInfoRelease(RTDBGKRNLINFO hKrnlInfo) { PRTDBGKRNLINFOINT pThis = hKrnlInfo; if (pThis == NIL_RTDBGKRNLINFO) return 0; AssertPtrReturn(pThis, UINT32_MAX); AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX); if (g_frtSolInitDone) RT_ASSERT_PREEMPTIBLE(); uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); if (cRefs == 0) { pThis->u32Magic = ~RTDBGKRNLINFO_MAGIC; rtR0DbgKrnlInfoModRelease(pThis->pGenUnixMod, pThis->pGenUnixCTF); RTMemFree(pThis); } return cRefs; } RTR0DECL(int) RTR0DbgKrnlInfoQueryMember(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszStructure, const char *pszMember, size_t *poffMember) { PRTDBGKRNLINFOINT pThis = hKrnlInfo; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); AssertPtrReturn(pszMember, VERR_INVALID_PARAMETER); AssertPtrReturn(pszStructure, VERR_INVALID_PARAMETER); AssertPtrReturn(poffMember, VERR_INVALID_PARAMETER); if (g_frtSolInitDone) RT_ASSERT_PREEMPTIBLE(); ctf_file_t *pCtf = NULL; modctl_t *pMod = NULL; if (!pszModule) { pCtf = pThis->pGenUnixCTF; pMod = pThis->pGenUnixMod; } else { int rc2 = rtR0DbgKrnlInfoModRetainEx(pszModule, &pMod, &pCtf); if (RT_FAILURE(rc2)) return rc2; Assert(pMod); Assert(pCtf); } int rc = VERR_NOT_FOUND; ctf_id_t TypeIdent = ctf_lookup_by_name(pCtf, pszStructure); if (TypeIdent != CTF_ERR) { ctf_membinfo_t MemberInfo; RT_ZERO(MemberInfo); if (ctf_member_info(pCtf, TypeIdent, pszMember, &MemberInfo) != CTF_ERR) { *poffMember = (MemberInfo.ctm_offset >> 3); rc = VINF_SUCCESS; } } if (pszModule) rtR0DbgKrnlInfoModRelease(pMod, pCtf); return rc; } RTR0DECL(int) RTR0DbgKrnlInfoQuerySymbol(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszSymbol, void **ppvSymbol) { PRTDBGKRNLINFOINT pThis = hKrnlInfo; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); AssertPtrReturn(pszSymbol, VERR_INVALID_PARAMETER); AssertPtrNullReturn(ppvSymbol, VERR_INVALID_PARAMETER); AssertReturn(!pszModule, VERR_MODULE_NOT_FOUND); if (g_frtSolInitDone) RT_ASSERT_PREEMPTIBLE(); uintptr_t uValue = kobj_getsymvalue((char *)pszSymbol, 1 /* only kernel */); if (ppvSymbol) *ppvSymbol = (void *)uValue; if (uValue) return VINF_SUCCESS; return VERR_SYMBOL_NOT_FOUND; } RTR0DECL(int) RTR0DbgKrnlInfoQuerySize(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszType, size_t *pcbType) { PRTDBGKRNLINFOINT pThis = hKrnlInfo; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); AssertPtrReturn(pszType, VERR_INVALID_PARAMETER); AssertPtrReturn(pcbType, VERR_INVALID_PARAMETER); if (g_frtSolInitDone) RT_ASSERT_PREEMPTIBLE(); modctl_t *pMod = NULL; ctf_file_t *pCtf = NULL; if (!pszModule) { pCtf = pThis->pGenUnixCTF; pMod = pThis->pGenUnixMod; } else { int rc2 = rtR0DbgKrnlInfoModRetainEx(pszModule, &pMod, &pCtf); if (RT_FAILURE(rc2)) return rc2; Assert(pMod); Assert(pCtf); } int rc = VERR_NOT_FOUND; ctf_id_t TypeIdent = ctf_lookup_by_name(pCtf, pszType); if (TypeIdent != CTF_ERR) { ssize_t cbType = ctf_type_size(pCtf, TypeIdent); if (cbType > 0) { *pcbType = cbType; rc = VINF_SUCCESS; } else rc = VERR_WRONG_TYPE; } if (pszModule) rtR0DbgKrnlInfoModRelease(pMod, pCtf); return rc; }