VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use