DBGPlugInLinux.cpp revision 98427c0ab08697e468c26dc33ee9571308577867
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync/* $Id$ */
ee29f83d0638dba8e0380d7f594fe2d726d9403cvboxsync/** @file
0569fe99ac6ea5da1bf4775fe3523165ac39c030vboxsync * DBGPlugInLinux - Debugger and Guest OS Digger Plugin For Linux.
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync */
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync/*
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync * Copyright (C) 2008-2010 Oracle Corporation
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync *
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync * available from http://www.virtualbox.org. This file is free software;
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync * you can redistribute it and/or modify it under the terms of the GNU
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync * General Public License (GPL) as published by the Free Software
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync */
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync/*******************************************************************************
0569fe99ac6ea5da1bf4775fe3523165ac39c030vboxsync* Header Files *
0569fe99ac6ea5da1bf4775fe3523165ac39c030vboxsync*******************************************************************************/
3b58b08293698f7f081b5558c52e80741a4a6763vboxsync#define LOG_GROUP LOG_GROUP_DBGF ///@todo add new log group.
0569fe99ac6ea5da1bf4775fe3523165ac39c030vboxsync#include "DBGPlugIns.h"
3b58b08293698f7f081b5558c52e80741a4a6763vboxsync#include "DBGPlugInCommonELF.h"
3b58b08293698f7f081b5558c52e80741a4a6763vboxsync#include <VBox/vmm/dbgf.h>
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync#include <iprt/string.h>
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync#include <iprt/mem.h>
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync#include <iprt/stream.h>
3b58b08293698f7f081b5558c52e80741a4a6763vboxsync#include <iprt/ctype.h>
3b58b08293698f7f081b5558c52e80741a4a6763vboxsync
3b58b08293698f7f081b5558c52e80741a4a6763vboxsync
3b58b08293698f7f081b5558c52e80741a4a6763vboxsync/*******************************************************************************
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync* Structures and Typedefs *
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync*******************************************************************************/
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync/** @name InternalLinux structures
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsync * @{ */
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsync
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync/** @} */
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsync
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsync
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsync/**
6e793fadebda53870ee54ac9dcdbe30d612f22b5vboxsync * Linux guest OS digger instance data.
6e793fadebda53870ee54ac9dcdbe30d612f22b5vboxsync */
6e793fadebda53870ee54ac9dcdbe30d612f22b5vboxsynctypedef struct DBGDIGGERLINUX
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync{
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync /** Whether the information is valid or not.
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync * (For fending off illegal interface method calls.) */
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync bool fValid;
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync /** The address of the linux banner.
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync * This is set during probing. */
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync DBGFADDRESS AddrLinuxBanner;
1bfc7215a51c113dafd83953d96ab4897d2d3690vboxsync /** Kernel base address.
3b58b08293698f7f081b5558c52e80741a4a6763vboxsync * This is set during probing. */
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync DBGFADDRESS AddrKernelBase;
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync} DBGDIGGERLINUX;
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync/** Pointer to the linux guest OS digger instance data. */
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsynctypedef DBGDIGGERLINUX *PDBGDIGGERLINUX;
f09a9ba0e70c09ac2c9728909fbf45cb7f81195dvboxsync
f09a9ba0e70c09ac2c9728909fbf45cb7f81195dvboxsync
f09a9ba0e70c09ac2c9728909fbf45cb7f81195dvboxsync/*******************************************************************************
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync* Defined Constants And Macros *
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync*******************************************************************************/
cfd41a3683178a30bac4417128b4673806653797vboxsync/** Validates a 32-bit linux kernel address */
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync#define LNX32_VALID_ADDRESS(Addr) ((Addr) > UINT32_C(0x80000000) && (Addr) < UINT32_C(0xfffff000))
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync/** The max kernel size. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync#define LNX_MAX_KERNEL_SIZE 0x0f000000
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync/*******************************************************************************
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync* Internal Functions *
da6bcae46a663366ea0e6dc42ac221f327efd01fvboxsync*******************************************************************************/
771761cda2c81e899526a0dce22c8cd2510fff82vboxsyncstatic DECLCALLBACK(int) dbgDiggerLinuxInit(PUVM pUVM, void *pvData);
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync
da6bcae46a663366ea0e6dc42ac221f327efd01fvboxsync
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync/*******************************************************************************
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync* Global Variables *
da6bcae46a663366ea0e6dc42ac221f327efd01fvboxsync*******************************************************************************/
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync/** Table of common linux kernel addresses. */
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsyncstatic uint64_t g_au64LnxKernelAddresses[] =
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync{
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync UINT64_C(0xc0100000),
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync UINT64_C(0x90100000),
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync UINT64_C(0xffffffff80200000)
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync};
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync/**
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync * @copydoc DBGFOSREG::pfnQueryInterface
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync */
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsyncstatic DECLCALLBACK(void *) dbgDiggerLinuxQueryInterface(PUVM pUVM, void *pvData, DBGFOSINTERFACE enmIf)
f4ccb18a71e0e531719734918583f84fbc72ebfevboxsync{
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync return NULL;
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync}
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsync/**
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsync * @copydoc DBGFOSREG::pfnQueryVersion
3b58b08293698f7f081b5558c52e80741a4a6763vboxsync */
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsyncstatic DECLCALLBACK(int) dbgDiggerLinuxQueryVersion(PUVM pUVM, void *pvData, char *pszVersion, size_t cchVersion)
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsync{
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsync PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsync Assert(pThis->fValid);
1bfc7215a51c113dafd83953d96ab4897d2d3690vboxsync
3b58b08293698f7f081b5558c52e80741a4a6763vboxsync /*
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsync * It's all in the linux banner.
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync */
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsync int rc = DBGFR3MemReadString(pUVM, 0, &pThis->AddrLinuxBanner, pszVersion, cchVersion);
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync if (RT_SUCCESS(rc))
{
char *pszEnd = RTStrEnd(pszVersion, cchVersion);
AssertReturn(pszEnd, VERR_BUFFER_OVERFLOW);
while ( pszEnd > pszVersion
&& RT_C_IS_SPACE(pszEnd[-1]))
pszEnd--;
*pszEnd = '\0';
}
else
RTStrPrintf(pszVersion, cchVersion, "DBGFR3MemRead -> %Rrc", rc);
return rc;
}
/**
* @copydoc DBGFOSREG::pfnTerm
*/
static DECLCALLBACK(void) dbgDiggerLinuxTerm(PUVM pUVM, void *pvData)
{
PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
Assert(pThis->fValid);
pThis->fValid = false;
}
/**
* @copydoc DBGFOSREG::pfnRefresh
*/
static DECLCALLBACK(int) dbgDiggerLinuxRefresh(PUVM pUVM, void *pvData)
{
PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
NOREF(pThis);
Assert(pThis->fValid);
/*
* For now we'll flush and reload everything.
*/
dbgDiggerLinuxTerm(pUVM, pvData);
return dbgDiggerLinuxInit(pUVM, pvData);
}
/**
* @copydoc DBGFOSREG::pfnInit
*/
static DECLCALLBACK(int) dbgDiggerLinuxInit(PUVM pUVM, void *pvData)
{
PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
Assert(!pThis->fValid);
#if 0 /* later */
int rc;
/*
* Algorithm to find the ksymtab:
* 1. Find a known export string in kstrtab ("init_task", "enable_hlt" or something).
* 2. Search for the address its at, this should give you the corresponding ksymtab entry.
* 3. Search backwards assuming that kstrtab is corresponding to ksymtab.
*/
DBGFADDRESS AddrKernelKsymTab;
#endif
pThis->fValid = true;
return VINF_SUCCESS;
}
/**
* @copydoc DBGFOSREG::pfnProbe
*/
static DECLCALLBACK(bool) dbgDiggerLinuxProbe(PUVM pUVM, void *pvData)
{
PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
/*
* Look for "Linux version " at the start of the rodata segment.
* Hope that this comes before any message buffer or other similar string.
* .
* Note! Only Linux version 2.x.y, where x in {0..6}. .
*/
for (unsigned i = 0; i < RT_ELEMENTS(g_au64LnxKernelAddresses); i++)
{
DBGFADDRESS KernelAddr;
DBGFR3AddrFromFlat(pUVM, &KernelAddr, g_au64LnxKernelAddresses[i]);
DBGFADDRESS HitAddr;
static const uint8_t s_abLinuxVersion[] = "Linux version 2.";
int rc = DBGFR3MemScan(pUVM, 0, &KernelAddr, LNX_MAX_KERNEL_SIZE, 1,
s_abLinuxVersion, sizeof(s_abLinuxVersion) - 1, &HitAddr);
if (RT_SUCCESS(rc))
{
char szTmp[128];
char const *pszY = &szTmp[sizeof(s_abLinuxVersion) - 1];
rc = DBGFR3MemReadString(pUVM, 0, &HitAddr, szTmp, sizeof(szTmp));
if ( RT_SUCCESS(rc)
&& *pszY >= '0'
&& *pszY <= '6')
{
pThis->AddrKernelBase = KernelAddr;
pThis->AddrLinuxBanner = HitAddr;
return true;
}
}
}
return false;
}
/**
* @copydoc DBGFOSREG::pfnDestruct
*/
static DECLCALLBACK(void) dbgDiggerLinuxDestruct(PUVM pUVM, void *pvData)
{
}
/**
* @copydoc DBGFOSREG::pfnConstruct
*/
static DECLCALLBACK(int) dbgDiggerLinuxConstruct(PUVM pUVM, void *pvData)
{
return VINF_SUCCESS;
}
const DBGFOSREG g_DBGDiggerLinux =
{
/* .u32Magic = */ DBGFOSREG_MAGIC,
/* .fFlags = */ 0,
/* .cbData = */ sizeof(DBGDIGGERLINUX),
/* .szName = */ "Linux",
/* .pfnConstruct = */ dbgDiggerLinuxConstruct,
/* .pfnDestruct = */ dbgDiggerLinuxDestruct,
/* .pfnProbe = */ dbgDiggerLinuxProbe,
/* .pfnInit = */ dbgDiggerLinuxInit,
/* .pfnRefresh = */ dbgDiggerLinuxRefresh,
/* .pfnTerm = */ dbgDiggerLinuxTerm,
/* .pfnQueryVersion = */ dbgDiggerLinuxQueryVersion,
/* .pfnQueryInterface = */ dbgDiggerLinuxQueryInterface,
/* .u32EndMagic = */ DBGFOSREG_MAGIC
};