VirtualBox

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

Last change on this file since 85546 was 85341, checked in by vboxsync, 5 years ago

IPRt/dbgkrnlinfo-r0drv-darwin.cpp: Don't use deprecated network KPIs (>=10.15) as standard candles. bugref:9790

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