VirtualBox

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

Last change on this file since 84330 was 83845, checked in by vboxsync, 5 years ago

IPRT/dbgkrnlinfo-r0drv-darwin.cpp: VC++ 14.1 warnings. bugref:8489

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.4 KB
Line 
1/* $Id: dbgkrnlinfo-r0drv-darwin.cpp 83845 2020-04-20 09:39:33Z vboxsync $ */
2/** @file
3 * IPRT - Kernel Debug Information, R0 Driver, Darwin.
4 */
5
6/*
7 * Copyright (C) 2011-2020 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#if defined(DEBUG_bird) && !defined(IN_RING3)
123# define LOG_MISMATCH(...) kprintf(__VA_ARGS__)
124# define LOG_NOT_PRESENT(...) kprintf(__VA_ARGS__)
125# define LOG_BAD_SYM(...) kprintf(__VA_ARGS__)
126# define LOG_SUCCESS(...) kprintf(__VA_ARGS__)
127#else
128# define LOG_MISMATCH(...) Log((__VA_ARGS__))
129# define LOG_NOT_PRESENT(...) Log((__VA_ARGS__))
130# define LOG_BAD_SYM(...) printf(__VA_ARGS__)
131# define LOG_SUCCESS(...) printf(__VA_ARGS__)
132#endif
133/** @} */
134
135#define VERR_LDR_UNEXPECTED (-641)
136
137#ifndef RT_OS_DARWIN
138# define MAC_OS_X_VERSION_MIN_REQUIRED 1050
139#endif
140
141
142/*********************************************************************************************************************************
143* Structures and Typedefs *
144*********************************************************************************************************************************/
145/**
146 * Our internal representation of the mach_kernel after loading it's symbols
147 * and successfully resolving their addresses.
148 */
149typedef struct RTDBGKRNLINFOINT
150{
151 /** Magic value (RTDBGKRNLINFO_MAGIC). */
152 uint32_t u32Magic;
153 /** Reference counter. */
154 uint32_t volatile cRefs;
155
156 /** Set if this is an in-memory rather than on-disk instance. */
157 bool fIsInMem;
158 bool afAlignment[7];
159
160 /** @name Result.
161 * @{ */
162 /** Pointer to the string table. */
163 char *pachStrTab;
164 /** The size of the string table. */
165 uint32_t cbStrTab;
166 /** The file offset of the string table. */
167 uint32_t offStrTab;
168 /** The link address of the string table. */
169 uintptr_t uStrTabLinkAddr;
170 /** Pointer to the symbol table. */
171 MY_NLIST *paSyms;
172 /** The size of the symbol table. */
173 uint32_t cSyms;
174 /** The file offset of the symbol table. */
175 uint32_t offSyms;
176 /** The link address of the symbol table. */
177 uintptr_t uSymTabLinkAddr;
178 /** The link address of the text segment. */
179 uintptr_t uTextSegLinkAddr;
180 /** Size of the text segment. */
181 uintptr_t cbTextSeg;
182 /** Offset between link address and actual load address. */
183 uintptr_t offLoad;
184 /** The minimum OS version (A.B.C; A is 16 bits, B & C each 8 bits). */
185 uint32_t uMinOsVer;
186 /** The SDK version (A.B.C; A is 16 bits, B & C each 8 bits). */
187 uint32_t uSdkVer;
188 /** The source version (A.B.C.D.E; A is 24 bits, the rest 10 each). */
189 uint64_t uSrcVer;
190 /** @} */
191
192 /** @name Used during loading.
193 * @{ */
194 /** The file handle. */
195 RTFILE hFile;
196 /** The architecture image offset (fat_arch_t::offset). */
197 uint64_t offArch;
198 /** The architecture image size (fat_arch_t::size). */
199 uint32_t cbArch;
200 /** The number of load commands (mach_header_XX_t::ncmds). */
201 uint32_t cLoadCmds;
202 /** The size of the load commands. */
203 uint32_t cbLoadCmds;
204 /** The load commands. */
205 load_command_t *pLoadCmds;
206 /** The number of segments. */
207 uint32_t cSegments;
208 /** The number of sections. */
209 uint32_t cSections;
210 /** Section pointer table (points into the load commands). */
211 MY_SEGMENT_COMMAND const *apSegments[MACHO_MAX_SECT / 2];
212 /** Section pointer table (points into the load commands). */
213 MY_SECTION const *apSections[MACHO_MAX_SECT];
214 /** @} */
215
216 /** Buffer space. */
217 char abBuf[_4K];
218} RTDBGKRNLINFOINT;
219
220
221/*********************************************************************************************************************************
222* Structures and Typedefs *
223*********************************************************************************************************************************/
224#ifdef DEBUG
225static bool g_fBreakpointOnError = false;
226#endif
227
228
229/**
230 * Close and free up resources we no longer needs.
231 *
232 * @param pThis The internal scratch data.
233 */
234static void rtR0DbgKrnlDarwinLoadDone(RTDBGKRNLINFOINT *pThis)
235{
236 if (!pThis->fIsInMem)
237 RTFileClose(pThis->hFile);
238 pThis->hFile = NIL_RTFILE;
239
240 if (!pThis->fIsInMem)
241 RTMemFree(pThis->pLoadCmds);
242 pThis->pLoadCmds = NULL;
243 RT_ZERO(pThis->apSections);
244 RT_ZERO(pThis->apSegments);
245}
246
247
248/**
249 * Looks up a kernel symbol.
250 *
251 * @returns The symbol address on success, 0 on failure.
252 * @param pThis The internal scratch data.
253 * @param pszSymbol The symbol to resolve. Automatically prefixed
254 * with an underscore.
255 */
256static uintptr_t rtR0DbgKrnlDarwinLookup(RTDBGKRNLINFOINT *pThis, const char *pszSymbol)
257{
258 uint32_t const cSyms = pThis->cSyms;
259 MY_NLIST const *pSym = pThis->paSyms;
260
261#if 1
262 /* linear search. */
263 for (uint32_t iSym = 0; iSym < cSyms; iSym++, pSym++)
264 {
265 if (pSym->n_type & MACHO_N_STAB)
266 continue;
267
268 const char *pszTabName= &pThis->pachStrTab[(uint32_t)pSym->n_un.n_strx];
269 if ( *pszTabName == '_'
270 && strcmp(pszTabName + 1, pszSymbol) == 0)
271 return pSym->n_value + pThis->offLoad;
272 }
273#else
274 /** @todo binary search. */
275
276#endif
277 return 0;
278}
279
280
281/* Rainy day: Find the right headers for these symbols ... if there are any. */
282extern "C" void ev_try_lock(void);
283extern "C" void OSMalloc(void);
284extern "C" void OSlibkernInit(void);
285extern "C" void kdp_set_interface(void);
286
287
288/**
289 * Check the symbol table against symbols we known symbols.
290 *
291 * This is done to detect whether the on disk image and the in
292 * memory images matches. Mismatches could stem from user
293 * replacing the default kernel image on disk.
294 *
295 * @returns IPRT status code.
296 * @param pThis The internal scratch data.
297 * @param pszKernelFile The name of the kernel file.
298 */
299static int rtR0DbgKrnlDarwinCheckStandardSymbols(RTDBGKRNLINFOINT *pThis, const char *pszKernelFile)
300{
301 static struct
302 {
303 const char *pszName;
304 uintptr_t uAddr;
305 } const s_aStandardCandles[] =
306 {
307#ifdef IN_RING0
308# define KNOWN_ENTRY(a_Sym) { #a_Sym, (uintptr_t)&a_Sym }
309#else
310# define KNOWN_ENTRY(a_Sym) { #a_Sym, 0 }
311#endif
312 /* IOKit: */
313 KNOWN_ENTRY(IOMalloc),
314 KNOWN_ENTRY(IOFree),
315 KNOWN_ENTRY(IOSleep),
316 KNOWN_ENTRY(IORWLockAlloc),
317 KNOWN_ENTRY(IORecursiveLockLock),
318 KNOWN_ENTRY(IOSimpleLockAlloc),
319 KNOWN_ENTRY(PE_cpu_halt),
320 KNOWN_ENTRY(gIOKitDebug),
321 KNOWN_ENTRY(gIOServicePlane),
322 KNOWN_ENTRY(ev_try_lock),
323
324 /* Libkern: */
325 KNOWN_ENTRY(OSAddAtomic),
326 KNOWN_ENTRY(OSBitAndAtomic),
327 KNOWN_ENTRY(OSBitOrAtomic),
328 KNOWN_ENTRY(OSBitXorAtomic),
329 KNOWN_ENTRY(OSCompareAndSwap),
330 KNOWN_ENTRY(OSMalloc),
331 KNOWN_ENTRY(OSlibkernInit),
332 KNOWN_ENTRY(bcmp),
333 KNOWN_ENTRY(copyout),
334 KNOWN_ENTRY(copyin),
335 KNOWN_ENTRY(kprintf),
336 KNOWN_ENTRY(printf),
337 KNOWN_ENTRY(lck_grp_alloc_init),
338 KNOWN_ENTRY(lck_mtx_alloc_init),
339 KNOWN_ENTRY(lck_rw_alloc_init),
340 KNOWN_ENTRY(lck_spin_alloc_init),
341 KNOWN_ENTRY(osrelease),
342 KNOWN_ENTRY(ostype),
343 KNOWN_ENTRY(panic),
344 KNOWN_ENTRY(strprefix),
345 //KNOWN_ENTRY(sysctlbyname), - we get kernel_sysctlbyname from the 10.10+ kernels.
346 KNOWN_ENTRY(vsscanf),
347 KNOWN_ENTRY(page_mask),
348
349 /* Mach: */
350 KNOWN_ENTRY(absolutetime_to_nanoseconds),
351 KNOWN_ENTRY(assert_wait),
352 KNOWN_ENTRY(clock_delay_until),
353 KNOWN_ENTRY(clock_get_uptime),
354 KNOWN_ENTRY(current_task),
355 KNOWN_ENTRY(current_thread),
356 KNOWN_ENTRY(kernel_task),
357 KNOWN_ENTRY(lck_mtx_sleep),
358 KNOWN_ENTRY(lck_rw_sleep),
359 KNOWN_ENTRY(lck_spin_sleep),
360 KNOWN_ENTRY(mach_absolute_time),
361 KNOWN_ENTRY(semaphore_create),
362 KNOWN_ENTRY(task_reference),
363 KNOWN_ENTRY(thread_block),
364 KNOWN_ENTRY(thread_reference),
365 KNOWN_ENTRY(thread_terminate),
366 KNOWN_ENTRY(thread_wakeup_prim),
367
368 /* BSDKernel: */
369 KNOWN_ENTRY(buf_size),
370 KNOWN_ENTRY(copystr),
371 KNOWN_ENTRY(current_proc),
372 KNOWN_ENTRY(ifnet_hdrlen),
373 KNOWN_ENTRY(ifnet_set_promiscuous),
374 KNOWN_ENTRY(kauth_getuid),
375#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
376 KNOWN_ENTRY(kauth_cred_unref),
377#else
378 KNOWN_ENTRY(kauth_cred_rele),
379#endif
380 KNOWN_ENTRY(mbuf_data),
381 KNOWN_ENTRY(msleep),
382 KNOWN_ENTRY(nanotime),
383 KNOWN_ENTRY(nop_close),
384 KNOWN_ENTRY(proc_pid),
385 KNOWN_ENTRY(sock_accept),
386 KNOWN_ENTRY(sockopt_name),
387 //KNOWN_ENTRY(spec_write),
388 KNOWN_ENTRY(suword),
389 //KNOWN_ENTRY(sysctl_int),
390 KNOWN_ENTRY(uio_rw),
391 KNOWN_ENTRY(vfs_flags),
392 KNOWN_ENTRY(vfs_name),
393 KNOWN_ENTRY(vfs_statfs),
394 KNOWN_ENTRY(VNOP_READ),
395 KNOWN_ENTRY(uio_create),
396 KNOWN_ENTRY(uio_addiov),
397 KNOWN_ENTRY(uio_free),
398 KNOWN_ENTRY(vnode_get),
399 KNOWN_ENTRY(vnode_open),
400 KNOWN_ENTRY(vnode_ref),
401 KNOWN_ENTRY(vnode_rele),
402 KNOWN_ENTRY(vnop_close_desc),
403 KNOWN_ENTRY(wakeup),
404 KNOWN_ENTRY(wakeup_one),
405
406 /* Unsupported: */
407 KNOWN_ENTRY(kdp_set_interface),
408 KNOWN_ENTRY(pmap_find_phys),
409 KNOWN_ENTRY(vm_map),
410 KNOWN_ENTRY(vm_protect),
411 KNOWN_ENTRY(vm_region),
412 KNOWN_ENTRY(vm_map_unwire), /* vm_map_wire has an alternative symbol, vm_map_wire_external, in 10.11 */
413 KNOWN_ENTRY(PE_kputc),
414 KNOWN_ENTRY(kernel_map),
415 KNOWN_ENTRY(kernel_pmap),
416 };
417
418 for (unsigned i = 0; i < RT_ELEMENTS(s_aStandardCandles); i++)
419 {
420 uintptr_t uAddr = rtR0DbgKrnlDarwinLookup(pThis, s_aStandardCandles[i].pszName);
421#ifdef IN_RING0
422 if (uAddr != s_aStandardCandles[i].uAddr)
423#else
424 if (uAddr == 0)
425#endif
426 {
427#if defined(IN_RING0) && defined(DEBUG_bird)
428 kprintf("RTR0DbgKrnlInfoOpen: error: %s (%p != %p) in %s\n",
429 s_aStandardCandles[i].pszName, (void *)uAddr, (void *)s_aStandardCandles[i].uAddr, pszKernelFile);
430#endif
431 printf("RTR0DbgKrnlInfoOpen: error: %s (%p != %p) in %s\n",
432 s_aStandardCandles[i].pszName, (void *)uAddr, (void *)s_aStandardCandles[i].uAddr, pszKernelFile);
433 return VERR_INTERNAL_ERROR_2;
434 }
435 }
436 return VINF_SUCCESS;
437}
438
439
440/**
441 * Loads and validates the symbol and string tables.
442 *
443 * @returns IPRT status code.
444 * @param pThis The internal scratch data.
445 * @param pszKernelFile The name of the kernel file.
446 */
447static int rtR0DbgKrnlDarwinParseSymTab(RTDBGKRNLINFOINT *pThis, const char *pszKernelFile)
448{
449 /*
450 * The first string table symbol must be a zero length name.
451 */
452 if (pThis->pachStrTab[0] != '\0')
453 RETURN_VERR_BAD_EXE_FORMAT;
454
455 /*
456 * Validate the symbol table.
457 */
458 const char *pszPrev = "";
459 uint32_t const cSyms = pThis->cSyms;
460 MY_NLIST const *pSym = pThis->paSyms;
461 for (uint32_t iSym = 0; iSym < cSyms; iSym++, pSym++)
462 {
463 if ((uint32_t)pSym->n_un.n_strx >= pThis->cbStrTab)
464 {
465 LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Symbol #%u has a bad string table index: %#x vs cbStrTab=%#x\n",
466 pszKernelFile, iSym, pSym->n_un.n_strx, pThis->cbStrTab);
467 RETURN_VERR_BAD_EXE_FORMAT;
468 }
469 const char *pszSym = &pThis->pachStrTab[(uint32_t)pSym->n_un.n_strx];
470#ifdef IN_RING3
471 RTAssertMsg2("%05i: %02x:%08llx %02x %04x %s\n", iSym, pSym->n_sect, (uint64_t)pSym->n_value, pSym->n_type, pSym->n_desc, pszSym);
472#endif
473
474 if (strcmp(pszSym, pszPrev) < 0)
475 RETURN_VERR_BAD_EXE_FORMAT; /* not sorted */
476
477 if (!(pSym->n_type & MACHO_N_STAB))
478 {
479 switch (pSym->n_type & MACHO_N_TYPE)
480 {
481 case MACHO_N_SECT:
482 if (pSym->n_sect == MACHO_NO_SECT)
483 {
484 LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Symbol #%u '%s' problem: n_sect = MACHO_NO_SECT\n",
485 pszKernelFile, iSym, pszSym);
486 RETURN_VERR_BAD_EXE_FORMAT;
487 }
488 if (pSym->n_sect > pThis->cSections)
489 {
490 LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Symbol #%u '%s' problem: n_sect (%u) is higher than cSections (%u)\n",
491 pszKernelFile, iSym, pszSym, pSym->n_sect, pThis->cSections);
492 RETURN_VERR_BAD_EXE_FORMAT;
493 }
494 if (pSym->n_desc & ~(REFERENCED_DYNAMICALLY | N_WEAK_DEF))
495 {
496 LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Symbol #%u '%s' problem: Unexpected value n_desc=%#x\n",
497 pszKernelFile, iSym, pszSym, pSym->n_desc);
498 RETURN_VERR_BAD_EXE_FORMAT;
499 }
500 if ( pSym->n_value < pThis->apSections[pSym->n_sect - 1]->addr
501 && strcmp(pszSym, "__mh_execute_header")) /* in 10.8 it's no longer absolute (PIE?). */
502 {
503 LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Symbol #%u '%s' problem: n_value (%#llx) < section addr (%#llx)\n",
504 pszKernelFile, iSym, pszSym, (uint64_t)pSym->n_value,
505 (uint64_t)pThis->apSections[pSym->n_sect - 1]->addr);
506 RETURN_VERR_BAD_EXE_FORMAT;
507 }
508 if ( pSym->n_value - pThis->apSections[pSym->n_sect - 1]->addr
509 > pThis->apSections[pSym->n_sect - 1]->size
510 && strcmp(pszSym, "__mh_execute_header")) /* see above. */
511 {
512 LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Symbol #%u '%s' problem: n_value (%#llx) >= end of section (%#llx + %#llx)\n",
513 pszKernelFile, iSym, pszSym, (uint64_t)pSym->n_value,
514 (uint64_t)pThis->apSections[pSym->n_sect - 1]->addr,
515 (uint64_t)pThis->apSections[pSym->n_sect - 1]->size);
516 RETURN_VERR_BAD_EXE_FORMAT;
517 }
518 break;
519
520 case MACHO_N_ABS:
521 if ( pSym->n_sect != MACHO_NO_SECT
522 && ( strcmp(pszSym, "__mh_execute_header") /* n_sect=1 in 10.7/amd64 */
523 || pSym->n_sect > pThis->cSections) )
524 {
525 LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Abs symbol #%u '%s' problem: n_sect (%u) is not MACHO_NO_SECT (cSections is %u)\n",
526 pszKernelFile, iSym, pszSym, pSym->n_sect, pThis->cSections);
527 RETURN_VERR_BAD_EXE_FORMAT;
528 }
529 if (pSym->n_desc & ~(REFERENCED_DYNAMICALLY | N_WEAK_DEF))
530 {
531 LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Abs symbol #%u '%s' problem: Unexpected value n_desc=%#x\n",
532 pszKernelFile, iSym, pszSym, pSym->n_desc);
533 RETURN_VERR_BAD_EXE_FORMAT;
534 }
535 break;
536
537 case MACHO_N_UNDF:
538 /* No undefined or common symbols in the kernel. */
539 LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Unexpected undefined symbol #%u '%s'\n", pszKernelFile, iSym, pszSym);
540 RETURN_VERR_BAD_EXE_FORMAT;
541
542 case MACHO_N_INDR:
543 /* No indirect symbols in the kernel. */
544 LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Unexpected indirect symbol #%u '%s'\n", pszKernelFile, iSym, pszSym);
545 RETURN_VERR_BAD_EXE_FORMAT;
546
547 case MACHO_N_PBUD:
548 /* No prebound symbols in the kernel. */
549 LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Unexpected prebound symbol #%u '%s'\n", pszKernelFile, iSym, pszSym);
550 RETURN_VERR_BAD_EXE_FORMAT;
551
552 default:
553 LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Unexpected symbol n_type %#x for symbol #%u '%s'\n",
554 pszKernelFile, pSym->n_type, iSym, pszSym);
555 RETURN_VERR_BAD_EXE_FORMAT;
556 }
557 }
558 /* else: Ignore debug symbols. */
559 }
560
561 return VINF_SUCCESS;
562}
563
564
565/**
566 * Uses the segment table to translate a file offset into a virtual memory
567 * address.
568 *
569 * @returns The virtual memory address on success, 0 if not found.
570 * @param pThis The instance.
571 * @param offFile The file offset to translate.
572 */
573static uintptr_t rtR0DbgKrnlDarwinFileOffToVirtAddr(RTDBGKRNLINFOINT *pThis, uint64_t offFile)
574{
575 uint32_t iSeg = pThis->cSegments;
576 while (iSeg-- > 0)
577 {
578 uint64_t offSeg = offFile - pThis->apSegments[iSeg]->fileoff;
579 if (offSeg < pThis->apSegments[iSeg]->vmsize)
580 return pThis->apSegments[iSeg]->vmaddr + (uintptr_t)offSeg;
581 }
582 return 0;
583}
584
585
586/**
587 * Parses and validates the load commands.
588 *
589 * @returns IPRT status code.
590 * @param pThis The internal scratch data.
591 */
592static int rtR0DbgKrnlDarwinParseCommands(RTDBGKRNLINFOINT *pThis)
593{
594 Assert(pThis->pLoadCmds);
595
596 /*
597 * Reset the state.
598 */
599 pThis->offStrTab = 0;
600 pThis->cbStrTab = 0;
601 pThis->offSyms = 0;
602 pThis->cSyms = 0;
603 pThis->cSections = 0;
604 pThis->uTextSegLinkAddr = 0;
605 pThis->cbTextSeg = 0;
606 pThis->uMinOsVer = 0;
607 pThis->uSdkVer = 0;
608 pThis->uSrcVer = 0;
609
610 /*
611 * Validate the relevant commands, picking up sections and the symbol
612 * table location.
613 */
614 load_command_t const *pCmd = pThis->pLoadCmds;
615 for (uint32_t iCmd = 0; ; iCmd++)
616 {
617 /* cmd index & offset. */
618 uintptr_t offCmd = (uintptr_t)pCmd - (uintptr_t)pThis->pLoadCmds;
619 if (offCmd == pThis->cbLoadCmds && iCmd == pThis->cLoadCmds)
620 break;
621 if (offCmd + sizeof(*pCmd) > pThis->cbLoadCmds)
622 RETURN_VERR_BAD_EXE_FORMAT;
623 if (iCmd >= pThis->cLoadCmds)
624 RETURN_VERR_BAD_EXE_FORMAT;
625
626 /* cmdsize */
627 if (pCmd->cmdsize < sizeof(*pCmd))
628 RETURN_VERR_BAD_EXE_FORMAT;
629 if (pCmd->cmdsize > pThis->cbLoadCmds)
630 RETURN_VERR_BAD_EXE_FORMAT;
631 if (RT_ALIGN_32(pCmd->cmdsize, 4) != pCmd->cmdsize)
632 RETURN_VERR_BAD_EXE_FORMAT;
633
634 /* cmd */
635 switch (pCmd->cmd & ~LC_REQ_DYLD)
636 {
637 /* Validate and store the symbol table details. */
638 case LC_SYMTAB:
639 {
640 struct symtab_command const *pSymTab = (struct symtab_command const *)pCmd;
641 if (pSymTab->cmdsize != sizeof(*pSymTab))
642 RETURN_VERR_BAD_EXE_FORMAT;
643 if (pSymTab->nsyms > _1M)
644 RETURN_VERR_BAD_EXE_FORMAT;
645 if (pSymTab->strsize > _2M)
646 RETURN_VERR_BAD_EXE_FORMAT;
647
648 pThis->offStrTab = pSymTab->stroff;
649 pThis->cbStrTab = pSymTab->strsize;
650 pThis->offSyms = pSymTab->symoff;
651 pThis->cSyms = pSymTab->nsyms;
652 break;
653 }
654
655 /* Validate the segment. */
656#if ARCH_BITS == 32
657 case LC_SEGMENT_32:
658#elif ARCH_BITS == 64
659 case LC_SEGMENT_64:
660#else
661# error ARCH_BITS
662#endif
663 {
664 MY_SEGMENT_COMMAND const *pSeg = (MY_SEGMENT_COMMAND const *)pCmd;
665 if (pSeg->cmdsize < sizeof(*pSeg))
666 RETURN_VERR_BAD_EXE_FORMAT;
667
668 if (pSeg->segname[0] == '\0')
669 RETURN_VERR_BAD_EXE_FORMAT;
670
671 if (pSeg->nsects > MACHO_MAX_SECT)
672 RETURN_VERR_BAD_EXE_FORMAT;
673 if (pSeg->nsects * sizeof(MY_SECTION) + sizeof(*pSeg) != pSeg->cmdsize)
674 RETURN_VERR_BAD_EXE_FORMAT;
675
676 if (pSeg->flags & ~(SG_HIGHVM | SG_FVMLIB | SG_NORELOC | SG_PROTECTED_VERSION_1))
677 RETURN_VERR_BAD_EXE_FORMAT;
678
679 if ( pSeg->vmaddr != 0
680 || !strcmp(pSeg->segname, "__PAGEZERO"))
681 {
682 if (pSeg->vmaddr + RT_ALIGN_Z(pSeg->vmsize, RT_BIT_32(12)) < pSeg->vmaddr)
683 RETURN_VERR_BAD_EXE_FORMAT;
684 }
685 else if (pSeg->vmsize)
686 RETURN_VERR_BAD_EXE_FORMAT;
687
688 if (pSeg->maxprot & ~VM_PROT_ALL)
689 RETURN_VERR_BAD_EXE_FORMAT;
690 if (pSeg->initprot & ~VM_PROT_ALL)
691 RETURN_VERR_BAD_EXE_FORMAT;
692
693 /* Validate the sections. */
694 uint32_t uAlignment = 0;
695 MY_SECTION const *paSects = (MY_SECTION const *)(pSeg + 1);
696 for (uint32_t i = 0; i < pSeg->nsects; i++)
697 {
698 if (paSects[i].sectname[0] == '\0')
699 RETURN_VERR_BAD_EXE_FORMAT;
700 if (memcmp(paSects[i].segname, pSeg->segname, sizeof(pSeg->segname)))
701 RETURN_VERR_BAD_EXE_FORMAT;
702
703 switch (paSects[i].flags & SECTION_TYPE)
704 {
705 case S_REGULAR:
706 case S_CSTRING_LITERALS:
707 case S_NON_LAZY_SYMBOL_POINTERS:
708 case S_MOD_INIT_FUNC_POINTERS:
709 case S_MOD_TERM_FUNC_POINTERS:
710 case S_COALESCED:
711 case S_4BYTE_LITERALS:
712 if ( pSeg->filesize != 0
713 ? paSects[i].offset - pSeg->fileoff >= pSeg->filesize
714 : paSects[i].offset - pSeg->fileoff != pSeg->filesize)
715 RETURN_VERR_BAD_EXE_FORMAT;
716 if ( paSects[i].addr != 0
717 && paSects[i].offset - pSeg->fileoff != paSects[i].addr - pSeg->vmaddr)
718 RETURN_VERR_BAD_EXE_FORMAT;
719 break;
720
721 case S_ZEROFILL:
722 if (paSects[i].offset != 0)
723 RETURN_VERR_BAD_EXE_FORMAT;
724 break;
725
726 /* not observed */
727 case S_SYMBOL_STUBS:
728 case S_INTERPOSING:
729 case S_8BYTE_LITERALS:
730 case S_16BYTE_LITERALS:
731 case S_DTRACE_DOF:
732 case S_LAZY_SYMBOL_POINTERS:
733 case S_LAZY_DYLIB_SYMBOL_POINTERS:
734 RETURN_VERR_LDR_UNEXPECTED;
735 case S_GB_ZEROFILL:
736 RETURN_VERR_LDR_UNEXPECTED;
737 default:
738 RETURN_VERR_BAD_EXE_FORMAT;
739 }
740
741 if (paSects[i].align > 12)
742 RETURN_VERR_BAD_EXE_FORMAT;
743 if (paSects[i].align > uAlignment)
744 uAlignment = paSects[i].align;
745
746 /* Add to the section table. */
747 if (pThis->cSections >= RT_ELEMENTS(pThis->apSections))
748 RETURN_VERR_BAD_EXE_FORMAT;
749 pThis->apSections[pThis->cSections++] = &paSects[i];
750 }
751
752 if (RT_ALIGN_Z(pSeg->vmaddr, RT_BIT_32(uAlignment)) != pSeg->vmaddr)
753 RETURN_VERR_BAD_EXE_FORMAT;
754 if ( pSeg->filesize > RT_ALIGN_Z(pSeg->vmsize, RT_BIT_32(uAlignment))
755 && pSeg->vmsize != 0)
756 RETURN_VERR_BAD_EXE_FORMAT;
757
758 /*
759 * Add to the segment table.
760 */
761 if (pThis->cSegments >= RT_ELEMENTS(pThis->apSegments))
762 RETURN_VERR_BAD_EXE_FORMAT;
763 pThis->apSegments[pThis->cSegments++] = pSeg;
764
765 /*
766 * Take down the text segment size and link address (for in-mem variant):
767 */
768 if (!strcmp(pSeg->segname, "__TEXT"))
769 {
770 if (pThis->cbTextSeg != 0)
771 RETURN_VERR_BAD_EXE_FORMAT;
772 pThis->uTextSegLinkAddr = pSeg->vmaddr;
773 pThis->cbTextSeg = pSeg->vmsize;
774 }
775 break;
776 }
777
778 case LC_UUID:
779 if (pCmd->cmdsize != sizeof(uuid_command))
780 RETURN_VERR_BAD_EXE_FORMAT;
781 break;
782
783 case LC_DYSYMTAB:
784 case LC_UNIXTHREAD:
785 case LC_CODE_SIGNATURE:
786 case LC_VERSION_MIN_MACOSX:
787 case LC_FUNCTION_STARTS:
788 case LC_MAIN:
789 case LC_DATA_IN_CODE:
790 case LC_ENCRYPTION_INFO_64:
791 case LC_LINKER_OPTION:
792 case LC_LINKER_OPTIMIZATION_HINT:
793 case LC_VERSION_MIN_TVOS:
794 case LC_VERSION_MIN_WATCHOS:
795 case LC_NOTE:
796 break;
797
798 case LC_BUILD_VERSION:
799 if (pCmd->cmdsize >= RT_UOFFSETOF(build_version_command_t, aTools))
800 {
801 build_version_command_t *pBldVerCmd = (build_version_command_t *)pCmd;
802 pThis->uMinOsVer = pBldVerCmd->minos;
803 pThis->uSdkVer = pBldVerCmd->sdk;
804 }
805 break;
806
807 case LC_SOURCE_VERSION:
808 if (pCmd->cmdsize == sizeof(source_version_command_t))
809 {
810 source_version_command_t *pSrcVerCmd = (source_version_command_t *)pCmd;
811 pThis->uSrcVer = pSrcVerCmd->version;
812 }
813 break;
814
815 /* not observed */
816 case LC_SYMSEG:
817#if ARCH_BITS == 32
818 case LC_SEGMENT_64:
819#elif ARCH_BITS == 64
820 case LC_SEGMENT_32:
821#endif
822 case LC_ROUTINES_64:
823 case LC_ROUTINES:
824 case LC_THREAD:
825 case LC_LOADFVMLIB:
826 case LC_IDFVMLIB:
827 case LC_IDENT:
828 case LC_FVMFILE:
829 case LC_PREPAGE:
830 case LC_TWOLEVEL_HINTS:
831 case LC_PREBIND_CKSUM:
832 case LC_SEGMENT_SPLIT_INFO:
833 case LC_ENCRYPTION_INFO:
834 RETURN_VERR_LDR_UNEXPECTED;
835
836 /* no phones here yet */
837 case LC_VERSION_MIN_IPHONEOS:
838 RETURN_VERR_LDR_UNEXPECTED;
839
840 /* dylib */
841 case LC_LOAD_DYLIB:
842 case LC_ID_DYLIB:
843 case LC_LOAD_DYLINKER:
844 case LC_ID_DYLINKER:
845 case LC_PREBOUND_DYLIB:
846 case LC_LOAD_WEAK_DYLIB & ~LC_REQ_DYLD:
847 case LC_SUB_FRAMEWORK:
848 case LC_SUB_UMBRELLA:
849 case LC_SUB_CLIENT:
850 case LC_SUB_LIBRARY:
851 case LC_RPATH:
852 case LC_REEXPORT_DYLIB:
853 case LC_LAZY_LOAD_DYLIB:
854 case LC_DYLD_INFO:
855 case LC_DYLD_INFO_ONLY:
856 case LC_LOAD_UPWARD_DYLIB:
857 case LC_DYLD_ENVIRONMENT:
858 case LC_DYLIB_CODE_SIGN_DRS:
859 RETURN_VERR_LDR_UNEXPECTED;
860
861 default:
862 RETURN_VERR_BAD_EXE_FORMAT;
863 }
864
865 /* next */
866 pCmd = (load_command_t *)((uintptr_t)pCmd + pCmd->cmdsize);
867 }
868
869 /*
870 * Try figure out the virtual addresses for the symbol and string tables.
871 */
872 if (pThis->cbStrTab > 0)
873 pThis->uStrTabLinkAddr = rtR0DbgKrnlDarwinFileOffToVirtAddr(pThis, pThis->offStrTab);
874 if (pThis->cSyms > 0)
875 pThis->uSymTabLinkAddr = rtR0DbgKrnlDarwinFileOffToVirtAddr(pThis, pThis->offSyms);
876
877 return VINF_SUCCESS;
878}
879
880
881/**
882 * Loads and validates the symbol and string tables.
883 *
884 * @returns IPRT status code.
885 * @param pThis The internal scratch data.
886 * @param pszKernelFile The name of the kernel file.
887 */
888static int rtR0DbgKrnlDarwinLoadSymTab(RTDBGKRNLINFOINT *pThis, const char *pszKernelFile)
889{
890 /*
891 * Load the tables.
892 */
893 int rc;
894 pThis->paSyms = (MY_NLIST *)RTMemAllocZ(pThis->cSyms * sizeof(MY_NLIST));
895 if (pThis->paSyms)
896 {
897 rc = RTFileReadAt(pThis->hFile, pThis->offArch + pThis->offSyms, pThis->paSyms, pThis->cSyms * sizeof(MY_NLIST), NULL);
898 if (RT_SUCCESS(rc))
899 {
900 pThis->pachStrTab = (char *)RTMemAllocZ(pThis->cbStrTab + 1);
901 if (pThis->pachStrTab)
902 {
903 rc = RTFileReadAt(pThis->hFile, pThis->offArch + pThis->offStrTab, pThis->pachStrTab, pThis->cbStrTab, NULL);
904 if (RT_SUCCESS(rc))
905 {
906 /*
907 * Join paths with the in-memory code path.
908 */
909 rc = rtR0DbgKrnlDarwinParseSymTab(pThis, pszKernelFile);
910 }
911 }
912 else
913 rc = VERR_NO_MEMORY;
914 }
915 }
916 else
917 rc = VERR_NO_MEMORY;
918 return rc;
919}
920
921
922/**
923 * Loads the load commands and validates them.
924 *
925 * @returns IPRT status code.
926 * @param pThis The internal scratch data.
927 */
928static int rtR0DbgKrnlDarwinLoadCommands(RTDBGKRNLINFOINT *pThis)
929{
930 int rc;
931 pThis->pLoadCmds = (load_command_t *)RTMemAlloc(pThis->cbLoadCmds);
932 if (pThis->pLoadCmds)
933 {
934 rc = RTFileReadAt(pThis->hFile, pThis->offArch + sizeof(MY_MACHO_HEADER), pThis->pLoadCmds, pThis->cbLoadCmds, NULL);
935 if (RT_SUCCESS(rc))
936 rc = rtR0DbgKrnlDarwinParseCommands(pThis);
937 }
938 else
939 rc = VERR_NO_MEMORY;
940 return rc;
941}
942
943
944/**
945 * Loads the FAT and MACHO headers, noting down the relevant info.
946 *
947 * @returns IPRT status code.
948 * @param pThis The internal scratch data.
949 */
950static int rtR0DbgKrnlDarwinLoadFileHeaders(RTDBGKRNLINFOINT *pThis)
951{
952 uint32_t i;
953
954 pThis->offArch = 0;
955 pThis->cbArch = 0;
956
957 /*
958 * Read the first bit of the file, parse the FAT if found there.
959 */
960 int rc = RTFileReadAt(pThis->hFile, 0, pThis->abBuf, sizeof(fat_header_t) + sizeof(fat_arch_t) * 16, NULL);
961 if (RT_FAILURE(rc))
962 return rc;
963
964 fat_header_t *pFat = (fat_header *)pThis->abBuf;
965 fat_arch_t *paFatArches = (fat_arch_t *)(pFat + 1);
966
967 /* Correct FAT endian first. */
968 if (pFat->magic == IMAGE_FAT_SIGNATURE_OE)
969 {
970 pFat->magic = RT_BSWAP_U32(pFat->magic);
971 pFat->nfat_arch = RT_BSWAP_U32(pFat->nfat_arch);
972 i = RT_MIN(pFat->nfat_arch, 16);
973 while (i-- > 0)
974 {
975 paFatArches[i].cputype = RT_BSWAP_U32(paFatArches[i].cputype);
976 paFatArches[i].cpusubtype = RT_BSWAP_U32(paFatArches[i].cpusubtype);
977 paFatArches[i].offset = RT_BSWAP_U32(paFatArches[i].offset);
978 paFatArches[i].size = RT_BSWAP_U32(paFatArches[i].size);
979 paFatArches[i].align = RT_BSWAP_U32(paFatArches[i].align);
980 }
981 }
982
983 /* Lookup our architecture in the FAT. */
984 if (pFat->magic == IMAGE_FAT_SIGNATURE)
985 {
986 if (pFat->nfat_arch > 16)
987 RETURN_VERR_BAD_EXE_FORMAT;
988
989 for (i = 0; i < pFat->nfat_arch; i++)
990 {
991 if ( paFatArches[i].cputype == MY_CPU_TYPE
992 && paFatArches[i].cpusubtype == MY_CPU_SUBTYPE_ALL)
993 {
994 pThis->offArch = paFatArches[i].offset;
995 pThis->cbArch = paFatArches[i].size;
996 if (!pThis->cbArch)
997 RETURN_VERR_BAD_EXE_FORMAT;
998 if (pThis->offArch < sizeof(fat_header_t) + sizeof(fat_arch_t) * pFat->nfat_arch)
999 RETURN_VERR_BAD_EXE_FORMAT;
1000 if (pThis->offArch + pThis->cbArch <= pThis->offArch)
1001 RETURN_VERR_LDR_ARCH_MISMATCH;
1002 break;
1003 }
1004 }
1005 if (i >= pFat->nfat_arch)
1006 RETURN_VERR_LDR_ARCH_MISMATCH;
1007 }
1008
1009 /*
1010 * Read the Mach-O header and validate it.
1011 */
1012 rc = RTFileReadAt(pThis->hFile, pThis->offArch, pThis->abBuf, sizeof(MY_MACHO_HEADER), NULL);
1013 if (RT_FAILURE(rc))
1014 return rc;
1015 MY_MACHO_HEADER const *pHdr = (MY_MACHO_HEADER const *)pThis->abBuf;
1016 if (pHdr->magic != MY_MACHO_MAGIC)
1017 {
1018 if ( pHdr->magic == IMAGE_MACHO32_SIGNATURE
1019 || pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
1020 || pHdr->magic == IMAGE_MACHO64_SIGNATURE
1021 || pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE)
1022 RETURN_VERR_LDR_ARCH_MISMATCH;
1023 RETURN_VERR_BAD_EXE_FORMAT;
1024 }
1025
1026 if (pHdr->cputype != MY_CPU_TYPE)
1027 RETURN_VERR_LDR_ARCH_MISMATCH;
1028 if (pHdr->cpusubtype != MY_CPU_SUBTYPE_ALL)
1029 RETURN_VERR_LDR_ARCH_MISMATCH;
1030 if (pHdr->filetype != MH_EXECUTE)
1031 RETURN_VERR_LDR_UNEXPECTED;
1032 if (pHdr->ncmds < 4)
1033 RETURN_VERR_LDR_UNEXPECTED;
1034 if (pHdr->ncmds > 256)
1035 RETURN_VERR_LDR_UNEXPECTED;
1036 if (pHdr->sizeofcmds <= pHdr->ncmds * sizeof(load_command_t))
1037 RETURN_VERR_LDR_UNEXPECTED;
1038 if (pHdr->sizeofcmds >= _1M)
1039 RETURN_VERR_LDR_UNEXPECTED;
1040 if (pHdr->flags & ~MH_VALID_FLAGS)
1041 RETURN_VERR_LDR_UNEXPECTED;
1042
1043 pThis->cLoadCmds = pHdr->ncmds;
1044 pThis->cbLoadCmds = pHdr->sizeofcmds;
1045 return VINF_SUCCESS;
1046}
1047
1048
1049/**
1050 * Destructor.
1051 *
1052 * @param pThis The instance to destroy.
1053 */
1054static void rtR0DbgKrnlDarwinDtor(RTDBGKRNLINFOINT *pThis)
1055{
1056 pThis->u32Magic = ~RTDBGKRNLINFO_MAGIC;
1057
1058 if (!pThis->fIsInMem)
1059 RTMemFree(pThis->pachStrTab);
1060 pThis->pachStrTab = NULL;
1061
1062 if (!pThis->fIsInMem)
1063 RTMemFree(pThis->paSyms);
1064 pThis->paSyms = NULL;
1065
1066 RTMemFree(pThis);
1067}
1068
1069
1070/**
1071 * Completes a handle, logging details.
1072 *
1073 * @returns VINF_SUCCESS
1074 * @param phKrnlInfo Where to return the handle.
1075 * @param pThis The instance to complete.
1076 * @param pszKernelFile What kernel file it's based on.
1077 */
1078static int rtR0DbgKrnlDarwinSuccess(PRTDBGKRNLINFO phKrnlInfo, RTDBGKRNLINFOINT *pThis, const char *pszKernelFile)
1079{
1080 pThis->u32Magic = RTDBGKRNLINFO_MAGIC;
1081 pThis->cRefs = 1;
1082
1083#if defined(DEBUG) || defined(IN_RING3)
1084 LOG_SUCCESS("RTR0DbgKrnlInfoOpen: Found: %#zx + %#zx - %s\n", pThis->uTextSegLinkAddr, pThis->offLoad, pszKernelFile);
1085#else
1086 LOG_SUCCESS("RTR0DbgKrnlInfoOpen: Found: %s\n", pszKernelFile);
1087#endif
1088 LOG_SUCCESS("RTR0DbgKrnlInfoOpen: SDK version: %u.%u.%u MinOS version: %u.%u.%u Source version: %u.%u.%u.%u.%u\n",
1089 pThis->uSdkVer >> 16, (pThis->uSdkVer >> 8) & 0xff, pThis->uSdkVer & 0xff,
1090 pThis->uMinOsVer >> 16, (pThis->uMinOsVer >> 8) & 0xff, pThis->uMinOsVer & 0xff,
1091 (uint32_t)(pThis->uSrcVer >> 40),
1092 (uint32_t)(pThis->uSrcVer >> 30) & 0x3ff,
1093 (uint32_t)(pThis->uSrcVer >> 20) & 0x3ff,
1094 (uint32_t)(pThis->uSrcVer >> 10) & 0x3ff,
1095 (uint32_t)(pThis->uSrcVer) & 0x3ff);
1096
1097 *phKrnlInfo = pThis;
1098 return VINF_SUCCESS;
1099}
1100
1101
1102static int rtR0DbgKrnlDarwinOpen(PRTDBGKRNLINFO phKrnlInfo, const char *pszKernelFile)
1103{
1104 RTDBGKRNLINFOINT *pThis = (RTDBGKRNLINFOINT *)RTMemAllocZ(sizeof(*pThis));
1105 if (!pThis)
1106 return VERR_NO_MEMORY;
1107 pThis->hFile = NIL_RTFILE;
1108
1109 int rc = RTFileOpen(&pThis->hFile, pszKernelFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1110 if (RT_SUCCESS(rc))
1111 rc = rtR0DbgKrnlDarwinLoadFileHeaders(pThis);
1112 if (RT_SUCCESS(rc))
1113 rc = rtR0DbgKrnlDarwinLoadCommands(pThis);
1114 if (RT_SUCCESS(rc))
1115 rc = rtR0DbgKrnlDarwinLoadSymTab(pThis, pszKernelFile);
1116 if (RT_SUCCESS(rc))
1117 {
1118#ifdef IN_RING0
1119 /*
1120 * Determine the load displacement (10.8 kernels are PIE).
1121 */
1122 uintptr_t uLinkAddr = rtR0DbgKrnlDarwinLookup(pThis, "kernel_map");
1123 if (uLinkAddr != 0)
1124 pThis->offLoad = (uintptr_t)&kernel_map - uLinkAddr;
1125#endif
1126 rc = rtR0DbgKrnlDarwinCheckStandardSymbols(pThis, pszKernelFile);
1127 }
1128
1129 rtR0DbgKrnlDarwinLoadDone(pThis);
1130 if (RT_SUCCESS(rc))
1131 rtR0DbgKrnlDarwinSuccess(phKrnlInfo, pThis, pszKernelFile);
1132 else
1133 rtR0DbgKrnlDarwinDtor(pThis);
1134 return rc;
1135}
1136
1137
1138#ifdef IN_RING0
1139
1140/**
1141 * Checks if a page is present.
1142 * @returns true if it is, false if it isn't.
1143 * @param uPageAddr The address of/in the page to check.
1144 */
1145static bool rtR0DbgKrnlDarwinIsPagePresent(uintptr_t uPageAddr)
1146{
1147 /** @todo the dtrace code subjects the result to pmap_is_valid, but that
1148 * isn't exported, so we'll have to make to with != 0 here. */
1149 return pmap_find_phys(kernel_pmap, uPageAddr) != 0;
1150}
1151
1152
1153/**
1154 * Used to check whether a memory range is present or not.
1155 *
1156 * This is applied to the to the load commands and selected portions of the link
1157 * edit segment.
1158 *
1159 * @returns true if all present, false if not.
1160 * @param uAddress The start address.
1161 * @param cb Number of bytes to check.
1162 * @param pszWhat What we're checking, for logging.
1163 * @param pHdr The header address (for logging).
1164 */
1165static bool rtR0DbgKrnlDarwinIsRangePresent(uintptr_t uAddress, size_t cb,
1166 const char *pszWhat, MY_MACHO_HEADER const volatile *pHdr)
1167{
1168 uintptr_t const uStartAddress = uAddress;
1169 intptr_t cPages = RT_ALIGN_Z(cb + (uAddress & PAGE_OFFSET_MASK), PAGE_SIZE);
1170 RT_NOREF(uStartAddress, pszWhat, pHdr);
1171 for (;;)
1172 {
1173 if (!rtR0DbgKrnlDarwinIsPagePresent(uAddress))
1174 {
1175 LOG_NOT_PRESENT("RTR0DbgInfo: %p: Page in %s is not present: %#zx - rva %#zx; in structure %#zx (%#zx LB %#zx)\n",
1176 pHdr, pszWhat, uAddress, uAddress - (uintptr_t)pHdr, uAddress - uStartAddress, uStartAddress, cb);
1177 return false;
1178 }
1179
1180 cPages -= 1;
1181 if (cPages <= 0)
1182 uAddress += PAGE_SIZE;
1183 else
1184 return true;
1185 }
1186}
1187
1188
1189/**
1190 * Try "open" the in-memory kernel image
1191 *
1192 * @returns IPRT stauts code
1193 * @param phKrnlInfo Where to return the info instance on success.
1194 */
1195static int rtR0DbgKrnlDarwinOpenInMemory(PRTDBGKRNLINFO phKrnlInfo)
1196{
1197 RTDBGKRNLINFOINT *pThis = (RTDBGKRNLINFOINT *)RTMemAllocZ(sizeof(*pThis));
1198 if (!pThis)
1199 return VERR_NO_MEMORY;
1200 pThis->hFile = NIL_RTFILE;
1201 pThis->fIsInMem = true;
1202
1203 /*
1204 * Figure the search range based on a symbol that is supposed to be in
1205 * kernel text segment, using it as the upper boundrary. The lower boundary
1206 * is determined by subtracting a max kernel size of 64MB (the largest kernel
1207 * file, kernel.kasan, is around 45MB, but the end of __TEXT is about 27 MB,
1208 * which means we should still have plenty of room for future growth with 64MB).
1209 */
1210 uintptr_t const uSomeKernelAddr = (uintptr_t)&absolutetime_to_nanoseconds;
1211 uintptr_t const uLowestKernelAddr = uSomeKernelAddr - _64M;
1212
1213 /*
1214 * The kernel is probably aligned at some boundrary larger than a page size,
1215 * so to speed things up we start by assuming the alignment is page directory
1216 * sized. In case we're wrong and it's smaller, we decrease the alignment till
1217 * we've reach the page size.
1218 */
1219 uintptr_t fPrevAlignMask = ~(uintptr_t)0;
1220 uintptr_t uCurAlign = _2M; /* ASSUMES the kernel is typically 2MB aligned. */
1221 while (uCurAlign >= PAGE_SIZE)
1222 {
1223 /*
1224 * Search down from the symbol address looking for a mach-O header that
1225 * looks like it might belong to the kernel.
1226 */
1227 for (uintptr_t uCur = uSomeKernelAddr & ~(uCurAlign - 1); uCur >= uLowestKernelAddr; uCur -= uCurAlign)
1228 {
1229 /* Skip pages we've checked in previous iterations and pages that aren't present: */
1230 /** @todo This is a little bogus in case the header is paged out. */
1231 if ( (uCur & fPrevAlignMask)
1232 && rtR0DbgKrnlDarwinIsPagePresent(uCur))
1233 {
1234 /*
1235 * Look for valid mach-o header (we skip cpusubtype on purpose here).
1236 */
1237 MY_MACHO_HEADER const volatile *pHdr = (MY_MACHO_HEADER const volatile *)uCur;
1238 if ( pHdr->magic == MY_MACHO_MAGIC
1239 && pHdr->filetype == MH_EXECUTE
1240 && pHdr->cputype == MY_CPU_TYPE)
1241 {
1242 /* More header validation: */
1243 pThis->cLoadCmds = pHdr->ncmds;
1244 pThis->cbLoadCmds = pHdr->sizeofcmds;
1245 if (pHdr->ncmds < 4)
1246 LOG_MISMATCH("RTR0DbgInfo: %p: ncmds=%u is too small\n", pHdr, pThis->cLoadCmds);
1247 else if (pThis->cLoadCmds > 256)
1248 LOG_MISMATCH("RTR0DbgInfo: %p: ncmds=%u is too big\n", pHdr, pThis->cLoadCmds);
1249 else if (pThis->cbLoadCmds <= pThis->cLoadCmds * sizeof(load_command_t))
1250 LOG_MISMATCH("RTR0DbgInfo: %p: sizeofcmds=%u is too small for ncmds=%u\n",
1251 pHdr, pThis->cbLoadCmds, pThis->cLoadCmds);
1252 else if (pThis->cbLoadCmds >= _1M)
1253 LOG_MISMATCH("RTR0DbgInfo: %p: sizeofcmds=%u is too big\n", pHdr, pThis->cbLoadCmds);
1254 else if (pHdr->flags & ~MH_VALID_FLAGS)
1255 LOG_MISMATCH("RTR0DbgInfo: %p: invalid flags=%#x\n", pHdr, pHdr->flags);
1256 /*
1257 * Check that we can safely read the load commands, then parse & validate them.
1258 */
1259 else if (rtR0DbgKrnlDarwinIsRangePresent((uintptr_t)(pHdr + 1), pThis->cbLoadCmds, "load commands", pHdr))
1260 {
1261 pThis->pLoadCmds = (load_command_t *)(pHdr + 1);
1262 int rc = rtR0DbgKrnlDarwinParseCommands(pThis);
1263 if (RT_SUCCESS(rc))
1264 {
1265 /* Calculate the slide value. This is typically zero as the
1266 load commands has been relocated (the case with 10.14.0 at least). */
1267 /** @todo ASSUMES that the __TEXT segment comes first and includes the
1268 * mach-o header and load commands and all that. */
1269 pThis->offLoad = uCur - pThis->uTextSegLinkAddr;
1270
1271 /* Check that the kernel symbol is in the text segment: */
1272 uintptr_t const offSomeKernAddr = uSomeKernelAddr - uCur;
1273 if (offSomeKernAddr >= pThis->cbTextSeg)
1274 LOG_MISMATCH("RTR0DbgInfo: %p: Our symbol at %zx (off %zx) isn't within the text segment (size %#zx)\n",
1275 pHdr, uSomeKernelAddr, offSomeKernAddr, pThis->cbTextSeg);
1276 /*
1277 * Parse the symbol+string tables.
1278 */
1279 else if (pThis->uSymTabLinkAddr == 0)
1280 LOG_MISMATCH("RTR0DbgInfo: %p: No symbol table VA (off %#x L %#x)\n",
1281 pHdr, pThis->offSyms, pThis->cSyms);
1282 else if (pThis->uStrTabLinkAddr == 0)
1283 LOG_MISMATCH("RTR0DbgInfo: %p: No string table VA (off %#x LB %#x)\n",
1284 pHdr, pThis->offSyms, pThis->cbStrTab);
1285 else if ( rtR0DbgKrnlDarwinIsRangePresent(pThis->uStrTabLinkAddr + pThis->offLoad,
1286 pThis->cbStrTab, "string table", pHdr)
1287 && rtR0DbgKrnlDarwinIsRangePresent(pThis->uSymTabLinkAddr + pThis->offLoad,
1288 pThis->cSyms * sizeof(pThis->paSyms),
1289 "symbol table", pHdr))
1290 {
1291 pThis->pachStrTab = (char *)pThis->uStrTabLinkAddr + pThis->offLoad;
1292 pThis->paSyms = (MY_NLIST *)pThis->uSymTabLinkAddr + pThis->offLoad;
1293 rc = rtR0DbgKrnlDarwinParseSymTab(pThis, "in-memory");
1294 if (RT_SUCCESS(rc))
1295 {
1296 /*
1297 * Finally check the standard candles.
1298 */
1299 rc = rtR0DbgKrnlDarwinCheckStandardSymbols(pThis, "in-memory");
1300 rtR0DbgKrnlDarwinLoadDone(pThis);
1301 if (RT_SUCCESS(rc))
1302 return rtR0DbgKrnlDarwinSuccess(phKrnlInfo, pThis, "in-memory");
1303 }
1304 }
1305 }
1306
1307 RT_ZERO(pThis->apSections);
1308 RT_ZERO(pThis->apSegments);
1309 pThis->pLoadCmds = NULL;
1310 }
1311 }
1312 }
1313 }
1314
1315 fPrevAlignMask = uCurAlign - 1;
1316 uCurAlign >>= 1;
1317 }
1318
1319 RTMemFree(pThis);
1320 return VERR_GENERAL_FAILURE;
1321}
1322
1323#endif /* IN_RING0 */
1324
1325RTR0DECL(int) RTR0DbgKrnlInfoOpen(PRTDBGKRNLINFO phKrnlInfo, uint32_t fFlags)
1326{
1327 AssertPtrReturn(phKrnlInfo, VERR_INVALID_POINTER);
1328 *phKrnlInfo = NIL_RTDBGKRNLINFO;
1329 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
1330
1331#ifdef IN_RING0
1332 /*
1333 * Try see if we can use the kernel memory directly. This depends on not
1334 * having the __LINKEDIT segment jettisoned or swapped out. For older
1335 * kernels this is typically the case, unless kallsyms=1 is in boot-args.
1336 */
1337 int rc = rtR0DbgKrnlDarwinOpenInMemory(phKrnlInfo);
1338 if (RT_SUCCESS(rc))
1339 {
1340 Log(("RTR0DbgKrnlInfoOpen: Using in-memory kernel.\n"));
1341 return rc;
1342 }
1343#else
1344 int rc = VERR_WRONG_ORDER; /* shut up stupid MSC */
1345#endif
1346
1347 /*
1348 * Go thru likely kernel locations
1349 *
1350 * Note! Check the OS X version and reorder the list?
1351 * Note! We should try fish kcsuffix out of bootargs or somewhere one day.
1352 */
1353 static bool s_fFirstCall = true;
1354#ifdef IN_RING3
1355 extern const char *g_pszTestKernel;
1356#endif
1357 struct
1358 {
1359 const char *pszLocation;
1360 int rc;
1361 } aKernels[] =
1362 {
1363#ifdef IN_RING3
1364 { g_pszTestKernel, VERR_WRONG_ORDER },
1365#endif
1366 { "/System/Library/Kernels/kernel", VERR_WRONG_ORDER },
1367 { "/System/Library/Kernels/kernel.development", VERR_WRONG_ORDER },
1368 { "/System/Library/Kernels/kernel.debug", VERR_WRONG_ORDER },
1369 { "/mach_kernel", VERR_WRONG_ORDER },
1370 };
1371 for (uint32_t i = 0; i < RT_ELEMENTS(aKernels); i++)
1372 {
1373 aKernels[i].rc = rc = rtR0DbgKrnlDarwinOpen(phKrnlInfo, aKernels[i].pszLocation);
1374 if (RT_SUCCESS(rc))
1375 {
1376 if (s_fFirstCall)
1377 {
1378 printf("RTR0DbgKrnlInfoOpen: Using kernel file '%s'\n", aKernels[i].pszLocation);
1379 s_fFirstCall = false;
1380 }
1381 return rc;
1382 }
1383 }
1384
1385 /*
1386 * Failed.
1387 */
1388 /* Pick the best error code. */
1389 for (uint32_t i = 0; rc == VERR_FILE_NOT_FOUND && i < RT_ELEMENTS(aKernels); i++)
1390 if (aKernels[i].rc != VERR_FILE_NOT_FOUND)
1391 rc = aKernels[i].rc;
1392
1393 /* Bitch about it. */
1394 printf("RTR0DbgKrnlInfoOpen: failed to find matching kernel file! rc=%d\n", rc);
1395 if (s_fFirstCall)
1396 {
1397 for (uint32_t i = 0; i < RT_ELEMENTS(aKernels); i++)
1398 printf("RTR0DbgKrnlInfoOpen: '%s' -> %d\n", aKernels[i].pszLocation, aKernels[i].rc);
1399 s_fFirstCall = false;
1400 }
1401
1402 return rc;
1403}
1404
1405
1406RTR0DECL(uint32_t) RTR0DbgKrnlInfoRetain(RTDBGKRNLINFO hKrnlInfo)
1407{
1408 RTDBGKRNLINFOINT *pThis = hKrnlInfo;
1409 AssertPtrReturn(pThis, UINT32_MAX);
1410 AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX);
1411
1412 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
1413 Assert(cRefs && cRefs < 100000);
1414 return cRefs;
1415}
1416
1417
1418RTR0DECL(uint32_t) RTR0DbgKrnlInfoRelease(RTDBGKRNLINFO hKrnlInfo)
1419{
1420 RTDBGKRNLINFOINT *pThis = hKrnlInfo;
1421 if (pThis == NIL_RTDBGKRNLINFO)
1422 return 0;
1423 AssertPtrReturn(pThis, UINT32_MAX);
1424 AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX);
1425
1426 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
1427 if (cRefs == 0)
1428 rtR0DbgKrnlDarwinDtor(pThis);
1429 return cRefs;
1430}
1431
1432
1433RTR0DECL(int) RTR0DbgKrnlInfoQueryMember(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszStructure,
1434 const char *pszMember, size_t *poffMember)
1435{
1436 RTDBGKRNLINFOINT *pThis = hKrnlInfo;
1437 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1438 AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
1439 AssertPtrReturn(pszMember, VERR_INVALID_POINTER);
1440 AssertPtrReturn(pszModule, VERR_INVALID_POINTER);
1441 AssertPtrReturn(pszStructure, VERR_INVALID_POINTER);
1442 AssertPtrReturn(poffMember, VERR_INVALID_POINTER);
1443 return VERR_NOT_FOUND;
1444}
1445
1446
1447RTR0DECL(int) RTR0DbgKrnlInfoQuerySymbol(RTDBGKRNLINFO hKrnlInfo, const char *pszModule,
1448 const char *pszSymbol, void **ppvSymbol)
1449{
1450 RTDBGKRNLINFOINT *pThis = hKrnlInfo;
1451 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1452 AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
1453 AssertPtrReturn(pszSymbol, VERR_INVALID_PARAMETER);
1454 AssertPtrNullReturn(ppvSymbol, VERR_INVALID_PARAMETER);
1455 AssertReturn(!pszModule, VERR_MODULE_NOT_FOUND);
1456
1457 uintptr_t uValue = rtR0DbgKrnlDarwinLookup(pThis, pszSymbol);
1458 if (ppvSymbol)
1459 *ppvSymbol = (void *)uValue;
1460 if (uValue)
1461 return VINF_SUCCESS;
1462 return VERR_SYMBOL_NOT_FOUND;
1463}
1464
Note: See TracBrowser for help on using the repository browser.

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