VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/darwin/dbgkrnlinfo-r0drv-darwin.cpp@ 41050

Last change on this file since 41050 was 41050, checked in by vboxsync, 13 years ago

Made dbgkrnlinfo-r0drv-darwin.cpp work with 10.8 preview 3.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.8 KB
Line 
1/* $Id: dbgkrnlinfo-r0drv-darwin.cpp 41050 2012-04-25 10:10:16Z vboxsync $ */
2/** @file
3 * IPRT - Kernel Debug Information, R0 Driver, Darwin.
4 */
5
6/*
7 * Copyright (C) 2011-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#ifdef IN_RING0
32# include "the-darwin-kernel.h"
33# include <sys/kauth.h>
34RT_C_DECLS_BEGIN /* Buggy 10.4 headers, fixed in 10.5. */
35# include <sys/kpi_mbuf.h>
36# include <net/kpi_interfacefilter.h>
37# include <sys/kpi_socket.h>
38# include <sys/kpi_socketfilter.h>
39RT_C_DECLS_END
40# include <sys/buf.h>
41# include <sys/vm.h>
42# include <sys/vnode_if.h>
43/*# include <sys/sysctl.h>*/
44# include <sys/systm.h>
45# include <vfs/vfs_support.h>
46/*# include <miscfs/specfs/specdev.h>*/
47#endif
48
49#include "internal/iprt.h"
50#include <iprt/dbg.h>
51
52#include <iprt/asm.h>
53#include <iprt/assert.h>
54#include <iprt/err.h>
55#include <iprt/assert.h>
56#include <iprt/file.h>
57#include <iprt/log.h>
58#include <iprt/mem.h>
59#include <iprt/string.h>
60#include "internal/ldrMach-O.h"
61#include "internal/magics.h"
62
63/** @def MY_CPU_TYPE
64 * The CPU type targeted by the compiler. */
65/** @def MY_CPU_TYPE
66 * The "ALL" CPU subtype targeted by the compiler. */
67/** @def MY_MACHO_HEADER
68 * The Mach-O header targeted by the compiler. */
69/** @def MY_MACHO_MAGIC
70 * The Mach-O header magic we're targeting. */
71/** @def MY_SEGMENT_COMMAND
72 * The segment command targeted by the compiler. */
73/** @def MY_SECTION
74 * The section struture targeted by the compiler. */
75/** @def MY_NLIST
76 * The symbol table entry targeted by the compiler. */
77#ifdef RT_ARCH_X86
78# define MY_CPU_TYPE CPU_TYPE_I386
79# define MY_CPU_SUBTYPE_ALL CPU_SUBTYPE_I386_ALL
80# define MY_MACHO_HEADER mach_header_32_t
81# define MY_MACHO_MAGIC IMAGE_MACHO32_SIGNATURE
82# define MY_SEGMENT_COMMAND segment_command_32_t
83# define MY_SECTION section_32_t
84# define MY_NLIST macho_nlist_32_t
85
86#elif defined(RT_ARCH_AMD64)
87# define MY_CPU_TYPE CPU_TYPE_X86_64
88# define MY_CPU_SUBTYPE_ALL CPU_SUBTYPE_X86_64_ALL
89# define MY_MACHO_HEADER mach_header_64_t
90# define MY_MACHO_MAGIC IMAGE_MACHO64_SIGNATURE
91# define MY_SEGMENT_COMMAND segment_command_64_t
92# define MY_SECTION section_64_t
93# define MY_NLIST macho_nlist_64_t
94
95#else
96# error "Port me!"
97#endif
98
99/** @name Return macros for make it simpler to track down too paranoid code.
100 * @{
101 */
102#ifdef DEBUG
103# define RETURN_VERR_BAD_EXE_FORMAT \
104 do { Assert(!g_fBreakpointOnError); return VERR_BAD_EXE_FORMAT; } while (0)
105# define RETURN_VERR_LDR_UNEXPECTED \
106 do { Assert(!g_fBreakpointOnError); return VERR_LDR_UNEXPECTED; } while (0)
107# define RETURN_VERR_LDR_ARCH_MISMATCH \
108 do { Assert(!g_fBreakpointOnError); return VERR_LDR_ARCH_MISMATCH; } while (0)
109#else
110# define RETURN_VERR_BAD_EXE_FORMAT do { return VERR_BAD_EXE_FORMAT; } while (0)
111# define RETURN_VERR_LDR_UNEXPECTED do { return VERR_LDR_UNEXPECTED; } while (0)
112# define RETURN_VERR_LDR_ARCH_MISMATCH do { return VERR_LDR_ARCH_MISMATCH; } while (0)
113#endif
114/** @} */
115
116#define VERR_LDR_UNEXPECTED (-641)
117
118
119/*******************************************************************************
120* Structures and Typedefs *
121*******************************************************************************/
122/**
123 * Our internal representation of the mach_kernel after loading it's symbols
124 * and successfully resolving their addresses.
125 */
126typedef struct RTDBGKRNLINFOINT
127{
128 /** Magic value (RTDBGKRNLINFO_MAGIC). */
129 uint32_t u32Magic;
130 /** Reference counter. */
131 uint32_t volatile cRefs;
132
133 /** @name Result.
134 * @{ */
135 /** Pointer to the string table. */
136 char *pachStrTab;
137 /** The size of the string table. */
138 uint32_t cbStrTab;
139 /** The file offset of the string table. */
140 uint32_t offStrTab;
141 /** Pointer to the symbol table. */
142 MY_NLIST *paSyms;
143 /** The size of the symbol table. */
144 uint32_t cSyms;
145 /** The file offset of the symbol table. */
146 uint32_t offSyms;
147 /** @} */
148
149 /** @name Used during loading.
150 * @{ */
151 /** The file handle. */
152 RTFILE hFile;
153 /** The architecture image offset (fat_arch_t::offset). */
154 uint64_t offArch;
155 /** The architecture image size (fat_arch_t::size). */
156 uint32_t cbArch;
157 /** The number of load commands (mach_header_XX_t::ncmds). */
158 uint32_t cLoadCmds;
159 /** The size of the load commands. */
160 uint32_t cbLoadCmds;
161 /** The load commands. */
162 load_command_t *pLoadCmds;
163 /** Section pointer table (points into the load commands). */
164 MY_SECTION const *apSections[MACHO_MAX_SECT];
165 /** The number of sections. */
166 uint32_t cSections;
167 /** @} */
168
169 /** Buffer space. */
170 char abBuf[_4K];
171} RTDBGKRNLINFOINT;
172
173
174/*******************************************************************************
175* Structures and Typedefs *
176*******************************************************************************/
177#ifdef DEBUG
178static bool g_fBreakpointOnError = false;
179#endif
180
181
182#ifdef IN_RING0
183
184/** Default file permissions for newly created files. */
185#if defined(S_IRUSR) && defined(S_IWUSR)
186# define RT_FILE_PERMISSION (S_IRUSR | S_IWUSR)
187#else
188# define RT_FILE_PERMISSION (00600)
189#endif
190
191/**
192 * Darwin kernel file handle data.
193 */
194typedef struct RTFILEINT
195{
196 /** Magic value (RTFILE_MAGIC). */
197 uint32_t u32Magic;
198 /** The open mode flags passed to the kernel API. */
199 int fOpenMode;
200 /** The open flags passed to RTFileOpen. */
201 uint64_t fOpen;
202 /** The VFS context in which the file was opened. */
203 vfs_context_t hVfsCtx;
204 /** The vnode returned by vnode_open. */
205 vnode_t hVnode;
206} RTFILEINT;
207/** Magic number for RTFILEINT::u32Magic (To Be Determined). */
208#define RTFILE_MAGIC UINT32_C(0x01020304)
209
210
211RTDECL(int) RTFileOpen(PRTFILE phFile, const char *pszFilename, uint64_t fOpen)
212{
213 RTFILEINT *pThis = (RTFILEINT *)RTMemAllocZ(sizeof(*pThis));
214 if (!pThis)
215 return VERR_NO_MEMORY;
216
217 errno_t rc;
218 pThis->u32Magic = RTFILE_MAGIC;
219 pThis->fOpen = fOpen;
220 pThis->hVfsCtx = vfs_context_current();
221 if (pThis->hVfsCtx != NULL)
222 {
223 int fCMode = (fOpen & RTFILE_O_CREATE_MODE_MASK)
224 ? (fOpen & RTFILE_O_CREATE_MODE_MASK) >> RTFILE_O_CREATE_MODE_SHIFT
225 : RT_FILE_PERMISSION;
226 int fVnFlags = 0; /* VNODE_LOOKUP_XXX */
227 int fOpenMode = 0;
228 if (fOpen & RTFILE_O_NON_BLOCK)
229 fOpenMode |= O_NONBLOCK;
230 if (fOpen & RTFILE_O_WRITE_THROUGH)
231 fOpenMode |= O_SYNC;
232
233 /* create/truncate file */
234 switch (fOpen & RTFILE_O_ACTION_MASK)
235 {
236 case RTFILE_O_OPEN: break;
237 case RTFILE_O_OPEN_CREATE: fOpenMode |= O_CREAT; break;
238 case RTFILE_O_CREATE: fOpenMode |= O_CREAT | O_EXCL; break;
239 case RTFILE_O_CREATE_REPLACE: fOpenMode |= O_CREAT | O_TRUNC; break; /** @todo replacing needs fixing, this is *not* a 1:1 mapping! */
240 }
241 if (fOpen & RTFILE_O_TRUNCATE)
242 fOpenMode |= O_TRUNC;
243
244 switch (fOpen & RTFILE_O_ACCESS_MASK)
245 {
246 case RTFILE_O_READ:
247 fOpenMode |= FREAD;
248 break;
249 case RTFILE_O_WRITE:
250 fOpenMode |= fOpen & RTFILE_O_APPEND ? O_APPEND | FWRITE : FWRITE;
251 break;
252 case RTFILE_O_READWRITE:
253 fOpenMode |= fOpen & RTFILE_O_APPEND ? O_APPEND | FWRITE | FREAD : FWRITE | FREAD;
254 break;
255 default:
256 AssertMsgFailed(("RTFileOpen received an invalid RW value, fOpen=%#x\n", fOpen));
257 return VERR_INVALID_PARAMETER;
258 }
259
260 pThis->fOpenMode = fOpenMode;
261 rc = vnode_open(pszFilename, fOpenMode, fCMode, fVnFlags, &pThis->hVnode, pThis->hVfsCtx);
262 if (rc == 0)
263 {
264 *phFile = pThis;
265 return VINF_SUCCESS;
266 }
267
268 rc = RTErrConvertFromErrno(rc);
269 }
270 else
271 rc = VERR_INTERNAL_ERROR_5;
272 RTMemFree(pThis);
273
274 return rc;
275}
276
277
278RTDECL(int) RTFileClose(RTFILE hFile)
279{
280 if (hFile == NIL_RTFILE)
281 return VINF_SUCCESS;
282
283 RTFILEINT *pThis = hFile;
284 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
285 AssertReturn(pThis->u32Magic == RTFILE_MAGIC, VERR_INVALID_HANDLE);
286 pThis->u32Magic = ~RTFILE_MAGIC;
287
288 errno_t rc = vnode_close(pThis->hVnode, pThis->fOpenMode & (FREAD | FWRITE), pThis->hVfsCtx);
289
290 RTMemFree(pThis);
291 return RTErrConvertFromErrno(rc);
292}
293
294
295RTDECL(int) RTFileReadAt(RTFILE hFile, RTFOFF off, void *pvBuf, size_t cbToRead, size_t *pcbRead)
296{
297 RTFILEINT *pThis = hFile;
298 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
299 AssertReturn(pThis->u32Magic == RTFILE_MAGIC, VERR_INVALID_HANDLE);
300
301 off_t offNative = (off_t)off;
302 AssertReturn((RTFOFF)offNative == off, VERR_OUT_OF_RANGE);
303
304#if 0 /* Added in 10.6, grr. */
305 errno_t rc;
306 if (!pcbRead)
307 rc = vn_rdwr(UIO_READ, pThis->hVnode, (char *)pvBuf, cbToRead, offNative, UIO_SYSSPACE, 0 /*ioflg*/,
308 vfs_context_ucred(pThis->hVfsCtx), NULL, vfs_context_proc(pThis->hVfsCtx));
309 else
310 {
311 int cbLeft = 0;
312 rc = vn_rdwr(UIO_READ, pThis->hVnode, (char *)pvBuf, cbToRead, offNative, UIO_SYSSPACE, 0 /*ioflg*/,
313 vfs_context_ucred(pThis->hVfsCtx), &cbLeft, vfs_context_proc(pThis->hVfsCtx));
314 *pcbRead = cbToRead - cbLeft;
315 }
316 return !rc ? VINF_SUCCESS : RTErrConvertFromErrno(rc);
317
318#else
319 uio_t hUio = uio_create(1, offNative, UIO_SYSSPACE, UIO_READ);
320 if (!hUio)
321 return VERR_NO_MEMORY;
322 errno_t rc;
323 if (uio_addiov(hUio, (user_addr_t)(uintptr_t)pvBuf, cbToRead) == 0)
324 {
325 rc = VNOP_READ(pThis->hVnode, hUio, 0 /*ioflg*/, pThis->hVfsCtx);
326 if (pcbRead)
327 *pcbRead = cbToRead - uio_resid(hUio);
328 else if (!rc && uio_resid(hUio))
329 rc = VERR_FILE_IO_ERROR;
330 }
331 else
332 rc = VERR_INTERNAL_ERROR_3;
333 uio_free(hUio);
334 return rc;
335
336#endif
337}
338
339#endif
340
341
342/**
343 * Close and free up resources we no longer needs.
344 *
345 * @param pThis The internal scratch data.
346 */
347static void rtR0DbgKrnlDarwinLoadDone(RTDBGKRNLINFOINT *pThis)
348{
349 RTFileClose(pThis->hFile);
350 pThis->hFile = NIL_RTFILE;
351
352 RTMemFree(pThis->pLoadCmds);
353 pThis->pLoadCmds = NULL;
354 memset((void *)&pThis->apSections[0], 0, sizeof(pThis->apSections[0]) * MACHO_MAX_SECT);
355}
356
357
358/**
359 * Looks up a kernel symbol.
360 *
361 *
362 * @returns The symbol address on success, 0 on failure.
363 * @param pThis The internal scratch data.
364 * @param pszSymbol The symbol to resolve. Automatically prefixed
365 * with an underscore.
366 */
367static uintptr_t rtR0DbgKrnlDarwinLookup(RTDBGKRNLINFOINT *pThis, const char *pszSymbol)
368{
369 uint32_t const cSyms = pThis->cSyms;
370 MY_NLIST const *pSym = pThis->paSyms;
371
372#if 1
373 /* linear search. */
374 for (uint32_t iSym = 0; iSym < cSyms; iSym++, pSym++)
375 {
376 if (pSym->n_type & MACHO_N_STAB)
377 continue;
378
379 const char *pszTabName= &pThis->pachStrTab[(uint32_t)pSym->n_un.n_strx];
380 if ( *pszTabName == '_'
381 && strcmp(pszTabName + 1, pszSymbol) == 0)
382 return pSym->n_value;
383 }
384#else
385 /** @todo binary search. */
386
387#endif
388 return 0;
389}
390
391
392/* Rainy day: Find the right headers for these symbols ... if there are any. */
393extern "C" void ev_try_lock(void);
394extern "C" void OSMalloc(void);
395extern "C" void OSlibkernInit(void);
396extern "C" int osrelease;
397extern "C" int ostype;
398extern "C" void kdp_set_interface(void);
399
400
401/**
402 * Check the symbol table against symbols we known symbols.
403 *
404 * This is done to detect whether the on disk image and the in
405 * memory images matches. Mismatches could stem from user
406 * replacing the default kernel image on disk.
407 *
408 * @returns IPRT status code.
409 * @param pThis The internal scratch data.
410 */
411static int rtR0DbgKrnlDarwinCheckStandardSymbols(RTDBGKRNLINFOINT *pThis)
412{
413 static struct
414 {
415 const char *pszName;
416 uintptr_t uAddr;
417 } const s_aStandardCandles[] =
418 {
419#ifdef IN_RING0
420# define KNOWN_ENTRY(a_Sym) { #a_Sym, (uintptr_t)&a_Sym }
421#else
422# define KNOWN_ENTRY(a_Sym) { #a_Sym, 0 }
423#endif
424 /* IOKit: */
425 KNOWN_ENTRY(IOMalloc),
426 KNOWN_ENTRY(IOFree),
427 KNOWN_ENTRY(IOSleep),
428 KNOWN_ENTRY(IORWLockAlloc),
429 KNOWN_ENTRY(IORecursiveLockLock),
430 KNOWN_ENTRY(IOSimpleLockAlloc),
431 KNOWN_ENTRY(PE_cpu_halt),
432 KNOWN_ENTRY(gIOKitDebug),
433 KNOWN_ENTRY(gIOServicePlane),
434 KNOWN_ENTRY(ev_try_lock),
435
436 /* Libkern: */
437 KNOWN_ENTRY(OSAddAtomic),
438 KNOWN_ENTRY(OSBitAndAtomic),
439 KNOWN_ENTRY(OSBitOrAtomic),
440 KNOWN_ENTRY(OSBitXorAtomic),
441 KNOWN_ENTRY(OSCompareAndSwap),
442 KNOWN_ENTRY(OSMalloc),
443 KNOWN_ENTRY(OSlibkernInit),
444 KNOWN_ENTRY(bcmp),
445 KNOWN_ENTRY(copyout),
446 KNOWN_ENTRY(copyin),
447 KNOWN_ENTRY(kprintf),
448 KNOWN_ENTRY(printf),
449 KNOWN_ENTRY(lck_grp_alloc_init),
450 KNOWN_ENTRY(lck_mtx_alloc_init),
451 KNOWN_ENTRY(lck_rw_alloc_init),
452 KNOWN_ENTRY(lck_spin_alloc_init),
453 KNOWN_ENTRY(osrelease),
454 KNOWN_ENTRY(ostype),
455 KNOWN_ENTRY(panic),
456 KNOWN_ENTRY(strprefix),
457 KNOWN_ENTRY(sysctlbyname),
458 KNOWN_ENTRY(vsscanf),
459 KNOWN_ENTRY(page_mask),
460
461 /* Mach: */
462 KNOWN_ENTRY(absolutetime_to_nanoseconds),
463 KNOWN_ENTRY(assert_wait),
464 KNOWN_ENTRY(clock_delay_until),
465 KNOWN_ENTRY(clock_get_uptime),
466 KNOWN_ENTRY(current_task),
467 KNOWN_ENTRY(current_thread),
468 KNOWN_ENTRY(kernel_task),
469 KNOWN_ENTRY(lck_mtx_sleep),
470 KNOWN_ENTRY(lck_rw_sleep),
471 KNOWN_ENTRY(lck_spin_sleep),
472 KNOWN_ENTRY(mach_absolute_time),
473 KNOWN_ENTRY(semaphore_create),
474 KNOWN_ENTRY(task_reference),
475 KNOWN_ENTRY(thread_block),
476 KNOWN_ENTRY(thread_reference),
477 KNOWN_ENTRY(thread_terminate),
478 KNOWN_ENTRY(thread_wakeup_prim),
479
480 /* BSDKernel: */
481 KNOWN_ENTRY(buf_size),
482 KNOWN_ENTRY(copystr),
483 KNOWN_ENTRY(current_proc),
484 KNOWN_ENTRY(ifnet_hdrlen),
485 KNOWN_ENTRY(ifnet_set_promiscuous),
486 KNOWN_ENTRY(kauth_getuid),
487#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
488 KNOWN_ENTRY(kauth_cred_unref),
489#else
490 KNOWN_ENTRY(kauth_cred_rele),
491#endif
492 KNOWN_ENTRY(mbuf_data),
493 KNOWN_ENTRY(msleep),
494 KNOWN_ENTRY(nanotime),
495 KNOWN_ENTRY(nop_close),
496 KNOWN_ENTRY(proc_pid),
497 KNOWN_ENTRY(sock_accept),
498 KNOWN_ENTRY(sockopt_name),
499 //KNOWN_ENTRY(spec_write),
500 KNOWN_ENTRY(suword),
501 //KNOWN_ENTRY(sysctl_int),
502 KNOWN_ENTRY(uio_rw),
503 KNOWN_ENTRY(vfs_flags),
504 KNOWN_ENTRY(vfs_name),
505 KNOWN_ENTRY(vfs_statfs),
506 KNOWN_ENTRY(VNOP_READ),
507 KNOWN_ENTRY(uio_create),
508 KNOWN_ENTRY(uio_addiov),
509 KNOWN_ENTRY(uio_free),
510 KNOWN_ENTRY(vnode_get),
511 KNOWN_ENTRY(vnode_open),
512 KNOWN_ENTRY(vnode_ref),
513 KNOWN_ENTRY(vnode_rele),
514 KNOWN_ENTRY(vnop_close_desc),
515 KNOWN_ENTRY(wakeup),
516 KNOWN_ENTRY(wakeup_one),
517
518 /* Unsupported: */
519 KNOWN_ENTRY(kdp_set_interface),
520 KNOWN_ENTRY(pmap_find_phys),
521 KNOWN_ENTRY(vm_map),
522 KNOWN_ENTRY(vm_protect),
523 KNOWN_ENTRY(vm_region),
524 KNOWN_ENTRY(vm_map_wire),
525 KNOWN_ENTRY(PE_kputc),
526 };
527
528 for (unsigned i = 0; i < RT_ELEMENTS(s_aStandardCandles); i++)
529 {
530 uintptr_t uAddr = rtR0DbgKrnlDarwinLookup(pThis, s_aStandardCandles[i].pszName);
531#ifdef IN_RING0
532 if (uAddr != s_aStandardCandles[i].uAddr)
533#else
534 if (uAddr == 0)
535#endif
536 {
537 AssertLogRelMsgFailed(("%s (%p != %p)\n", s_aStandardCandles[i].pszName, uAddr, s_aStandardCandles[i].uAddr));
538 return VERR_INTERNAL_ERROR_2;
539 }
540 }
541 return VINF_SUCCESS;
542}
543
544
545/**
546 * Loads and validates the symbol and string tables.
547 *
548 * @returns IPRT status code.
549 * @param pThis The internal scratch data.
550 */
551static int rtR0DbgKrnlDarwinLoadSymTab(RTDBGKRNLINFOINT *pThis)
552{
553 /*
554 * Load the tables.
555 */
556 pThis->paSyms = (MY_NLIST *)RTMemAllocZ(pThis->cSyms * sizeof(MY_NLIST));
557 if (!pThis->paSyms)
558 return VERR_NO_MEMORY;
559
560 int rc = RTFileReadAt(pThis->hFile, pThis->offArch + pThis->offSyms,
561 pThis->paSyms, pThis->cSyms * sizeof(MY_NLIST), NULL);
562 if (RT_FAILURE(rc))
563 return rc;
564
565 pThis->pachStrTab = (char *)RTMemAllocZ(pThis->cbStrTab + 1);
566 if (!pThis->pachStrTab)
567 return VERR_NO_MEMORY;
568
569 rc = RTFileReadAt(pThis->hFile, pThis->offArch + pThis->offStrTab,
570 pThis->pachStrTab, pThis->cbStrTab, NULL);
571 if (RT_FAILURE(rc))
572 return rc;
573
574 /*
575 * The first string table symbol must be a zero length name.
576 */
577 if (pThis->pachStrTab[0] != '\0')
578 RETURN_VERR_BAD_EXE_FORMAT;
579
580 /*
581 * Validate the symbol table.
582 */
583 const char *pszPrev = "";
584 uint32_t const cSyms = pThis->cSyms;
585 MY_NLIST const *pSym = pThis->paSyms;
586 for (uint32_t iSym = 0; iSym < cSyms; iSym++, pSym++)
587 {
588 if ((uint32_t)pSym->n_un.n_strx >= pThis->cbStrTab)
589 RETURN_VERR_BAD_EXE_FORMAT;
590 const char *pszSym = &pThis->pachStrTab[(uint32_t)pSym->n_un.n_strx];
591#ifdef IN_RING3
592 RTAssertMsg2("%05i: %02x:%08llx %02x %04x %s\n", iSym, pSym->n_sect, (uint64_t)pSym->n_value, pSym->n_type, pSym->n_desc, pszSym);
593#endif
594
595 if (strcmp(pszSym, pszPrev) < 0)
596 RETURN_VERR_BAD_EXE_FORMAT; /* not sorted */
597
598 if (!(pSym->n_type & MACHO_N_STAB))
599 {
600 switch (pSym->n_type & MACHO_N_TYPE)
601 {
602 case MACHO_N_SECT:
603 if (pSym->n_sect == MACHO_NO_SECT)
604 RETURN_VERR_BAD_EXE_FORMAT;
605 if (pSym->n_sect > pThis->cSections)
606 RETURN_VERR_BAD_EXE_FORMAT;
607 if (pSym->n_desc & ~(REFERENCED_DYNAMICALLY))
608 RETURN_VERR_BAD_EXE_FORMAT;
609 if ( pSym->n_value < pThis->apSections[pSym->n_sect - 1]->addr
610 && strcmp(pszSym, "__mh_execute_header")) /* in 10.8 it's no longer absolute (PIE?). */
611 RETURN_VERR_BAD_EXE_FORMAT;
612 if ( pSym->n_value - pThis->apSections[pSym->n_sect - 1]->addr
613 > pThis->apSections[pSym->n_sect - 1]->size
614 && strcmp(pszSym, "__mh_execute_header")) /* see above. */
615 RETURN_VERR_BAD_EXE_FORMAT;
616 break;
617
618 case MACHO_N_ABS:
619 if ( pSym->n_sect != MACHO_NO_SECT
620 && ( strcmp(pszSym, "__mh_execute_header") /* n_sect=1 in 10.7/amd64 */
621 || pSym->n_sect > pThis->cSections) )
622 RETURN_VERR_BAD_EXE_FORMAT;
623 if (pSym->n_desc & ~(REFERENCED_DYNAMICALLY))
624 RETURN_VERR_BAD_EXE_FORMAT;
625 break;
626
627 case MACHO_N_UNDF:
628 /* No undefined or common symbols in the kernel. */
629 RETURN_VERR_BAD_EXE_FORMAT;
630
631 case MACHO_N_INDR:
632 /* No indirect symbols in the kernel. */
633 RETURN_VERR_BAD_EXE_FORMAT;
634
635 case MACHO_N_PBUD:
636 /* No prebound symbols in the kernel. */
637 RETURN_VERR_BAD_EXE_FORMAT;
638
639 default:
640 RETURN_VERR_BAD_EXE_FORMAT;
641 }
642 }
643 /* else: Ignore debug symbols. */
644 }
645
646 return VINF_SUCCESS;
647}
648
649
650/**
651 * Loads the load commands and validates them.
652 *
653 * @returns IPRT status code.
654 * @param pThis The internal scratch data.
655 */
656static int rtR0DbgKrnlDarwinLoadCommands(RTDBGKRNLINFOINT *pThis)
657{
658 pThis->offStrTab = 0;
659 pThis->cbStrTab = 0;
660 pThis->offSyms = 0;
661 pThis->cSyms = 0;
662 pThis->cSections = 0;
663
664 pThis->pLoadCmds = (load_command_t *)RTMemAlloc(pThis->cbLoadCmds);
665 if (!pThis->pLoadCmds)
666 return VERR_NO_MEMORY;
667
668 int rc = RTFileReadAt(pThis->hFile, pThis->offArch + sizeof(MY_MACHO_HEADER),
669 pThis->pLoadCmds, pThis->cbLoadCmds, NULL);
670 if (RT_FAILURE(rc))
671 return rc;
672
673 /*
674 * Validate the relevant commands, picking up sections and the symbol
675 * table location.
676 */
677 load_command_t const *pCmd = pThis->pLoadCmds;
678 for (uint32_t iCmd = 0; ; iCmd++)
679 {
680 /* cmd index & offset. */
681 uintptr_t offCmd = (uintptr_t)pCmd - (uintptr_t)pThis->pLoadCmds;
682 if (offCmd == pThis->cbLoadCmds && iCmd == pThis->cLoadCmds)
683 break;
684 if (offCmd + sizeof(*pCmd) > pThis->cbLoadCmds)
685 RETURN_VERR_BAD_EXE_FORMAT;
686 if (iCmd >= pThis->cLoadCmds)
687 RETURN_VERR_BAD_EXE_FORMAT;
688
689 /* cmdsize */
690 if (pCmd->cmdsize < sizeof(*pCmd))
691 RETURN_VERR_BAD_EXE_FORMAT;
692 if (pCmd->cmdsize > pThis->cbLoadCmds)
693 RETURN_VERR_BAD_EXE_FORMAT;
694 if (RT_ALIGN_32(pCmd->cmdsize, 4) != pCmd->cmdsize)
695 RETURN_VERR_BAD_EXE_FORMAT;
696
697 /* cmd */
698 switch (pCmd->cmd & ~LC_REQ_DYLD)
699 {
700 /* Validate and store the symbol table details. */
701 case LC_SYMTAB:
702 {
703 struct symtab_command const *pSymTab = (struct symtab_command const *)pCmd;
704 if (pSymTab->cmdsize != sizeof(*pSymTab))
705 RETURN_VERR_BAD_EXE_FORMAT;
706 if (pSymTab->nsyms > _1M)
707 RETURN_VERR_BAD_EXE_FORMAT;
708 if (pSymTab->strsize > _2M)
709 RETURN_VERR_BAD_EXE_FORMAT;
710
711 pThis->offStrTab = pSymTab->stroff;
712 pThis->cbStrTab = pSymTab->strsize;
713 pThis->offSyms = pSymTab->symoff;
714 pThis->cSyms = pSymTab->nsyms;
715 break;
716 }
717
718 /* Validate the segment. */
719#if ARCH_BITS == 32
720 case LC_SEGMENT_32:
721#elif ARCH_BITS == 64
722 case LC_SEGMENT_64:
723#else
724# error ARCH_BITS
725#endif
726 {
727 MY_SEGMENT_COMMAND const *pSeg = (MY_SEGMENT_COMMAND const *)pCmd;
728 if (pSeg->cmdsize < sizeof(*pSeg))
729 RETURN_VERR_BAD_EXE_FORMAT;
730
731 if (pSeg->segname[0] == '\0')
732 RETURN_VERR_BAD_EXE_FORMAT;
733
734 if (pSeg->nsects > MACHO_MAX_SECT)
735 RETURN_VERR_BAD_EXE_FORMAT;
736 if (pSeg->nsects * sizeof(MY_SECTION) + sizeof(*pSeg) != pSeg->cmdsize)
737 RETURN_VERR_BAD_EXE_FORMAT;
738
739 if (pSeg->flags & ~(SG_HIGHVM | SG_FVMLIB | SG_NORELOC | SG_PROTECTED_VERSION_1))
740 RETURN_VERR_BAD_EXE_FORMAT;
741
742 if (pSeg->vmaddr != 0)
743 {
744 if (pSeg->vmaddr + RT_ALIGN_Z(pSeg->vmsize, RT_BIT_32(12)) < pSeg->vmaddr)
745 RETURN_VERR_BAD_EXE_FORMAT;
746 }
747 else if (pSeg->vmsize)
748 RETURN_VERR_BAD_EXE_FORMAT;
749
750 if (pSeg->maxprot & ~VM_PROT_ALL)
751 RETURN_VERR_BAD_EXE_FORMAT;
752 if (pSeg->initprot & ~VM_PROT_ALL)
753 RETURN_VERR_BAD_EXE_FORMAT;
754
755 /* Validate the sections. */
756 uint32_t uAlignment = 0;
757 uintptr_t uAddr = pSeg->vmaddr;
758 MY_SECTION const *paSects = (MY_SECTION const *)(pSeg + 1);
759 for (uint32_t i = 0; i < pSeg->nsects; i++)
760 {
761 if (paSects[i].sectname[0] == '\0')
762 RETURN_VERR_BAD_EXE_FORMAT;
763 if (memcmp(paSects[i].segname, pSeg->segname, sizeof(pSeg->segname)))
764 RETURN_VERR_BAD_EXE_FORMAT;
765
766 switch (paSects[i].flags & SECTION_TYPE)
767 {
768 case S_REGULAR:
769 case S_CSTRING_LITERALS:
770 case S_NON_LAZY_SYMBOL_POINTERS:
771 case S_MOD_INIT_FUNC_POINTERS:
772 case S_MOD_TERM_FUNC_POINTERS:
773 case S_COALESCED:
774 if ( pSeg->filesize != 0
775 ? paSects[i].offset - pSeg->fileoff >= pSeg->filesize
776 : paSects[i].offset - pSeg->fileoff != pSeg->filesize)
777 RETURN_VERR_BAD_EXE_FORMAT;
778 if ( paSects[i].addr != 0
779 && paSects[i].offset - pSeg->fileoff != paSects[i].addr - pSeg->vmaddr)
780 RETURN_VERR_BAD_EXE_FORMAT;
781 break;
782
783 case S_ZEROFILL:
784 if (paSects[i].offset != 0)
785 RETURN_VERR_BAD_EXE_FORMAT;
786 break;
787
788 /* not observed */
789 case S_SYMBOL_STUBS:
790 case S_INTERPOSING:
791 case S_4BYTE_LITERALS:
792 case S_8BYTE_LITERALS:
793 case S_16BYTE_LITERALS:
794 case S_DTRACE_DOF:
795 case S_LAZY_SYMBOL_POINTERS:
796 case S_LAZY_DYLIB_SYMBOL_POINTERS:
797 RETURN_VERR_LDR_UNEXPECTED;
798 case S_GB_ZEROFILL:
799 RETURN_VERR_LDR_UNEXPECTED;
800 default:
801 RETURN_VERR_BAD_EXE_FORMAT;
802 }
803
804 if (paSects[i].align > 12)
805 RETURN_VERR_BAD_EXE_FORMAT;
806 if (paSects[i].align > uAlignment)
807 uAlignment = paSects[i].align;
808
809 /* Add to the section table. */
810 if (pThis->cSections == MACHO_MAX_SECT)
811 RETURN_VERR_BAD_EXE_FORMAT;
812 pThis->apSections[pThis->cSections++] = &paSects[i];
813 }
814
815 if (RT_ALIGN_Z(pSeg->vmaddr, RT_BIT_32(uAlignment)) != pSeg->vmaddr)
816 RETURN_VERR_BAD_EXE_FORMAT;
817 if ( pSeg->filesize > RT_ALIGN_Z(pSeg->vmsize, RT_BIT_32(uAlignment))
818 && pSeg->vmsize != 0)
819 RETURN_VERR_BAD_EXE_FORMAT;
820 break;
821 }
822
823 case LC_UUID:
824 if (pCmd->cmdsize != sizeof(uuid_command))
825 RETURN_VERR_BAD_EXE_FORMAT;
826 break;
827
828 case LC_DYSYMTAB:
829 case LC_UNIXTHREAD:
830 case LC_CODE_SIGNATURE:
831 case LC_VERSION_MIN_MACOSX:
832 case LC_FUNCTION_STARTS:
833 case LC_MAIN:
834 case LC_DATA_IN_CODE:
835 case LC_SOURCE_VERSION:
836 break;
837
838 /* not observed */
839 case LC_SYMSEG:
840#if ARCH_BITS == 32
841 case LC_SEGMENT_64:
842#elif ARCH_BITS == 64
843 case LC_SEGMENT_32:
844#endif
845 case LC_ROUTINES_64:
846 case LC_ROUTINES:
847 case LC_THREAD:
848 case LC_LOADFVMLIB:
849 case LC_IDFVMLIB:
850 case LC_IDENT:
851 case LC_FVMFILE:
852 case LC_PREPAGE:
853 case LC_TWOLEVEL_HINTS:
854 case LC_PREBIND_CKSUM:
855 case LC_SEGMENT_SPLIT_INFO:
856 case LC_ENCRYPTION_INFO:
857 RETURN_VERR_LDR_UNEXPECTED;
858
859 /* no phones here yet */
860 case LC_VERSION_MIN_IPHONEOS:
861 RETURN_VERR_LDR_UNEXPECTED;
862
863 /* dylib */
864 case LC_LOAD_DYLIB:
865 case LC_ID_DYLIB:
866 case LC_LOAD_DYLINKER:
867 case LC_ID_DYLINKER:
868 case LC_PREBOUND_DYLIB:
869 case LC_LOAD_WEAK_DYLIB & ~LC_REQ_DYLD:
870 case LC_SUB_FRAMEWORK:
871 case LC_SUB_UMBRELLA:
872 case LC_SUB_CLIENT:
873 case LC_SUB_LIBRARY:
874 case LC_RPATH:
875 case LC_REEXPORT_DYLIB:
876 case LC_LAZY_LOAD_DYLIB:
877 case LC_DYLD_INFO:
878 case LC_DYLD_INFO_ONLY:
879 case LC_LOAD_UPWARD_DYLIB:
880 case LC_DYLD_ENVIRONMENT:
881 case LC_DYLIB_CODE_SIGN_DRS:
882 RETURN_VERR_LDR_UNEXPECTED;
883
884 default:
885 RETURN_VERR_BAD_EXE_FORMAT;
886 }
887
888 /* next */
889 pCmd = (load_command_t *)((uintptr_t)pCmd + pCmd->cmdsize);
890 }
891
892 return VINF_SUCCESS;
893}
894
895
896/**
897 * Loads the FAT and MACHO headers, noting down the relevant info.
898 *
899 * @returns IPRT status code.
900 * @param pThis The internal scratch data.
901 */
902static int rtR0DbgKrnlDarwinLoadFileHeaders(RTDBGKRNLINFOINT *pThis)
903{
904 uint32_t i;
905
906 pThis->offArch = 0;
907 pThis->cbArch = 0;
908
909 /*
910 * Read the first bit of the file, parse the FAT if found there.
911 */
912 int rc = RTFileReadAt(pThis->hFile, 0, pThis->abBuf, sizeof(fat_header_t) + sizeof(fat_arch_t) * 16, NULL);
913 if (RT_FAILURE(rc))
914 return rc;
915
916 fat_header_t *pFat = (fat_header *)pThis->abBuf;
917 fat_arch_t *paFatArches = (fat_arch_t *)(pFat + 1);
918
919 /* Correct FAT endian first. */
920 if (pFat->magic == IMAGE_FAT_SIGNATURE_OE)
921 {
922 pFat->magic = RT_BSWAP_U32(pFat->magic);
923 pFat->nfat_arch = RT_BSWAP_U32(pFat->nfat_arch);
924 i = RT_MIN(pFat->nfat_arch, 16);
925 while (i-- > 0)
926 {
927 paFatArches[i].cputype = RT_BSWAP_U32(paFatArches[i].cputype);
928 paFatArches[i].cpusubtype = RT_BSWAP_U32(paFatArches[i].cpusubtype);
929 paFatArches[i].offset = RT_BSWAP_U32(paFatArches[i].offset);
930 paFatArches[i].size = RT_BSWAP_U32(paFatArches[i].size);
931 paFatArches[i].align = RT_BSWAP_U32(paFatArches[i].align);
932 }
933 }
934
935 /* Lookup our architecture in the FAT. */
936 if (pFat->magic == IMAGE_FAT_SIGNATURE)
937 {
938 if (pFat->nfat_arch > 16)
939 RETURN_VERR_BAD_EXE_FORMAT;
940
941 for (i = 0; i < pFat->nfat_arch; i++)
942 {
943 if ( paFatArches[i].cputype == MY_CPU_TYPE
944 && paFatArches[i].cpusubtype == MY_CPU_SUBTYPE_ALL)
945 {
946 pThis->offArch = paFatArches[i].offset;
947 pThis->cbArch = paFatArches[i].size;
948 if (!pThis->cbArch)
949 RETURN_VERR_BAD_EXE_FORMAT;
950 if (pThis->offArch < sizeof(fat_header_t) + sizeof(fat_arch_t) * pFat->nfat_arch)
951 RETURN_VERR_BAD_EXE_FORMAT;
952 if (pThis->offArch + pThis->cbArch <= pThis->offArch)
953 RETURN_VERR_LDR_ARCH_MISMATCH;
954 break;
955 }
956 }
957 if (i >= pFat->nfat_arch)
958 RETURN_VERR_LDR_ARCH_MISMATCH;
959 }
960
961 /*
962 * Read the Mach-O header and validate it.
963 */
964 rc = RTFileReadAt(pThis->hFile, pThis->offArch, pThis->abBuf, sizeof(MY_MACHO_HEADER), NULL);
965 if (RT_FAILURE(rc))
966 return rc;
967 MY_MACHO_HEADER const *pHdr = (MY_MACHO_HEADER const *)pThis->abBuf;
968 if (pHdr->magic != MY_MACHO_MAGIC)
969 {
970 if ( pHdr->magic == IMAGE_MACHO32_SIGNATURE
971 || pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
972 || pHdr->magic == IMAGE_MACHO64_SIGNATURE
973 || pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE)
974 RETURN_VERR_LDR_ARCH_MISMATCH;
975 RETURN_VERR_BAD_EXE_FORMAT;
976 }
977
978 if (pHdr->cputype != MY_CPU_TYPE)
979 RETURN_VERR_LDR_ARCH_MISMATCH;
980 if (pHdr->cpusubtype != MY_CPU_SUBTYPE_ALL)
981 RETURN_VERR_LDR_ARCH_MISMATCH;
982 if (pHdr->filetype != MH_EXECUTE)
983 RETURN_VERR_LDR_UNEXPECTED;
984 if (pHdr->ncmds < 4)
985 RETURN_VERR_LDR_UNEXPECTED;
986 if (pHdr->ncmds > 256)
987 RETURN_VERR_LDR_UNEXPECTED;
988 if (pHdr->sizeofcmds <= pHdr->ncmds * sizeof(load_command_t))
989 RETURN_VERR_LDR_UNEXPECTED;
990 if (pHdr->sizeofcmds >= _1M)
991 RETURN_VERR_LDR_UNEXPECTED;
992 if (pHdr->flags & ~MH_VALID_FLAGS)
993 RETURN_VERR_LDR_UNEXPECTED;
994
995 pThis->cLoadCmds = pHdr->ncmds;
996 pThis->cbLoadCmds = pHdr->sizeofcmds;
997 return VINF_SUCCESS;
998}
999
1000
1001/**
1002 * Destructor.
1003 *
1004 * @param pThis The instance to destroy.
1005 */
1006static void rtR0DbgKrnlDarwinDtor(RTDBGKRNLINFOINT *pThis)
1007{
1008 pThis->u32Magic = ~RTDBGKRNLINFO_MAGIC;
1009
1010 RTMemFree(pThis->pachStrTab);
1011 pThis->pachStrTab = NULL;
1012
1013 RTMemFree(pThis->paSyms);
1014 pThis->paSyms = NULL;
1015
1016 RTMemFree(pThis);
1017}
1018
1019
1020RTR0DECL(int) RTR0DbgKrnlInfoOpen(PRTDBGKRNLINFO phKrnlInfo, uint32_t fFlags)
1021{
1022 AssertPtrReturn(phKrnlInfo, VERR_INVALID_POINTER);
1023 *phKrnlInfo = NIL_RTDBGKRNLINFO;
1024 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
1025
1026 RTDBGKRNLINFOINT *pThis = (RTDBGKRNLINFOINT *)RTMemAllocZ(sizeof(*pThis));
1027 if (!pThis)
1028 return VERR_NO_MEMORY;
1029 pThis->hFile = NIL_RTFILE;
1030
1031 int rc = RTFileOpen(&pThis->hFile, "/mach_kernel", RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1032 if (RT_SUCCESS(rc))
1033 rc = rtR0DbgKrnlDarwinLoadFileHeaders(pThis);
1034 if (RT_SUCCESS(rc))
1035 rc = rtR0DbgKrnlDarwinLoadCommands(pThis);
1036 if (RT_SUCCESS(rc))
1037 rc = rtR0DbgKrnlDarwinLoadSymTab(pThis);
1038 if (RT_SUCCESS(rc))
1039 rc = rtR0DbgKrnlDarwinCheckStandardSymbols(pThis);
1040
1041 rtR0DbgKrnlDarwinLoadDone(pThis);
1042 if (RT_SUCCESS(rc))
1043 {
1044 pThis->u32Magic = RTDBGKRNLINFO_MAGIC;
1045 pThis->cRefs = 1;
1046 *phKrnlInfo = pThis;
1047 }
1048 else
1049 rtR0DbgKrnlDarwinDtor(pThis);
1050 return rc;
1051}
1052
1053
1054RTR0DECL(uint32_t) RTR0DbgKrnlInfoRetain(RTDBGKRNLINFO hKrnlInfo)
1055{
1056 RTDBGKRNLINFOINT *pThis = hKrnlInfo;
1057 AssertPtrReturn(pThis, UINT32_MAX);
1058 AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX);
1059
1060 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
1061 Assert(cRefs && cRefs < 100000);
1062 return cRefs;
1063}
1064
1065
1066RTR0DECL(uint32_t) RTR0DbgKrnlInfoRelease(RTDBGKRNLINFO hKrnlInfo)
1067{
1068 RTDBGKRNLINFOINT *pThis = hKrnlInfo;
1069 if (pThis == NIL_RTDBGKRNLINFO)
1070 return 0;
1071 AssertPtrReturn(pThis, UINT32_MAX);
1072 AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX);
1073
1074 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
1075 if (cRefs == 0)
1076 rtR0DbgKrnlDarwinDtor(pThis);
1077 return cRefs;
1078}
1079
1080
1081RTR0DECL(int) RTR0DbgKrnlInfoQueryMember(RTDBGKRNLINFO hKrnlInfo, const char *pszStructure,
1082 const char *pszMember, size_t *poffMember)
1083{
1084 RTDBGKRNLINFOINT *pThis = hKrnlInfo;
1085 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1086 AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
1087 AssertPtrReturn(pszMember, VERR_INVALID_PARAMETER);
1088 AssertPtrReturn(pszStructure, VERR_INVALID_PARAMETER);
1089 AssertPtrReturn(poffMember, VERR_INVALID_PARAMETER);
1090 return VERR_NOT_FOUND;
1091}
1092
1093
1094RTR0DECL(int) RTR0DbgKrnlInfoQuerySymbol(RTDBGKRNLINFO hKrnlInfo, const char *pszModule,
1095 const char *pszSymbol, void **ppvSymbol)
1096{
1097 RTDBGKRNLINFOINT *pThis = hKrnlInfo;
1098 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1099 AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
1100 AssertPtrReturn(pszSymbol, VERR_INVALID_PARAMETER);
1101 AssertPtrNullReturn(ppvSymbol, VERR_INVALID_PARAMETER);
1102 AssertReturn(!pszModule, VERR_MODULE_NOT_FOUND);
1103
1104 uintptr_t uValue = rtR0DbgKrnlDarwinLookup(pThis, pszSymbol);
1105 if (ppvSymbol)
1106 *ppvSymbol = (void *)uValue;
1107 if (uValue)
1108 return VINF_SUCCESS;
1109 return VERR_SYMBOL_NOT_FOUND;
1110}
1111
Note: See TracBrowser for help on using the repository browser.

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