VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 15 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use