/* $Id$ */
/** @file
* IPRT - Binary Image Loader, The Memory/Debugger Oriented Parts.
*/
/*
* Copyright (C) 2006-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP RTLOGGROUP_LDR
#include <iprt/ldr.h>
#include "internal/iprt.h"
#include <iprt/alloc.h>
#include <iprt/assert.h>
#include <iprt/log.h>
#include <iprt/err.h>
#include <iprt/string.h>
#include "internal/ldr.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Memory reader (for debuggers) instance.
*/
typedef struct RTLDRRDRMEM
{
/** The core. */
RTLDRREADER Core;
/** The size of the image. */
size_t cbImage;
/** The current offset. */
size_t offCur;
/** User parameter for the reader and destructor functions.*/
void *pvUser;
/** Read function. */
PFNRTLDRRDRMEMREAD pfnRead;
/** Destructor callback. */
PFNRTLDRRDRMEMDTOR pfnDtor;
/** Mapping of the file. */
void *pvMapping;
/** Mapping usage counter. */
uint32_t cMappings;
/** The fake filename (variable size). */
char szName[1];
} RTLDRRDRMEM;
/** Memory based loader reader instance data. */
typedef RTLDRRDRMEM *PRTLDRRDRMEM;
/** @callback_method_impl{FNRTLDRRDRMEMDTOR,
* Default destructor - pvUser points to the image memory block.}
*/
static DECLCALLBACK(void) rtldrRdrMemDefaultDtor(void *pvUser)
{
RTMemFree(pvUser);
}
/** @callback_method_impl{FNRTLDRRDRMEMREAD,
* Default memory reader - pvUser points to the image memory block.}
*/
static DECLCALLBACK(int) rtldrRdrMemDefaultReader(void *pvBuf, size_t cb, size_t off, void *pvUser)
{
memcpy(pvBuf, (uint8_t *)pvUser + off, cb);
return VINF_SUCCESS;
}
/** @copydoc RTLDRREADER::pfnRead */
static DECLCALLBACK(int) rtldrRdrMem_Read(PRTLDRREADER pReader, void *pvBuf, size_t cb, RTFOFF off)
{
PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
AssertReturn(off >= 0, VERR_INVALID_PARAMETER);
if ( cb > pThis->cbImage
|| off > (RTFOFF)pThis->cbImage
|| off + (RTFOFF)cb > (RTFOFF)pThis->cbImage)
{
pThis->offCur = pThis->cbImage;
return VERR_EOF;
}
int rc = pThis->pfnRead(pvBuf, cb, (size_t)off, pThis->pvUser);
if (RT_SUCCESS(rc))
pThis->offCur = (size_t)off + cb;
else
pThis->offCur = ~(size_t)0;
return rc;
}
/** @copydoc RTLDRREADER::pfnTell */
static DECLCALLBACK(RTFOFF) rtldrRdrMem_Tell(PRTLDRREADER pReader)
{
PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
return pThis->offCur;
}
/** @copydoc RTLDRREADER::pfnSize */
static DECLCALLBACK(RTFOFF) rtldrRdrMem_Size(PRTLDRREADER pReader)
{
PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
return pThis->cbImage;
}
/** @copydoc RTLDRREADER::pfnLogName */
static DECLCALLBACK(const char *) rtldrRdrMem_LogName(PRTLDRREADER pReader)
{
PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
return pThis->szName;
}
/** @copydoc RTLDRREADER::pfnMap */
static DECLCALLBACK(int) rtldrRdrMem_Map(PRTLDRREADER pReader, const void **ppvBits)
{
PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
/*
* Already mapped?
*/
if (pThis->pvMapping)
{
pThis->cMappings++;
*ppvBits = pThis->pvMapping;
return VINF_SUCCESS;
}
/*
* Allocate memory.
*/
pThis->pvMapping = RTMemAlloc(pThis->cbImage);
if (!pThis->pvMapping)
return VERR_NO_MEMORY;
int rc = rtldrRdrMem_Read(pReader, pThis->pvMapping, pThis->cbImage, 0);
if (RT_SUCCESS(rc))
{
pThis->cMappings = 1;
*ppvBits = pThis->pvMapping;
}
else
{
RTMemFree(pThis->pvMapping);
pThis->pvMapping = NULL;
}
return rc;
}
/** @copydoc RTLDRREADER::pfnUnmap */
static DECLCALLBACK(int) rtldrRdrMem_Unmap(PRTLDRREADER pReader, const void *pvBits)
{
PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
AssertReturn(pThis->cMappings > 0, VERR_INVALID_PARAMETER);
if (!--pThis->cMappings)
{
RTMemFree(pThis->pvMapping);
pThis->pvMapping = NULL;
}
NOREF(pvBits);
return VINF_SUCCESS;
}
/** @copydoc RTLDRREADER::pfnDestroy */
static DECLCALLBACK(int) rtldrRdrMem_Destroy(PRTLDRREADER pReader)
{
PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
pThis->pfnDtor(pThis->pvUser);
RTMemFree(pThis);
return VINF_SUCCESS;
}
/**
* Opens a memory based loader reader.
*
* @returns iprt status code.
* @param ppReader Where to store the reader instance on success.
* @param pszName The name to give the image.
* @param cbImage The image size.
* @param pfnRead The reader function. If NULL, a default reader is
* used that assumes pvUser points to a memory buffer
* of at least @a cbImage size.
* @param pfnDtor The destructor. If NULL, a default destructore is
* used that will call RTMemFree on @a pvUser.
* @param pvUser User argument. If either @a pfnRead or @a pfnDtor
* is NULL, this must be a pointer to readable memory
* (see above).
*/
static int rtldrRdrMem_Create(PRTLDRREADER *ppReader, const char *pszName, size_t cbImage,
PFNRTLDRRDRMEMREAD pfnRead, PFNRTLDRRDRMEMDTOR pfnDtor, void *pvUser)
{
#if ARCH_BITS > 32 /* 'ing gcc. */
AssertReturn(cbImage < RTFOFF_MAX, VERR_INVALID_PARAMETER);
#endif
AssertReturn((RTFOFF)cbImage > 0, VERR_INVALID_PARAMETER);
size_t cchName = strlen(pszName);
int rc = VERR_NO_MEMORY;
PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)RTMemAlloc(sizeof(*pThis) + cchName);
if (pThis)
{
memcpy(pThis->szName, pszName, cchName + 1);
pThis->cbImage = cbImage;
pThis->pvUser = pvUser;
pThis->offCur = 0;
pThis->pvUser = pvUser;
pThis->pfnRead = pfnRead ? pfnRead : rtldrRdrMemDefaultReader;
pThis->pfnDtor = pfnDtor ? pfnDtor : rtldrRdrMemDefaultDtor;
pThis->pvMapping = NULL;
pThis->cMappings = 0;
pThis->Core.uMagic = RTLDRREADER_MAGIC;
pThis->Core.pfnRead = rtldrRdrMem_Read;
pThis->Core.pfnTell = rtldrRdrMem_Tell;
pThis->Core.pfnSize = rtldrRdrMem_Size;
pThis->Core.pfnLogName = rtldrRdrMem_LogName;
pThis->Core.pfnMap = rtldrRdrMem_Map;
pThis->Core.pfnUnmap = rtldrRdrMem_Unmap;
pThis->Core.pfnDestroy = rtldrRdrMem_Destroy;
*ppReader = &pThis->Core;
return VINF_SUCCESS;
}
*ppReader = NULL;
return rc;
}
RTDECL(int) RTLdrOpenInMemory(const char *pszName, uint32_t fFlags, RTLDRARCH enmArch, size_t cbImage,
PFNRTLDRRDRMEMREAD pfnRead, PFNRTLDRRDRMEMDTOR pfnDtor, void *pvUser,
PRTLDRMOD phLdrMod)
{
LogFlow(("RTLdrOpenInMemory: pszName=%p:{%s} fFlags=%#x enmArch=%d cbImage=%#zx pfnRead=%p pfnDtor=%p pvUser=%p phLdrMod=%p\n",
pszName, pszName, fFlags, enmArch, cbImage, pfnRead, pfnDtor, pvUser, phLdrMod));
if (!pfnRead || !pfnDtor)
AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
if (!pfnDtor)
pfnDtor = rtldrRdrMemDefaultDtor;
else
AssertPtrReturn(pfnRead, VERR_INVALID_POINTER);
/* The rest of the validations will call the destructor. */
AssertMsgReturnStmt(!(fFlags & ~RTLDR_O_VALID_MASK), ("%#x\n", fFlags),
pfnDtor(pvUser), VERR_INVALID_PARAMETER);
AssertMsgReturnStmt(enmArch > RTLDRARCH_INVALID && enmArch < RTLDRARCH_END, ("%d\n", enmArch),
pfnDtor(pvUser), VERR_INVALID_PARAMETER);
if (!pfnRead)
pfnRead = rtldrRdrMemDefaultReader;
else
AssertReturnStmt(RT_VALID_PTR(pfnRead), pfnDtor(pvUser), VERR_INVALID_POINTER);
AssertReturnStmt(cbImage > 0, pfnDtor(pvUser), VERR_INVALID_PARAMETER);
/*
* Resolve RTLDRARCH_HOST.
*/
if (enmArch == RTLDRARCH_HOST)
#if defined(RT_ARCH_AMD64)
enmArch = RTLDRARCH_AMD64;
#elif defined(RT_ARCH_X86)
enmArch = RTLDRARCH_X86_32;
#else
enmArch = RTLDRARCH_WHATEVER;
#endif
/*
* Create file reader & invoke worker which identifies and calls the image interpreter.
*/
PRTLDRREADER pReader = NULL; /* gcc may be wrong */
int rc = rtldrRdrMem_Create(&pReader, pszName, cbImage, pfnRead, pfnDtor, pvUser);
if (RT_SUCCESS(rc))
{
rc = RTLdrOpenWithReader(pReader, fFlags, enmArch, phLdrMod, NULL);
if (RT_SUCCESS(rc))
{
LogFlow(("RTLdrOpen: return %Rrc *phLdrMod\n", rc, *phLdrMod));
return rc;
}
pReader->pfnDestroy(pReader);
}
else
pfnDtor(pvUser);
*phLdrMod = NIL_RTLDRMOD;
LogFlow(("RTLdrOpen: return %Rrc\n", rc));
return rc;
}
RT_EXPORT_SYMBOL(RTLdrOpenInMemory);