VirtualBox

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

Last change on this file since 78327 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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