VirtualBox

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

Last change on this file since 109124 was 109124, checked in by vboxsync, 9 days ago

IPRT/dbgkrnlinfo-r0drv-darwin.cpp: Updates for arm64 and 14.7.5. jiraref:VBP-1653

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

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