VirtualBox

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

Last change on this file since 86290 was 86290, checked in by vboxsync, 4 years ago

Runtime/r0drv/dbgfkrnlinfo-r0drv-darwin: Starting with BigSur, each segment can have a different load displacement. Account for that, bugref:9836

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

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