VirtualBox

Changeset 83085 in vbox for trunk/src/VBox/VMM


Ignore:
Timestamp:
Feb 15, 2020 9:19:54 PM (5 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
136140
Message:

IPRT,VMM,DBGPlugInDarwin: Implemented in-memory guest kernel and kext image loading for OS X / Mach-O. Requires LINKEDIT to not be jettisoned to work.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/VMM/VMMR3/DBGFR3ModInMem.cpp

    r82968 r83085  
    2929#include <iprt/path.h>
    3030#include <iprt/string.h>
     31#include <iprt/sort.h>
    3132#include <iprt/formats/pecoff.h>
    3233#include <iprt/formats/mz.h>
    3334#include <iprt/formats/elf.h>
     35#include <iprt/formats/mach-o.h>
    3436
    3537
     
    3739*   Structures and Typedefs                                                                                                      *
    3840*********************************************************************************************************************************/
     41/** Entry for mapping file offset to memory location. */
     42typedef struct DBGFMODINMEMMAPPING
     43{
     44    /** The file offset. */
     45    uint32_t        offFile;
     46    /** The file size of this mapping. */
     47    uint32_t        cbFile;
     48    /** The size of this mapping. */
     49    uint32_t        cbMem;
     50    /** The offset to the memory from the start of the image.
     51     * @note This can be negative (for mach_kernel).  */
     52    int32_t         offMem;
     53} DBGFMODINMEMMAPPING;
     54typedef DBGFMODINMEMMAPPING *PDBGFMODINMEMMAPPING;
     55typedef DBGFMODINMEMMAPPING const *PCDBGFMODINMEMMAPPING;
     56
     57/**
     58 * Common in-memory reader instance data.
     59 */
     60typedef struct DBGFMODINMEMRDR
     61{
     62    /** The VM handle (referenced). */
     63    PUVM                pUVM;
     64    /** The image base. */
     65    DBGFADDRESS         ImageAddr;
     66    /** The file size, based on the offFile and cbFile of the last  mapping. */
     67    uint32_t            cbFile;
     68    /** Number of entries in the aMappings table. */
     69    uint32_t            cMappings;
     70    /** Mapping hint. */
     71    uint32_t            iHint;
     72    /** Mapping file offset to memory offsets, ordered by file offset. */
     73    DBGFMODINMEMMAPPING aMappings[RT_FLEXIBLE_ARRAY_NESTED];
     74} DBGFMODINMEMRDR;
     75/** Pointer to the common instance data for an in-memory file reader. */
     76typedef DBGFMODINMEMRDR *PDBGFMODINMEMRDR;
     77
    3978/**
    4079 * The WinNT digger's loader reader instance data.
     
    80119    IMAGE_NT_HEADERS32  Nt32;
    81120    IMAGE_NT_HEADERS64  Nt64;
     121    mach_header_64      MachoHdr;
     122    DBGFMODINMEMMAPPING aMappings[0x2000 / sizeof(DBGFMODINMEMMAPPING)];
    82123} DBGFMODINMEMBUF;
    83124/** Pointer to stack buffer. */
     
    130171
    131172    return pszBuf;
     173}
     174
     175
     176/**
     177 * @callback_method_impl{PFNRTLDRRDRMEMREAD}
     178 */
     179static DECLCALLBACK(int) dbgfModInMemCommon_Read(void *pvBuf, size_t cb, size_t off, void *pvUser)
     180{
     181    PDBGFMODINMEMRDR pThis   = (PDBGFMODINMEMRDR)pvUser;
     182    uint32_t         offFile = (uint32_t)off;
     183    AssertReturn(offFile == off, VERR_INVALID_PARAMETER);
     184
     185    /*
     186     * Set i to a mapping that starts at or before the specified offset.
     187     * ASSUMING aMappings are sorted by offFile.
     188     */
     189    uint32_t i = pThis->iHint;
     190    if (pThis->aMappings[i].offFile > offFile)
     191    {
     192        i = pThis->cMappings; /** @todo doesn't need to start from the end here... */
     193        while (i-- > 0)
     194            if (offFile >= pThis->aMappings[i].offFile)
     195                break;
     196        pThis->iHint = i;
     197    }
     198
     199    while (cb > 0)
     200    {
     201        uint32_t offNextMap =  i + 1 < pThis->cMappings ? pThis->aMappings[i + 1].offFile
     202                            : pThis->aMappings[i].offFile + RT_MAX(pThis->aMappings[i].cbFile, pThis->aMappings[i].cbMem);
     203        uint32_t offMap     = offFile - pThis->aMappings[i].offFile;
     204
     205        /* Read file bits backed by memory. */
     206        if (offMap < pThis->aMappings[i].cbMem)
     207        {
     208            uint32_t cbToRead = pThis->aMappings[i].cbMem - offMap;
     209            if (cbToRead > cb)
     210                cbToRead = (uint32_t)cb;
     211
     212            DBGFADDRESS Addr = pThis->ImageAddr;
     213            DBGFR3AddrAdd(&Addr, pThis->aMappings[i].offMem + offMap);
     214
     215            int rc = DBGFR3MemRead(pThis->pUVM, 0 /*idCpu*/, &Addr, pvBuf, cbToRead);
     216            if (RT_FAILURE(rc))
     217                return rc;
     218
     219            /* Done? */
     220            if (cbToRead == cb)
     221                break;
     222
     223            offFile += cbToRead;
     224            cb      -= cbToRead;
     225            pvBuf    = (char *)pvBuf + cbToRead;
     226        }
     227
     228        /* Mind the gap. */
     229        if (offNextMap > offFile)
     230        {
     231            uint32_t cbZero = offNextMap - offFile;
     232            if (cbZero > cb)
     233            {
     234                RT_BZERO(pvBuf, cb);
     235                break;
     236            }
     237
     238            RT_BZERO(pvBuf, cbZero);
     239            offFile += cbZero;
     240            cb      -= cbZero;
     241            pvBuf   = (char *)pvBuf + cbZero;
     242        }
     243
     244        pThis->iHint = ++i;
     245    }
     246
     247    return VINF_SUCCESS;
     248}
     249
     250
     251/**
     252 * @callback_method_impl{PFNRTLDRRDRMEMDTOR}
     253 */
     254static DECLCALLBACK(void) dbgfModInMemCommon_Dtor(void *pvUser, size_t cbImage)
     255{
     256    PDBGFMODINMEMRDR pThis = (PDBGFMODINMEMRDR)pvUser;
     257    RT_NOREF(cbImage);
     258
     259    VMR3ReleaseUVM(pThis->pUVM);
     260    pThis->pUVM = NULL;
     261
     262    RTMemFree(pThis);
     263}
     264
     265
     266/**
     267 * @callback_method_impl{FNRTSORTCMP}
     268 */
     269static DECLCALLBACK(int) dbgfModInMemCompMappings(void const *pvElement1, void const *pvElement2, void *pvUser)
     270{
     271    RT_NOREF(pvUser);
     272    PCDBGFMODINMEMMAPPING pElement1 = (PCDBGFMODINMEMMAPPING)pvElement1;
     273    PCDBGFMODINMEMMAPPING pElement2 = (PCDBGFMODINMEMMAPPING)pvElement2;
     274    if (pElement1->offFile < pElement2->offFile)
     275        return -1;
     276    if (pElement1->offFile > pElement2->offFile)
     277        return 1;
     278    if (pElement1->cbFile < pElement2->cbFile)
     279        return -1;
     280    if (pElement1->cbFile > pElement2->cbFile)
     281        return 1;
     282    if (pElement1->offMem < pElement2->offMem)
     283        return -1;
     284    if (pElement1->offMem > pElement2->offMem)
     285        return 1;
     286    if (pElement1->cbMem < pElement2->cbMem)
     287        return -1;
     288    if (pElement1->cbMem > pElement2->cbMem)
     289        return 1;
     290    return 0;
     291}
     292
     293
     294static int dbgfModInMemCommon_Init(PDBGFMODINMEMRDR pThis, PUVM pUVM, PCDBGFADDRESS pImageAddr,PCDBGFMODINMEMMAPPING paMappings,
     295                                   uint32_t cMappings, const char *pszName, RTLDRARCH enmArch,
     296                                   PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
     297{
     298    /*
     299     * Initialize the reader instance.
     300     */
     301    VMR3RetainUVM(pUVM);
     302    pThis->pUVM      = pUVM;
     303    pThis->ImageAddr = *pImageAddr;
     304    pThis->cMappings = cMappings;
     305    pThis->iHint     = 0;
     306    memcpy(pThis->aMappings, paMappings, cMappings * sizeof(pThis->aMappings[0]));
     307    RTSortShell(pThis->aMappings, cMappings, sizeof(pThis->aMappings[0]), dbgfModInMemCompMappings, NULL);
     308    pThis->cbFile    = pThis->aMappings[cMappings - 1].offFile + pThis->aMappings[cMappings - 1].cbFile;
     309
     310    /*
     311     * Call the loader to open it.
     312     * Note! destructore is always called.
     313     */
     314
     315    RTLDRMOD hLdrMod;
     316    int rc = RTLdrOpenInMemory(pszName, RTLDR_O_FOR_DEBUG, enmArch, pThis->cbFile,
     317                               dbgfModInMemCommon_Read, dbgfModInMemCommon_Dtor, pThis,
     318                               &hLdrMod, pErrInfo);
     319    if (RT_SUCCESS(rc))
     320        *phLdrMod = hLdrMod;
     321    else
     322        *phLdrMod = NIL_RTLDRMOD;
     323    return rc;
    132324}
    133325
     
    155347    RT_NOREF(pUVM, fFlags, pszName, pszFilename, enmArch, cbImage, puBuf, phDbgMod);
    156348    return RTERRINFO_LOG_SET_F(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "Found ELF magic at %RGv", pImageAddr->FlatPtr);
     349}
     350
     351
     352/**
     353 * Handles in-memory Mach-O images.
     354 *
     355 * @returns VBox status code.
     356 * @param   pUVM            The user mode VM handle.
     357 * @param   pImageAddr      The image address.
     358 * @param   fFlags          Flags, DBGFMODINMEM_F_XXX.
     359 * @param   pszName         The module name, optional.
     360 * @param   pszFilename     The image filename, optional.
     361 * @param   enmArch         The image arch if we force it, pass
     362 *                          RTLDRARCH_WHATEVER if you don't care.
     363 * @param   cbImage         Image size.  Pass 0 if not known.
     364 * @param   puBuf           The header buffer.
     365 * @param   phDbgMod        Where to return the resulting debug module on success.
     366 * @param   pErrInfo        Where to return extended error info on failure.
     367 */
     368static int dbgfR3ModInMemMachO(PUVM pUVM, PCDBGFADDRESS pImageAddr, uint32_t fFlags, const char *pszName, const char *pszFilename,
     369                               RTLDRARCH enmArch, uint32_t cbImage, PDBGFMODINMEMBUF puBuf,
     370                               PRTDBGMOD phDbgMod, PRTERRINFO pErrInfo)
     371{
     372    RT_NOREF(cbImage, fFlags);
     373
     374    /*
     375     * Match up enmArch.
     376     */
     377    if (enmArch == RTLDRARCH_AMD64)
     378    {
     379        if (puBuf->MachoHdr.magic != IMAGE_MACHO64_SIGNATURE)
     380            return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Wanted AMD64 but header is not 64-bit");
     381        if (puBuf->MachoHdr.cputype != CPU_TYPE_X86_64)
     382            return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Wanted AMD64 but cpu type is %#x instead of %#x",
     383                                       puBuf->MachoHdr.cputype, CPU_TYPE_X86_64);
     384    }
     385    else if (enmArch == RTLDRARCH_X86_32)
     386    {
     387        if (puBuf->MachoHdr.magic != IMAGE_MACHO32_SIGNATURE)
     388            return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Wanted X86_32 but header is not 32-bit");
     389        if (puBuf->MachoHdr.cputype != CPU_TYPE_X86)
     390            return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Wanted X86_32 but cpu type is %#x instead of %#x",
     391                                       puBuf->MachoHdr.cputype, CPU_TYPE_X86);
     392    }
     393    else if (enmArch != RTLDRARCH_WHATEVER)
     394        return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Unsupported enmArch value %s (%d)",
     395                                   RTLdrArchName(enmArch), enmArch);
     396
     397    /*
     398     * Guess the module name if not specified and make sure it conforms to DBGC expectations.
     399     */
     400    char szNormalized[128];
     401    if (!pszName)
     402    {
     403        if (pszFilename)
     404            pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS /*whatever*/);
     405        if (!pszName)
     406        {
     407            RTStrPrintf(szNormalized, sizeof(szNormalized), "image_%#llx", (uint64_t)pImageAddr->FlatPtr);
     408            pszName = szNormalized;
     409        }
     410    }
     411    if (pszName != szNormalized)
     412        pszName = dbgfR3ModNormalizeName(pszName, szNormalized, sizeof(szNormalized));
     413
     414    /*
     415     * Read the load commands into memory, they follow the header.  Refuse
     416     * if there appear to be too many or too much of these.
     417     */
     418    uint32_t const cLoadCmds  = puBuf->MachoHdr.ncmds;
     419    uint32_t const cbLoadCmds = puBuf->MachoHdr.sizeofcmds;
     420    if (cLoadCmds > _8K || cLoadCmds < 2)
     421        return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRMACHO_BAD_HEADER,
     422                                   "ncmds=%u is out of sensible range (2..8192)", cLoadCmds);
     423    if (cbLoadCmds > _2M || cbLoadCmds < sizeof(load_command_t) * 2)
     424        return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRMACHO_BAD_HEADER,
     425                                   "cbLoadCmds=%#x is out of sensible range (8..2MiB)", cbLoadCmds);
     426
     427    uint8_t *pbLoadCmds = (uint8_t *)RTMemTmpAllocZ(cbLoadCmds);
     428    AssertReturn(pbLoadCmds, VERR_NO_TMP_MEMORY);
     429
     430    uint32_t const cbHdr = puBuf->MachoHdr.magic == IMAGE_MACHO64_SIGNATURE ? sizeof(mach_header_64) : sizeof(mach_header_32);
     431    DBGFADDRESS Addr = *pImageAddr;
     432    int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrAdd(&Addr, cbHdr), pbLoadCmds, cbLoadCmds);
     433    if (RT_SUCCESS(rc))
     434    {
     435        /*
     436         * Scan it for segments so we can tranlate file offsets to virtual
     437         * memory locations.
     438         */
     439        RTUUID   Uuid = RTUUID_INITIALIZE_NULL;
     440        uint32_t cMappings = 0;
     441        uint32_t offCmd = 0;
     442        for (uint32_t iCmd = 0; iCmd < cLoadCmds; iCmd++)
     443        {
     444            load_command_t const *pCurCmd = (load_command_t const *)&pbLoadCmds[offCmd];
     445            uint32_t       const  cbCurCmd = offCmd + sizeof(*pCurCmd) <= cbLoadCmds ? pCurCmd->cmdsize : sizeof(*pCurCmd);
     446            if (offCmd + cbCurCmd > cbLoadCmds)
     447                rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
     448                                         "Load command #%u @ %#x is out of bounds: size %#x, left %#x", iCmd, offCmd, cbCurCmd,
     449                                         cbLoadCmds - offCmd);
     450            else if (pCurCmd->cmd == LC_SEGMENT_64)
     451            {
     452                segment_command_64 const *pSeg = (segment_command_64 const *)pCurCmd;
     453                if (cbCurCmd >= sizeof(*pSeg))
     454                {
     455                    if (cMappings >= RT_ELEMENTS(puBuf->aMappings))
     456                        rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE, "Too many segments!");
     457                    else
     458                    {
     459                        puBuf->aMappings[cMappings].offFile = pSeg->fileoff;
     460                        puBuf->aMappings[cMappings].cbFile  = pSeg->filesize;
     461                        puBuf->aMappings[cMappings].offMem  = pSeg->vmaddr - pImageAddr->FlatPtr;
     462                        puBuf->aMappings[cMappings].cbMem   = pSeg->vmsize;
     463                        cMappings++;
     464                    }
     465                }
     466                else
     467                    rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
     468                                             "Load command #%u @ %#x is too small for a 64-bit segment: %#x", iCmd, offCmd, cbCurCmd);
     469            }
     470            else if (pCurCmd->cmd == LC_SEGMENT_32)
     471            {
     472                segment_command_32 const *pSeg = (segment_command_32 const *)pCurCmd;
     473                if (cbCurCmd >= sizeof(*pSeg))
     474                {
     475                    if (cMappings >= RT_ELEMENTS(puBuf->aMappings))
     476                        rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE, "Too many segments!");
     477                    else
     478                    {
     479                        puBuf->aMappings[cMappings].offFile = pSeg->fileoff;
     480                        puBuf->aMappings[cMappings].cbFile  = pSeg->filesize;
     481                        puBuf->aMappings[cMappings].offMem  = pSeg->vmaddr - pImageAddr->FlatPtr;
     482                        puBuf->aMappings[cMappings].cbMem   = pSeg->vmsize;
     483                        cMappings++;
     484                    }
     485                }
     486                else
     487                    rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
     488                                             "Load command #%u @ %#x is too small for a 32-bit segment: %#x", iCmd, offCmd, cbCurCmd);
     489            }
     490            else if (pCurCmd->cmd == LC_UUID && cbCurCmd == sizeof(uuid_command_t))
     491                memcpy(&Uuid, ((uuid_command_t const *)pCurCmd)->uuid, sizeof(Uuid));
     492
     493            if (RT_SUCCESS(rc))
     494                offCmd += cbCurCmd;
     495            else
     496                break;
     497        } /* for each command */
     498
     499        if (RT_SUCCESS(rc))
     500        {
     501            /*
     502             * Create generic loader module instance (pThis is tied to it
     503             * come rain come shine).
     504             */
     505            PDBGFMODINMEMRDR pThis = (PDBGFMODINMEMRDR)RTMemAllocZVar(RT_UOFFSETOF_DYN(DBGFMODINMEMRDR, aMappings[cMappings]));
     506            if (pThis)
     507            {
     508                RTLDRMOD hLdrMod;
     509                rc = dbgfModInMemCommon_Init(pThis, pUVM, pImageAddr, puBuf->aMappings, cMappings,
     510                                             pszName, enmArch, &hLdrMod, pErrInfo);
     511                if (RT_FAILURE(rc))
     512                    hLdrMod = NIL_RTLDRMOD;
     513
     514                RTDBGMOD hMod;
     515                rc = RTDbgModCreateFromMachOImage(&hMod, pszFilename ? pszFilename : pszName, pszName, enmArch,
     516                                                  &hLdrMod, 0 /*cbImage*/, 0, NULL, &Uuid, DBGFR3AsGetConfig(pUVM), fFlags);
     517                if (RT_SUCCESS(rc))
     518                    *phDbgMod = hMod;
     519#if 0 /** @todo later */
     520                else if (!(fFlags & DBGFMODINMEM_F_NO_CONTAINER_FALLBACK))
     521                {
     522                    /*
     523                     * Fallback is a container module.
     524                     */
     525                    rc = RTDbgModCreate(&hMod, pszName, cbImage, 0);
     526                    if (RT_SUCCESS(rc))
     527                    {
     528                        rc = RTDbgModSymbolAdd(hMod, "Headers", 0 /*iSeg*/, 0, cbImage, 0 /*fFlags*/, NULL);
     529                        AssertRC(rc);
     530                    }
     531                }
     532#endif
     533                if (hLdrMod != NIL_RTLDRMOD)
     534                    RTLdrClose(hLdrMod);
     535            }
     536            else
     537                rc = VERR_NO_MEMORY;
     538        }
     539    }
     540    else
     541        RTERRINFO_LOG_SET_F(pErrInfo, rc, "Failed to read %#x bytes of load commands", cbLoadCmds);
     542    RTMemTmpFree(pbLoadCmds);
     543    return rc;
    157544}
    158545
     
    6711058        return dbgfR3ModInMemElf(pUVM, pImageAddr, fFlags, pszName, pszFilename, enmArch, cbImage, &uBuf, phDbgMod, pErrInfo);
    6721059
     1060    if (   uBuf.MachoHdr.magic == IMAGE_MACHO64_SIGNATURE
     1061        || uBuf.MachoHdr.magic == IMAGE_MACHO32_SIGNATURE)
     1062        return dbgfR3ModInMemMachO(pUVM, pImageAddr, fFlags, pszName, pszFilename, enmArch, cbImage, &uBuf, phDbgMod, pErrInfo);
     1063
    6731064    uint32_t offNewHdrs;
    6741065    if (uBuf.DosHdr.e_magic == IMAGE_DOS_SIGNATURE)
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette