mach_kernel-r0drv-darwin.cpp revision cae3b8810c39392e70dfb12b951431018a80fcba
/* $Id$ */
/** @file
* IPRT - mach_kernel symbol resolving hack, R0 Driver, Darwin.
*/
/*
* Copyright (C) 2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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 *
*******************************************************************************/
#ifdef IN_RING0
# include "the-darwin-kernel.h"
RT_C_DECLS_BEGIN /* Buggy 10.4 headers, fixed in 10.5. */
# include <sys/kpi_mbuf.h>
# include <net/kpi_interfacefilter.h>
# include <sys/kpi_socket.h>
# include <sys/kpi_socketfilter.h>
# include <sys/vnode_if.h>
# include <vfs/vfs_support.h>
#endif
#include "internal/ldrMach-O.h"
/** @def MY_CPU_TYPE
* The CPU type targeted by the compiler. */
/** @def MY_CPU_TYPE
* The "ALL" CPU subtype targeted by the compiler. */
/** @def MY_MACHO_HEADER
* The Mach-O header targeted by the compiler. */
/** @def MY_MACHO_MAGIC
* The Mach-O header magic we're targeting. */
/** @def MY_SEGMENT_COMMAND
* The segment command targeted by the compiler. */
/** @def MY_SECTION
* The section struture targeted by the compiler. */
/** @def MY_NLIST
* The symbol table entry targeted by the compiler. */
#ifdef RT_ARCH_X86
# define MY_CPU_TYPE CPU_TYPE_I386
# define MY_CPU_SUBTYPE_ALL CPU_SUBTYPE_I386_ALL
# define MY_MACHO_HEADER mach_header_32_t
# define MY_MACHO_MAGIC IMAGE_MACHO32_SIGNATURE
# define MY_SEGMENT_COMMAND segment_command_32_t
# define MY_SECTION section_32_t
# define MY_NLIST macho_nlist_32_t
#elif defined(RT_ARCH_AMD64)
# define MY_CPU_TYPE CPU_TYPE_X86_64
# define MY_CPU_SUBTYPE_ALL CPU_SUBTYPE_X86_64_ALL
# define MY_MACHO_HEADER mach_header_64_t
# define MY_MACHO_MAGIC IMAGE_MACHO64_SIGNATURE
# define MY_SEGMENT_COMMAND segment_command_64_t
# define MY_SECTION section_64_t
# define MY_NLIST macho_nlist_64_t
#else
# error "Port me!"
#endif
/** @name Return macros for make it simpler to track down too paranoid code.
* @{
*/
#ifdef DEBUG
# define RETURN_VERR_BAD_EXE_FORMAT \
# define RETURN_VERR_LDR_UNEXPECTED \
# define RETURN_VERR_LDR_ARCH_MISMATCH \
#else
# define RETURN_VERR_BAD_EXE_FORMAT do { return VERR_BAD_EXE_FORMAT; } while (0)
# define RETURN_VERR_LDR_UNEXPECTED do { return VERR_LDR_UNEXPECTED; } while (0)
# define RETURN_VERR_LDR_ARCH_MISMATCH do { return VERR_LDR_ARCH_MISMATCH; } while (0)
#endif
/** @} */
#define VERR_LDR_UNEXPECTED (-641)
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Our internal representation of the mach_kernel after loading it's symbols
* and successfully resolving their addresses.
*/
typedef struct RTR0MACHKERNELINT
{
/** @name Result.
* @{ */
/** Pointer to the string table. */
char *pachStrTab;
/** The size of the string table. */
/** The file offset of the string table. */
/** Pointer to the symbol table. */
/** The size of the symbol table. */
/** The file offset of the symbol table. */
/** @} */
/** @name Used during loading.
* @{ */
/** The file handle. */
/** The architecture image offset (fat_arch_t::offset). */
/** The architecture image size (fat_arch_t::size). */
/** The number of load commands (mach_header_XX_t::ncmds). */
/** The size of the load commands. */
/** The load commands. */
/** Section pointer table (points into the load commands). */
/** The number of sections. */
/** @} */
/** Buffer space. */
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
#ifdef DEBUG
static bool g_fBreakpointOnError = false;
#endif
#ifdef IN_RING0
/** Default file permissions for newly created files. */
#else
# define RT_FILE_PERMISSION (00600)
#endif
/**
* Darwin kernel file handle data.
*/
typedef struct RTFILEINT
{
/** Magic value (RTFILE_MAGIC). */
/** The open mode flags passed to the kernel API. */
int fOpenMode;
/** The open flags passed to RTFileOpen. */
/** The VFS context in which the file was opened. */
/** The vnode returned by vnode_open. */
} RTFILEINT;
/** Magic number for RTFILEINT::u32Magic (To Be Determined). */
{
if (!pThis)
return VERR_NO_MEMORY;
{
int fVnFlags = 0; /* VNODE_LOOKUP_XXX */
int fOpenMode = 0;
if (fOpen & RTFILE_O_NON_BLOCK)
fOpenMode |= O_NONBLOCK;
if (fOpen & RTFILE_O_WRITE_THROUGH)
switch (fOpen & RTFILE_O_ACTION_MASK)
{
case RTFILE_O_OPEN: break;
case RTFILE_O_CREATE_REPLACE: fOpenMode |= O_CREAT | O_TRUNC; break; /** @todo replacing needs fixing, this is *not* a 1:1 mapping! */
}
if (fOpen & RTFILE_O_TRUNCATE)
switch (fOpen & RTFILE_O_ACCESS_MASK)
{
case RTFILE_O_READ:
break;
case RTFILE_O_WRITE:
break;
case RTFILE_O_READWRITE:
break;
default:
return VERR_INVALID_PARAMETER;
}
if (rc == 0)
{
return VINF_SUCCESS;
}
}
else
return rc;
}
{
if (hFile == NIL_RTFILE)
return VINF_SUCCESS;
return RTErrConvertFromErrno(rc);
}
{
#if 0 /* Added in 10.6, grr. */
if (!pcbRead)
rc = vn_rdwr(UIO_READ, pThis->hVnode, (char *)pvBuf, cbToRead, offNative, UIO_SYSSPACE, 0 /*ioflg*/,
else
{
int cbLeft = 0;
rc = vn_rdwr(UIO_READ, pThis->hVnode, (char *)pvBuf, cbToRead, offNative, UIO_SYSSPACE, 0 /*ioflg*/,
}
#else
if (!hUio)
return VERR_NO_MEMORY;
{
if (pcbRead)
}
else
return rc;
#endif
}
#endif
/**
* Close and free up resources we no longer needs.
*
* @param pThis The internal scratch data.
*/
{
}
/**
* Looks up a kernel symbol.
*
*
* @returns The symbol address on success, 0 on failure.
* @param pThis The internal scratch data.
* @param pszSymbol The symbol to resolve. Automatically prefixed
* with an underscore.
*/
{
#if 1
/* linear search. */
{
continue;
if ( *pszTabName == '_'
}
#else
/** @todo binary search. */
#endif
return 0;
}
/* Rainy day: Find the right headers for these symbols ... if there are any. */
extern "C" void ev_try_lock(void);
extern "C" void OSMalloc(void);
extern "C" void OSlibkernInit(void);
extern "C" int osrelease;
extern "C" int ostype;
extern "C" void kdp_set_interface(void);
/**
* Check the symbol table against symbols we known symbols.
*
* This is done to detect whether the on disk image and the in
* memory images matches. Mismatches could stem from user
* replacing the default kernel image on disk.
*
* @returns IPRT status code.
* @param pThis The internal scratch data.
*/
{
static struct
{
const char *pszName;
} const s_aStandardCandles[] =
{
#ifdef IN_RING0
#else
#endif
/* IOKit: */
/* Libkern: */
/* Mach: */
/* BSDKernel: */
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
#else
#endif
//KNOWN_ENTRY(spec_write),
//KNOWN_ENTRY(sysctl_int),
/* Unsupported: */
};
for (unsigned i = 0; i < RT_ELEMENTS(s_aStandardCandles); i++)
{
#ifdef IN_RING0
#else
if (uAddr == 0)
#endif
{
AssertLogRelMsgFailed(("%s (%p != %p)\n", s_aStandardCandles[i].pszName, uAddr, s_aStandardCandles[i].uAddr));
return VERR_INTERNAL_ERROR_2;
}
}
return VINF_SUCCESS;
}
/**
* Loads and validates the symbol and string tables.
*
* @returns IPRT status code.
* @param pThis The internal scratch data.
*/
{
/*
* Load the tables.
*/
return VERR_NO_MEMORY;
if (RT_FAILURE(rc))
return rc;
if (!pThis->pachStrTab)
return VERR_NO_MEMORY;
if (RT_FAILURE(rc))
return rc;
/*
* The first string table symbol must be a zero length name.
*/
/*
* Validate the symbol table.
*/
const char *pszPrev = "";
{
#ifdef IN_RING3
RTAssertMsg2("%05i: %02x:%08x %02x %04x %s\n", iSym, pSym->n_sect, pSym->n_value, pSym->n_type, pSym->n_desc, pszSym);
#endif
RETURN_VERR_BAD_EXE_FORMAT; /* not sorted */
{
{
case MACHO_N_SECT:
break;
case MACHO_N_ABS:
#if 0 /* Spec say MACHO_NO_SECT, __mh_execute_header has 1 with 10.7/amd64 */
#else
#endif
break;
case MACHO_N_UNDF:
/* No undefined or common symbols in the kernel. */
case MACHO_N_INDR:
/* No indirect symbols in the kernel. */
case MACHO_N_PBUD:
/* No prebound symbols in the kernel. */
default:
}
}
/* else: Ignore debug symbols. */
}
return VINF_SUCCESS;
}
/**
* Loads the load commands and validates them.
*
* @returns IPRT status code.
* @param pThis The internal scratch data.
*/
{
return VERR_NO_MEMORY;
if (RT_FAILURE(rc))
return rc;
/*
* Validate the relevant commands, picking up sections and the symbol
* table location.
*/
{
/* cmd index & offset. */
break;
/* cmdsize */
/* cmd */
{
/* Validate and store the symbol table details. */
case LC_SYMTAB:
{
break;
}
/* Validate the segment. */
#if ARCH_BITS == 32
case LC_SEGMENT_32:
case LC_SEGMENT_64:
#else
#endif
{
{
}
/* Validate the sections. */
uint32_t uAlignment = 0;
{
{
case S_REGULAR:
case S_CSTRING_LITERALS:
case S_MOD_INIT_FUNC_POINTERS:
case S_MOD_TERM_FUNC_POINTERS:
case S_COALESCED:
break;
case S_ZEROFILL:
break;
/* not observed */
case S_SYMBOL_STUBS:
case S_INTERPOSING:
case S_4BYTE_LITERALS:
case S_8BYTE_LITERALS:
case S_16BYTE_LITERALS:
case S_DTRACE_DOF:
case S_LAZY_SYMBOL_POINTERS:
case S_GB_ZEROFILL:
default:
}
/* Add to the section table. */
}
break;
}
case LC_UUID:
break;
case LC_DYSYMTAB:
case LC_UNIXTHREAD:
break;
/* not observed */
case LC_SYMSEG:
#if ARCH_BITS == 32
case LC_SEGMENT_64:
case LC_SEGMENT_32:
#endif
case LC_ROUTINES_64:
case LC_ROUTINES:
case LC_THREAD:
case LC_LOADFVMLIB:
case LC_IDFVMLIB:
case LC_IDENT:
case LC_FVMFILE:
case LC_PREPAGE:
case LC_TWOLEVEL_HINTS:
case LC_PREBIND_CKSUM:
/* dylib */
case LC_LOAD_DYLIB:
case LC_ID_DYLIB:
case LC_LOAD_DYLINKER:
case LC_ID_DYLINKER:
case LC_PREBOUND_DYLIB:
case LC_LOAD_WEAK_DYLIB & ~LC_REQ_DYLD:
case LC_SUB_FRAMEWORK:
case LC_SUB_UMBRELLA:
case LC_SUB_CLIENT:
case LC_SUB_LIBRARY:
default:
}
/* next */
}
return VINF_SUCCESS;
}
/**
* Loads the FAT and MACHO headers, noting down the relevant info.
*
* @returns IPRT status code.
* @param pThis The internal scratch data.
*/
{
uint32_t i;
/*
* Read the first bit of the file, parse the FAT if found there.
*/
int rc = RTFileReadAt(pThis->hFile, 0, pThis->abBuf, sizeof(fat_header_t) + sizeof(fat_arch_t) * 16, NULL);
if (RT_FAILURE(rc))
return rc;
/* Correct FAT endian first. */
{
while (i-- > 0)
{
}
}
/* Lookup our architecture in the FAT. */
{
{
{
break;
}
}
}
/*
* Read the Mach-O header and validate it.
*/
if (RT_FAILURE(rc))
return rc;
{
}
return VINF_SUCCESS;
}
{
if (!pThis)
return VERR_NO_MEMORY;
int rc = RTFileOpen(&pThis->hFile, pszMachKernel, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
else
return rc;
}
RTR0DECL(int) RTR0MachKernelGetSymbol(RTR0MACHKERNEL hKernel, const char *pszSymbol, void **ppvValue)
{
if (ppvValue)
if (!uValue)
return VERR_SYMBOL_NOT_FOUND;
return VINF_SUCCESS;
}
{
if (hKernel == NIL_RTR0MACHKERNEL)
return VINF_SUCCESS;
return VINF_SUCCESS;
}