VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGPlugInDarwin.cpp@ 83084

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

IPRT,DBGPlugInDarwin: Added flags for indicating that the LINKEDIT segment should be loaded in Mach-O images and debug files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.5 KB
Line 
1/* $Id: DBGPlugInDarwin.cpp 83084 2020-02-15 15:16:11Z vboxsync $ */
2/** @file
3 * DBGPlugInDarwin - Debugger and Guest OS Digger Plugin For Darwin / OS X.
4 */
5
6/*
7 * Copyright (C) 2008-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DBGF /// @todo add new log group.
23#include "DBGPlugIns.h"
24#include <VBox/vmm/dbgf.h>
25#include <iprt/err.h>
26#include <iprt/mem.h>
27#include <iprt/stream.h>
28#include <iprt/string.h>
29#include <iprt/uuid.h>
30#include <iprt/ctype.h>
31#include <iprt/formats/mach-o.h>
32
33#undef LogRel2
34#define LogRel2 LogRel
35
36
37/*********************************************************************************************************************************
38* Structures and Typedefs *
39*********************************************************************************************************************************/
40
41/** @name Internal Darwin structures
42 * @{ */
43
44/**
45 * 32-bit darwin kernel module info structure (kmod_info_t).
46 */
47typedef struct OSX32_kmod_info
48{
49 uint32_t next;
50 int32_t info_version;
51 uint32_t id;
52 char name[64];
53 char version[64];
54 int32_t reference_count;
55 uint32_t reference_list; /**< Points to kmod_reference_t. */
56 uint32_t address; /**< Where in memory the kext is loaded. */
57 uint32_t size;
58 uint32_t hdr_size;
59 uint32_t start; /**< Address of kmod_start_func_t. */
60 uint32_t stop; /**< Address of kmod_stop_func_t. */
61} OSX32_kmod_info_t;
62
63/**
64 * 32-bit darwin kernel module info structure (kmod_info_t).
65 */
66#pragma pack(1)
67typedef struct OSX64_kmod_info
68{
69 uint64_t next;
70 int32_t info_version;
71 uint32_t id;
72 char name[64];
73 char version[64];
74 int32_t reference_count;
75 uint64_t reference_list; /**< Points to kmod_reference_t. Misaligned, duh. */
76 uint64_t address; /**< Where in memory the kext is loaded. */
77 uint64_t size;
78 uint64_t hdr_size;
79 uint64_t start; /**< Address of kmod_start_func_t. */
80 uint64_t stop; /**< Address of kmod_stop_func_t. */
81} OSX64_kmod_info_t;
82#pragma pack()
83
84/** The value of the info_version field. */
85#define OSX_KMOD_INFO_VERSION INT32_C(1)
86
87/** @} */
88
89
90/**
91 * Linux guest OS digger instance data.
92 */
93typedef struct DBGDIGGERDARWIN
94{
95 /** Whether the information is valid or not.
96 * (For fending off illegal interface method calls.) */
97 bool fValid;
98
99 /** Set if 64-bit kernel, clear if 32-bit.
100 * Set during probing. */
101 bool f64Bit;
102 /** The address of an kernel version string (there are several).
103 * This is set during probing. */
104 DBGFADDRESS AddrKernelVersion;
105 /** Kernel base address.
106 * This is set during probing. */
107 DBGFADDRESS AddrKernel;
108
109 /** The kernel message log interface. */
110 DBGFOSIDMESG IDmesg;
111} DBGDIGGERDARWIN;
112/** Pointer to the linux guest OS digger instance data. */
113typedef DBGDIGGERDARWIN *PDBGDIGGERDARWIN;
114
115
116/*********************************************************************************************************************************
117* Defined Constants And Macros *
118*********************************************************************************************************************************/
119/** Validates a 32-bit darwin kernel address */
120#define OSX32_VALID_ADDRESS(Addr) ((Addr) > UINT32_C(0x00001000) && (Addr) < UINT32_C(0xfffff000))
121/** Validates a 64-bit darwin kernel address */
122#define OSX64_VALID_ADDRESS(Addr) ((Addr) > UINT64_C(0xffff800000000000) && (Addr) < UINT64_C(0xfffffffffffff000))
123/** Validates a 32-bit or 64-bit darwin kernel address. */
124#define OSX_VALID_ADDRESS(a_f64Bits, a_Addr) \
125 ((a_f64Bits) ? OSX64_VALID_ADDRESS(a_Addr) : OSX32_VALID_ADDRESS(a_Addr))
126
127/** AppleOsX on little endian ASCII systems. */
128#define DIG_DARWIN_MOD_TAG UINT64_C(0x58734f656c707041)
129
130
131/*********************************************************************************************************************************
132* Internal Functions *
133*********************************************************************************************************************************/
134static DECLCALLBACK(int) dbgDiggerDarwinInit(PUVM pUVM, void *pvData);
135
136
137
138/**
139 * @interface_method_impl{DBGFOSIDMESG,pfnQueryKernelLog}
140 */
141static DECLCALLBACK(int) dbgDiggerDarwinIDmsg_QueryKernelLog(PDBGFOSIDMESG pThis, PUVM pUVM, uint32_t fFlags, uint32_t cMessages,
142 char *pszBuf, size_t cbBuf, size_t *pcbActual)
143{
144 RT_NOREF1(fFlags);
145 PDBGDIGGERDARWIN pData = RT_FROM_MEMBER(pThis, DBGDIGGERDARWIN, IDmesg);
146
147 if (cMessages < 1)
148 return VERR_INVALID_PARAMETER;
149
150 /*
151 * The 'msgbufp' variable points to a struct msgbuf (bsd/kern/subr_log.c).
152 */
153 RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
154 RTDBGMOD hMod;
155 int rc = RTDbgAsModuleByName(hAs, "mach_kernel", 0, &hMod);
156 if (RT_FAILURE(rc))
157 return VERR_NOT_FOUND;
158 RTDbgAsRelease(hAs);
159
160 DBGFADDRESS Addr;
161 RTGCPTR GCPtrMsgBufP = 0;
162 RTDBGSYMBOL SymInfo;
163 rc = RTDbgModSymbolByName(hMod, "_msgbufp", &SymInfo);
164 if (RT_SUCCESS(rc))
165 {
166 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, SymInfo.Value + pData->AddrKernel.FlatPtr),
167 &GCPtrMsgBufP, pData->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t));
168 if (RT_FAILURE(rc))
169 {
170 LogRel(("dbgDiggerDarwinIDmsg_QueryKernelLog: failed to read _msgbufp at %RGv: %Rrc\n", Addr.FlatPtr, rc));
171 return VERR_NOT_FOUND;
172 }
173 if (!OSX_VALID_ADDRESS(pData->f64Bit, GCPtrMsgBufP))
174 {
175 LogRel(("dbgDiggerDarwinIDmsg_QueryKernelLog: Invalid address for _msgbufp: %RGv\n", GCPtrMsgBufP));
176 return VERR_NOT_FOUND;
177 }
178 }
179 else
180 {
181 rc = RTDbgModSymbolByName(hMod, "_msgbuf", &SymInfo);
182 if (RT_FAILURE(rc))
183 {
184 LogRel(("dbgDiggerDarwinIDmsg_QueryKernelLog: failed to find _msgbufp and _msgbuf: %Rrc\n", rc));
185 return VERR_NOT_FOUND;
186 }
187 GCPtrMsgBufP = SymInfo.Value + pData->AddrKernel.FlatPtr;
188 if (!OSX_VALID_ADDRESS(pData->f64Bit, GCPtrMsgBufP))
189 {
190 LogRel(("dbgDiggerDarwinIDmsg_QueryKernelLog: Invalid address for _msgbuf: %RGv\n", GCPtrMsgBufP));
191 return VERR_NOT_FOUND;
192 }
193 }
194
195 /*
196 * Read the msgbuf structure.
197 */
198 struct
199 {
200 uint32_t msg_magic;
201 uint32_t msg_size;
202 uint32_t msg_bufx;
203 uint32_t msg_bufr;
204 uint64_t msg_bufc; /**< Size depends on windows size. */
205 } MsgBuf;
206 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, GCPtrMsgBufP),
207 &MsgBuf, sizeof(MsgBuf) - (pData->f64Bit ? 0 : sizeof(uint32_t)) );
208 if (RT_FAILURE(rc))
209 {
210 LogRel(("dbgDiggerDarwinIDmsg_QueryKernelLog: failed to read msgbuf struct at %RGv: %Rrc\n", Addr.FlatPtr, rc));
211 return VERR_NOT_FOUND;
212 }
213 if (!pData->f64Bit)
214 MsgBuf.msg_bufc &= UINT32_MAX;
215
216 /*
217 * Validate the structure.
218 */
219 if ( MsgBuf.msg_magic != UINT32_C(0x63061)
220 || MsgBuf.msg_size < UINT32_C(4096)
221 || MsgBuf.msg_size > 16*_1M
222 || MsgBuf.msg_bufx > MsgBuf.msg_size
223 || MsgBuf.msg_bufr > MsgBuf.msg_size
224 || !OSX_VALID_ADDRESS(pData->f64Bit, MsgBuf.msg_bufc) )
225 {
226 LogRel(("dbgDiggerDarwinIDmsg_QueryKernelLog: Invalid MsgBuf data: magic=%#x size=%#x bufx=%#x bufr=%#x bufc=%RGv\n",
227 MsgBuf.msg_magic, MsgBuf.msg_size, MsgBuf.msg_bufx, MsgBuf.msg_bufr, MsgBuf.msg_bufc));
228 return VERR_INVALID_STATE;
229 }
230
231 /*
232 * Read the buffer.
233 */
234 char *pchMsgBuf = (char *)RTMemAlloc(MsgBuf.msg_size);
235 if (!pchMsgBuf)
236 {
237 LogRel(("dbgDiggerDarwinIDmsg_QueryKernelLog: Failed to allocate %#x bytes of memory for the log buffer\n",
238 MsgBuf.msg_size));
239 return VERR_INVALID_STATE;
240 }
241 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, MsgBuf.msg_bufc), pchMsgBuf, MsgBuf.msg_size);
242 if (RT_SUCCESS(rc))
243 {
244 /*
245 * Copy it out raw.
246 */
247 uint32_t offDst = 0;
248 if (MsgBuf.msg_bufr < MsgBuf.msg_bufx)
249 {
250 /* Single chunk between the read and write offsets. */
251 uint32_t cbToCopy = MsgBuf.msg_bufx - MsgBuf.msg_bufr;
252 if (cbToCopy < cbBuf)
253 {
254 memcpy(pszBuf, &pchMsgBuf[MsgBuf.msg_bufr], cbToCopy);
255 pszBuf[cbToCopy] = '\0';
256 rc = VINF_SUCCESS;
257 }
258 else
259 {
260 if (cbBuf)
261 {
262 memcpy(pszBuf, &pchMsgBuf[MsgBuf.msg_bufr], cbBuf - 1);
263 pszBuf[cbBuf - 1] = '\0';
264 }
265 rc = VERR_BUFFER_OVERFLOW;
266 }
267 offDst = cbToCopy + 1;
268 }
269 else
270 {
271 /* Two chunks, read offset to end, start to write offset. */
272 uint32_t cbFirst = MsgBuf.msg_size - MsgBuf.msg_bufr;
273 uint32_t cbSecond = MsgBuf.msg_bufx;
274 if (cbFirst + cbSecond < cbBuf)
275 {
276 memcpy(pszBuf, &pchMsgBuf[MsgBuf.msg_bufr], cbFirst);
277 memcpy(&pszBuf[cbFirst], pchMsgBuf, cbSecond);
278 offDst = cbFirst + cbSecond;
279 pszBuf[offDst++] = '\0';
280 rc = VINF_SUCCESS;
281 }
282 else
283 {
284 offDst = cbFirst + cbSecond + 1;
285 if (cbFirst < cbBuf)
286 {
287 memcpy(pszBuf, &pchMsgBuf[MsgBuf.msg_bufr], cbFirst);
288 memcpy(&pszBuf[cbFirst], pchMsgBuf, cbBuf - cbFirst);
289 pszBuf[cbBuf - 1] = '\0';
290 }
291 else if (cbBuf)
292 {
293 memcpy(pszBuf, &pchMsgBuf[MsgBuf.msg_bufr], cbBuf - 1);
294 pszBuf[cbBuf - 1] = '\0';
295 }
296 rc = VERR_BUFFER_OVERFLOW;
297 }
298 }
299
300 if (pcbActual)
301 *pcbActual = offDst;
302 }
303 else
304 LogRel(("dbgDiggerDarwinIDmsg_QueryKernelLog: Error reading %#x bytes at %RGv: %Rrc\n",
305 MsgBuf.msg_size, MsgBuf.msg_bufc, rc));
306 RTMemFree(pchMsgBuf);
307 return rc;
308}
309
310
311/**
312 * @copydoc DBGFOSREG::pfnStackUnwindAssist
313 */
314static DECLCALLBACK(int) dbgDiggerDarwinStackUnwindAssist(PUVM pUVM, void *pvData, VMCPUID idCpu, PDBGFSTACKFRAME pFrame,
315 PRTDBGUNWINDSTATE pState, PCCPUMCTX pInitialCtx, RTDBGAS hAs,
316 uint64_t *puScratch)
317{
318 RT_NOREF(pUVM, pvData, idCpu, pFrame, pState, pInitialCtx, hAs, puScratch);
319 return VINF_SUCCESS;
320}
321
322
323/**
324 * @copydoc DBGFOSREG::pfnQueryInterface
325 */
326static DECLCALLBACK(void *) dbgDiggerDarwinQueryInterface(PUVM pUVM, void *pvData, DBGFOSINTERFACE enmIf)
327{
328 RT_NOREF1(pUVM);
329 PDBGDIGGERDARWIN pThis = (PDBGDIGGERDARWIN)pvData;
330 switch (enmIf)
331 {
332 case DBGFOSINTERFACE_DMESG:
333 return &pThis->IDmesg;
334
335 default:
336 return NULL;
337 }
338}
339
340
341/**
342 * @copydoc DBGFOSREG::pfnQueryVersion
343 */
344static DECLCALLBACK(int) dbgDiggerDarwinQueryVersion(PUVM pUVM, void *pvData, char *pszVersion, size_t cchVersion)
345{
346 PDBGDIGGERDARWIN pThis = (PDBGDIGGERDARWIN)pvData;
347 Assert(pThis->fValid);
348
349 /*
350 * It's all in the linux banner.
351 */
352 int rc = DBGFR3MemReadString(pUVM, 0, &pThis->AddrKernelVersion, pszVersion, cchVersion);
353 if (RT_SUCCESS(rc))
354 {
355 char *pszEnd = RTStrEnd(pszVersion, cchVersion);
356 AssertReturn(pszEnd, VERR_BUFFER_OVERFLOW);
357 while ( pszEnd > pszVersion
358 && RT_C_IS_SPACE(pszEnd[-1]))
359 pszEnd--;
360 *pszEnd = '\0';
361 }
362 else
363 RTStrPrintf(pszVersion, cchVersion, "DBGFR3MemRead -> %Rrc", rc);
364
365 return rc;
366}
367
368
369/**
370 * @copydoc DBGFOSREG::pfnTerm
371 */
372static DECLCALLBACK(void) dbgDiggerDarwinTerm(PUVM pUVM, void *pvData)
373{
374 RT_NOREF1(pUVM);
375 PDBGDIGGERDARWIN pThis = (PDBGDIGGERDARWIN)pvData;
376
377 pThis->fValid = false;
378}
379
380
381/**
382 * @copydoc DBGFOSREG::pfnRefresh
383 */
384static DECLCALLBACK(int) dbgDiggerDarwinRefresh(PUVM pUVM, void *pvData)
385{
386 PDBGDIGGERDARWIN pThis = (PDBGDIGGERDARWIN)pvData;
387 NOREF(pThis);
388 Assert(pThis->fValid);
389
390 /*
391 * For now we'll flush and reload everything.
392 */
393 dbgDiggerDarwinTerm(pUVM, pvData);
394 return dbgDiggerDarwinInit(pUVM, pvData);
395}
396
397
398/**
399 * Helper function that tries to accertain whether a segment (__LINKEDIT) is
400 * present or not.
401 *
402 * @returns true if present, false if not.
403 * @param pUVM The user mode VM structure.
404 * @param uSegAddr The segment addresss.
405 * @param cbSeg The segment size.
406 * @param uMinAddr Lowest allowed address.
407 * @param uMaxAddr Highest allowed address.
408 */
409static int dbgDiggerDarwinIsSegmentPresent(PUVM pUVM, uint64_t uSegAddr, uint64_t cbSeg, uint64_t uMinAddr, uint64_t uMaxAddr)
410{
411 /*
412 * Validate the size and address.
413 */
414 if (cbSeg < 32)
415 {
416 LogRel(("OSXDig: __LINKEDIT too small %#RX64\n", cbSeg));
417 return false;
418 }
419 if (cbSeg > uMaxAddr - uMinAddr)
420 {
421 LogRel(("OSXDig: __LINKEDIT too big %#RX64, max %#RX64\n", cbSeg, uMaxAddr - uMinAddr));
422 return false;
423 }
424
425 if (uSegAddr < uMinAddr)
426 {
427 LogRel(("OSXDig: __LINKEDIT too low %#RX64, min %#RX64\n", uSegAddr, uMinAddr));
428 return false;
429 }
430 if (uSegAddr > uMaxAddr)
431 {
432 LogRel(("OSXDig: __LINKEDIT too high %#RX64, max %#RX64\n", uSegAddr, uMaxAddr));
433 return false;
434 }
435 if (uSegAddr + cbSeg > uMaxAddr)
436 {
437 LogRel(("OSXDig: __LINKEDIT ends too high %#RX64 (%#RX64+%#RX64), max %#RX64\n",
438 uSegAddr + cbSeg, uSegAddr, cbSeg, uMaxAddr));
439 return false;
440 }
441
442 /*
443 * Check that all the pages are present.
444 */
445 cbSeg += uSegAddr & X86_PAGE_OFFSET_MASK;
446 uSegAddr &= ~(uint64_t)X86_PAGE_OFFSET_MASK;
447 for (;;)
448 {
449 uint8_t abBuf[8];
450 DBGFADDRESS Addr;
451 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, uSegAddr), abBuf, sizeof(abBuf));
452 if (RT_FAILURE(rc))
453 {
454 LogRel(("OSXDig: __LINKEDIT read error at %#RX64: %Rrc\n", uSegAddr, rc));
455 return false;
456 }
457
458 /* Advance */
459 if (cbSeg <= X86_PAGE_SIZE)
460 return true;
461 cbSeg -= X86_PAGE_SIZE;
462 uSegAddr += X86_PAGE_SIZE;
463 }
464}
465
466
467/**
468 * Helper function that validates a segment (or section) name.
469 *
470 * @returns true if valid, false if not.
471 * @param pszName The name string.
472 * @param cbName The size of the string, including terminator.
473 */
474static bool dbgDiggerDarwinIsValidSegOrSectName(const char *pszName, size_t cbName)
475{
476 /* ascii chars */
477 char ch;
478 size_t off = 0;
479 while (off < cbName && (ch = pszName[off]))
480 {
481 if (RT_C_IS_CNTRL(ch) || ch >= 127)
482 return false;
483 off++;
484 }
485
486 /* Not empty nor 100% full. */
487 if (off == 0 || off == cbName)
488 return false;
489
490 /* remainder should be zeros. */
491 while (off < cbName)
492 {
493 if (pszName[off])
494 return false;
495 off++;
496 }
497
498 return true;
499}
500
501
502static int dbgDiggerDarwinAddModule(PDBGDIGGERDARWIN pThis, PUVM pUVM, uint64_t uModAddr, const char *pszName, bool *pf64Bit)
503{
504 RT_NOREF1(pThis);
505 union
506 {
507 uint8_t ab[2 * X86_PAGE_4K_SIZE];
508 mach_header_64_t Hdr64;
509 mach_header_32_t Hdr32;
510 } uBuf;
511
512 /* Read the first page of the image. */
513 DBGFADDRESS ModAddr;
514 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &ModAddr, uModAddr), uBuf.ab, X86_PAGE_4K_SIZE);
515 if (RT_FAILURE(rc))
516 return rc;
517
518 /* Validate the header. */
519 AssertCompileMembersSameSizeAndOffset(mach_header_64_t, magic, mach_header_32_t, magic);
520 if ( uBuf.Hdr64.magic != IMAGE_MACHO64_SIGNATURE
521 && uBuf.Hdr32.magic != IMAGE_MACHO32_SIGNATURE)
522 return VERR_INVALID_EXE_SIGNATURE;
523 AssertCompileMembersSameSizeAndOffset(mach_header_64_t, cputype, mach_header_32_t, cputype);
524 bool f64Bit = uBuf.Hdr64.magic == IMAGE_MACHO64_SIGNATURE;
525 if (uBuf.Hdr32.cputype != (f64Bit ? CPU_TYPE_X86_64 : CPU_TYPE_I386))
526 return VERR_LDR_ARCH_MISMATCH;
527 AssertCompileMembersSameSizeAndOffset(mach_header_64_t, filetype, mach_header_32_t, filetype);
528 if ( uBuf.Hdr32.filetype != MH_EXECUTE
529 && uBuf.Hdr32.filetype != (f64Bit ? MH_KEXT_BUNDLE : MH_OBJECT))
530 return VERR_BAD_EXE_FORMAT;
531 AssertCompileMembersSameSizeAndOffset(mach_header_64_t, ncmds, mach_header_32_t, ncmds);
532 if (uBuf.Hdr32.ncmds > 256)
533 return VERR_BAD_EXE_FORMAT;
534 AssertCompileMembersSameSizeAndOffset(mach_header_64_t, sizeofcmds, mach_header_32_t, sizeofcmds);
535 if (uBuf.Hdr32.sizeofcmds > X86_PAGE_4K_SIZE * 2 - sizeof(mach_header_64_t))
536 return VERR_BAD_EXE_FORMAT;
537
538 /* Do we need to read a 2nd page to get all the load commands? If so, do it. */
539 if (uBuf.Hdr32.sizeofcmds + (f64Bit ? sizeof(mach_header_64_t) : sizeof(mach_header_32_t)) > X86_PAGE_4K_SIZE)
540 {
541 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &ModAddr, uModAddr + X86_PAGE_4K_SIZE),
542 &uBuf.ab[X86_PAGE_4K_SIZE], X86_PAGE_4K_SIZE);
543 if (RT_FAILURE(rc))
544 return rc;
545 }
546
547 /*
548 * Process the load commands.
549 */
550 RTUUID Uuid = RTUUID_INITIALIZE_NULL;
551 RTDBGSEGMENT aSegs[24];
552 uint32_t cSegs = 0;
553 bool fHasLinkEdit = false;
554 uint32_t cLeft = uBuf.Hdr32.ncmds;
555 uint32_t cbLeft = uBuf.Hdr32.sizeofcmds;
556 union
557 {
558 uint8_t const *pb;
559 load_command_t const *pGenric;
560 segment_command_32_t const *pSeg32;
561 segment_command_64_t const *pSeg64;
562 uuid_command_t const *pUuid;
563 } uLCmd;
564 uLCmd.pb = &uBuf.ab[f64Bit ? sizeof(mach_header_64_t) : sizeof(mach_header_32_t)];
565
566 while (cLeft-- > 0)
567 {
568 uint32_t const cbCmd = uLCmd.pGenric->cmdsize;
569 if (cbCmd > cbLeft || cbCmd < sizeof(load_command_t))
570 return VERR_BAD_EXE_FORMAT;
571
572 switch (uLCmd.pGenric->cmd)
573 {
574 case LC_SEGMENT_32:
575 if (cbCmd != sizeof(segment_command_32_t) + uLCmd.pSeg32->nsects * sizeof(section_32_t))
576 return VERR_BAD_EXE_FORMAT;
577 if (!dbgDiggerDarwinIsValidSegOrSectName(uLCmd.pSeg32->segname, sizeof(uLCmd.pSeg32->segname)))
578 return VERR_INVALID_NAME;
579 if ( !strcmp(uLCmd.pSeg32->segname, "__LINKEDIT")
580 && !(fHasLinkEdit = dbgDiggerDarwinIsSegmentPresent(pUVM, uLCmd.pSeg32->vmaddr, uLCmd.pSeg32->vmsize,
581 uModAddr, uModAddr + _64M)))
582 break; /* This usually is discarded or not loaded at all. */
583 if (cSegs >= RT_ELEMENTS(aSegs))
584 return VERR_BUFFER_OVERFLOW;
585 aSegs[cSegs].Address = uLCmd.pSeg32->vmaddr;
586 aSegs[cSegs].uRva = uLCmd.pSeg32->vmaddr - uModAddr;
587 aSegs[cSegs].cb = uLCmd.pSeg32->vmsize;
588 aSegs[cSegs].fFlags = uLCmd.pSeg32->flags; /* Abusing the flags field here... */
589 aSegs[cSegs].iSeg = cSegs;
590 AssertCompile(RTDBG_SEGMENT_NAME_LENGTH > sizeof(uLCmd.pSeg32->segname));
591 strcpy(aSegs[cSegs].szName, uLCmd.pSeg32->segname);
592 cSegs++;
593 break;
594
595 case LC_SEGMENT_64:
596 if (cbCmd != sizeof(segment_command_64_t) + uLCmd.pSeg64->nsects * sizeof(section_64_t))
597 return VERR_BAD_EXE_FORMAT;
598 if (!dbgDiggerDarwinIsValidSegOrSectName(uLCmd.pSeg64->segname, sizeof(uLCmd.pSeg64->segname)))
599 return VERR_INVALID_NAME;
600 if ( !strcmp(uLCmd.pSeg64->segname, "__LINKEDIT")
601 && !(fHasLinkEdit = dbgDiggerDarwinIsSegmentPresent(pUVM, uLCmd.pSeg64->vmaddr, uLCmd.pSeg64->vmsize,
602 uModAddr, uModAddr + _128M)))
603 break; /* This usually is discarded or not loaded at all. */
604 if (cSegs >= RT_ELEMENTS(aSegs))
605 return VERR_BUFFER_OVERFLOW;
606 aSegs[cSegs].Address = uLCmd.pSeg64->vmaddr;
607 aSegs[cSegs].uRva = uLCmd.pSeg64->vmaddr - uModAddr;
608 aSegs[cSegs].cb = uLCmd.pSeg64->vmsize;
609 aSegs[cSegs].fFlags = uLCmd.pSeg64->flags; /* Abusing the flags field here... */
610 aSegs[cSegs].iSeg = cSegs;
611 AssertCompile(RTDBG_SEGMENT_NAME_LENGTH > sizeof(uLCmd.pSeg64->segname));
612 strcpy(aSegs[cSegs].szName, uLCmd.pSeg64->segname);
613 cSegs++;
614 break;
615
616 case LC_UUID:
617 if (cbCmd != sizeof(uuid_command_t))
618 return VERR_BAD_EXE_FORMAT;
619 if (RTUuidIsNull((PCRTUUID)&uLCmd.pUuid->uuid[0]))
620 return VERR_BAD_EXE_FORMAT;
621 memcpy(&Uuid, &uLCmd.pUuid->uuid[0], sizeof(uLCmd.pUuid->uuid));
622 break;
623
624 default:
625 /* Current known max plus a lot of slack. */
626 if (uLCmd.pGenric->cmd > LC_DYLIB_CODE_SIGN_DRS + 32)
627 return VERR_BAD_EXE_FORMAT;
628 break;
629 }
630
631 /* next */
632 cbLeft -= cbCmd;
633 uLCmd.pb += cbCmd;
634 }
635
636 if (cbLeft != 0)
637 {
638 LogRel(("OSXDig: uModAddr=%#RX64 - %u bytes of command left over!\n", uModAddr, cbLeft));
639 return VERR_BAD_EXE_FORMAT;
640 }
641
642 /*
643 * Some post processing checks.
644 */
645 uint32_t iSeg;
646 for (iSeg = 0; iSeg < cSegs; iSeg++)
647 if (aSegs[iSeg].Address == uModAddr)
648 break;
649 if (iSeg >= cSegs)
650 {
651 LogRel2(("OSXDig: uModAddr=%#RX64 was not found among the segments segments\n", uModAddr));
652 return VERR_ADDRESS_CONFLICT;
653 }
654
655 /*
656 * Create a debug module.
657 */
658 RTDBGMOD hMod;
659 rc = RTDbgModCreateFromMachOImage(&hMod, pszName, NULL, f64Bit ? RTLDRARCH_AMD64 : RTLDRARCH_X86_32, 0 /*cbImage*/,
660 cSegs, aSegs, &Uuid, DBGFR3AsGetConfig(pUVM),
661 RTDBGMOD_F_NOT_DEFERRED | (fHasLinkEdit ? RTDBGMOD_F_MACHO_LOAD_LINKEDIT : 0));
662
663 if (RT_FAILURE(rc))
664 {
665 /** @todo try open in memory. */
666
667 /*
668 * Final fallback is a container module.
669 */
670 rc = RTDbgModCreate(&hMod, pszName, 0, 0);
671 if (RT_FAILURE(rc))
672 return rc;
673
674 uint64_t uRvaNext = 0;
675 for (iSeg = 0; iSeg < cSegs && RT_SUCCESS(rc); iSeg++)
676 {
677 if ( aSegs[iSeg].uRva > uRvaNext
678 && aSegs[iSeg].uRva - uRvaNext < _1M)
679 uRvaNext = aSegs[iSeg].uRva;
680 rc = RTDbgModSegmentAdd(hMod, aSegs[iSeg].uRva, aSegs[iSeg].cb, aSegs[iSeg].szName, 0, NULL);
681 if (aSegs[iSeg].cb > 0 && RT_SUCCESS(rc))
682 {
683 char szTmp[RTDBG_SEGMENT_NAME_LENGTH + sizeof("_start")];
684 strcat(strcpy(szTmp, aSegs[iSeg].szName), "_start");
685 rc = RTDbgModSymbolAdd(hMod, szTmp, iSeg, 0 /*uRva*/, 0 /*cb*/, 0 /*fFlags*/, NULL);
686 }
687 uRvaNext += aSegs[iSeg].cb;
688 }
689
690 if (RT_FAILURE(rc))
691 {
692 RTDbgModRelease(hMod);
693 return rc;
694 }
695 }
696
697 /* Tag the module. */
698 rc = RTDbgModSetTag(hMod, DIG_DARWIN_MOD_TAG);
699 AssertRC(rc);
700
701 /*
702 * Link the module.
703 */
704 RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
705 if (hAs != NIL_RTDBGAS)
706 {
707 //uint64_t uRvaNext = 0; - what was this?
708 uint32_t cLinked = 0;
709 iSeg = cSegs;
710 while (iSeg-- > 0) /* HACK: Map in reverse order to avoid replacing __TEXT. */
711 if (aSegs[iSeg].cb)
712 {
713 /* Find matching segment in the debug module. */
714 uint32_t iDbgSeg = 0;
715 while (iDbgSeg < cSegs)
716 {
717 RTDBGSEGMENT SegInfo;
718 int rc3 = RTDbgModSegmentByIndex(hMod, iDbgSeg, &SegInfo);
719 if (RT_SUCCESS(rc3) && !strcmp(SegInfo.szName, aSegs[iSeg].szName))
720 break;
721 iDbgSeg++;
722 }
723 AssertMsgStmt(iDbgSeg < cSegs, ("%s\n", aSegs[iSeg].szName), continue);
724
725 /* Map it. */
726 int rc2 = RTDbgAsModuleLinkSeg(hAs, hMod, iDbgSeg, aSegs[iSeg].Address, RTDBGASLINK_FLAGS_REPLACE /*fFlags*/);
727 if (RT_SUCCESS(rc2))
728 cLinked++;
729 else if (RT_SUCCESS(rc))
730 rc = rc2;
731 }
732 if (RT_FAILURE(rc) && cLinked != 0)
733 rc = -rc;
734 }
735 else
736 rc = VERR_INTERNAL_ERROR;
737
738 RTDbgModRelease(hMod);
739 RTDbgAsRelease(hAs);
740
741 if (pf64Bit)
742 *pf64Bit = f64Bit;
743 return rc;
744}
745
746
747static bool dbgDiggerDarwinIsValidName(const char *pszName)
748{
749 char ch;
750 while ((ch = *pszName++) != '\0')
751 {
752 if (ch < 0x20 || ch >= 127)
753 return false;
754 }
755 return true;
756}
757
758
759static bool dbgDiggerDarwinIsValidVersion(const char *pszVersion)
760{
761 char ch;
762 while ((ch = *pszVersion++) != '\0')
763 {
764 if (ch < 0x20 || ch >= 127)
765 return false;
766 }
767 return true;
768}
769
770
771/**
772 * @copydoc DBGFOSREG::pfnInit
773 */
774static DECLCALLBACK(int) dbgDiggerDarwinInit(PUVM pUVM, void *pvData)
775{
776 PDBGDIGGERDARWIN pThis = (PDBGDIGGERDARWIN)pvData;
777 Assert(!pThis->fValid);
778
779 /*
780 * Add the kernel module.
781 */
782 bool f64Bit;
783 int rc = dbgDiggerDarwinAddModule(pThis, pUVM, pThis->AddrKernel.FlatPtr, "mach_kernel", &f64Bit);
784 if (RT_SUCCESS(rc))
785 {
786 /*
787 * The list of modules can be found at the 'kmod' symbol, that means
788 * that we currently require some kind of symbol file for the kernel
789 * to be loaded at this point.
790 *
791 * Note! Could also use the 'gLoadedKextSummaries', but I don't think
792 * it's any easier to find without any kernel map than 'kmod'.
793 */
794 RTDBGSYMBOL SymInfo;
795 rc = DBGFR3AsSymbolByName(pUVM, DBGF_AS_KERNEL, "mach_kernel!kmod", &SymInfo, NULL);
796 if (RT_FAILURE(rc))
797 rc = DBGFR3AsSymbolByName(pUVM, DBGF_AS_KERNEL, "mach_kernel!_kmod", &SymInfo, NULL);
798 if (RT_SUCCESS(rc))
799 {
800 DBGFADDRESS AddrModInfo;
801 DBGFR3AddrFromFlat(pUVM, &AddrModInfo, SymInfo.Value);
802
803 /* Read the variable. */
804 RTUINT64U uKmodValue = { 0 };
805 if (f64Bit)
806 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &AddrModInfo, &uKmodValue.u, sizeof(uKmodValue.u));
807 else
808 rc = DBGFR3MemRead (pUVM, 0 /*idCpu*/, &AddrModInfo, &uKmodValue.s.Lo, sizeof(uKmodValue.s.Lo));
809 if (RT_SUCCESS(rc))
810 {
811 DBGFR3AddrFromFlat(pUVM, &AddrModInfo, uKmodValue.u);
812
813 /* Walk the list of modules. */
814 uint32_t cIterations = 0;
815 while (AddrModInfo.FlatPtr != 0)
816 {
817 /* Some extra loop conditions... */
818 if (!OSX_VALID_ADDRESS(f64Bit, AddrModInfo.FlatPtr))
819 {
820 LogRel(("OSXDig: Invalid kmod_info pointer: %RGv\n", AddrModInfo.FlatPtr));
821 break;
822 }
823 if (AddrModInfo.FlatPtr == uKmodValue.u && cIterations != 0)
824 {
825 LogRel(("OSXDig: kmod_info list looped back to the start.\n"));
826 break;
827 }
828 if (cIterations++ >= 2048)
829 {
830 LogRel(("OSXDig: Too many mod_info loops (%u)\n", cIterations));
831 break;
832 }
833
834 /*
835 * Read the kmod_info_t structure.
836 */
837 union
838 {
839 OSX64_kmod_info_t Info64;
840 OSX32_kmod_info_t Info32;
841 } uMod;
842 RT_ZERO(uMod);
843 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &AddrModInfo, &uMod,
844 f64Bit ? sizeof(uMod.Info64) : sizeof(uMod.Info32));
845 if (RT_FAILURE(rc))
846 {
847 LogRel(("OSXDig: Error reading kmod_info structure at %RGv: %Rrc\n", AddrModInfo.FlatPtr, rc));
848 break;
849 }
850
851 /*
852 * Validate the kmod_info_t structure.
853 */
854 int32_t iInfoVer = f64Bit ? uMod.Info64.info_version : uMod.Info32.info_version;
855 if (iInfoVer != OSX_KMOD_INFO_VERSION)
856 {
857 LogRel(("OSXDig: kmod_info @%RGv: Bad info_version %d\n", AddrModInfo.FlatPtr, iInfoVer));
858 break;
859 }
860
861 const char *pszName = f64Bit ? uMod.Info64.name : uMod.Info32.name;
862 if ( !*pszName
863 || !RTStrEnd(pszName, sizeof(uMod.Info64.name))
864 || !dbgDiggerDarwinIsValidName(pszName) )
865 {
866 LogRel(("OSXDig: kmod_info @%RGv: Bad name '%.*s'\n", AddrModInfo.FlatPtr,
867 sizeof(uMod.Info64.name), pszName));
868 break;
869 }
870
871 const char *pszVersion = f64Bit ? uMod.Info64.version : uMod.Info32.version;
872 if ( !RTStrEnd(pszVersion, sizeof(uMod.Info64.version))
873 || !dbgDiggerDarwinIsValidVersion(pszVersion) )
874 {
875 LogRel(("OSXDig: kmod_info @%RGv: Bad version '%.*s'\n", AddrModInfo.FlatPtr,
876 sizeof(uMod.Info64.version), pszVersion));
877 break;
878 }
879
880 int32_t cRefs = f64Bit ? uMod.Info64.reference_count : uMod.Info32.reference_count;
881 if (cRefs < -1 || cRefs > 16384)
882 {
883 LogRel(("OSXDig: kmod_info @%RGv: Bad reference_count %d\n", AddrModInfo.FlatPtr, cRefs));
884 break;
885 }
886
887 uint64_t uImageAddr = f64Bit ? uMod.Info64.address : uMod.Info32.address;
888 if (!OSX_VALID_ADDRESS(f64Bit, uImageAddr))
889 {
890 LogRel(("OSXDig: kmod_info @%RGv: Bad address %#llx\n", AddrModInfo.FlatPtr, uImageAddr));
891 break;
892 }
893
894 uint64_t cbImage = f64Bit ? uMod.Info64.size : uMod.Info32.size;
895 if (cbImage > 64U*_1M)
896 {
897 LogRel(("OSXDig: kmod_info @%RGv: Bad size %#llx\n", AddrModInfo.FlatPtr, cbImage));
898 break;
899 }
900
901 uint64_t cbHdr = f64Bit ? uMod.Info64.hdr_size : uMod.Info32.hdr_size;
902 if (cbHdr > 16U*_1M)
903 {
904 LogRel(("OSXDig: kmod_info @%RGv: Bad hdr_size %#llx\n", AddrModInfo.FlatPtr, cbHdr));
905 break;
906 }
907
908 uint64_t uStartAddr = f64Bit ? uMod.Info64.start : uMod.Info32.start;
909 if (!uStartAddr && !OSX_VALID_ADDRESS(f64Bit, uStartAddr))
910 {
911 LogRel(("OSXDig: kmod_info @%RGv: Bad start function %#llx\n", AddrModInfo.FlatPtr, uStartAddr));
912 break;
913 }
914
915 uint64_t uStopAddr = f64Bit ? uMod.Info64.stop : uMod.Info32.stop;
916 if (!uStopAddr && !OSX_VALID_ADDRESS(f64Bit, uStopAddr))
917 {
918 LogRel(("OSXDig: kmod_info @%RGv: Bad stop function %#llx\n", AddrModInfo.FlatPtr, uStopAddr));
919 break;
920 }
921
922 /*
923 * Try add the module.
924 */
925 LogRel(("OSXDig: kmod_info @%RGv: '%s' ver '%s', image @%#llx LB %#llx cbHdr=%#llx\n", AddrModInfo.FlatPtr,
926 pszName, pszVersion, uImageAddr, cbImage, cbHdr));
927 rc = dbgDiggerDarwinAddModule(pThis, pUVM, uImageAddr, pszName, NULL);
928
929
930 /*
931 * Advance to the next kmod_info entry.
932 */
933 DBGFR3AddrFromFlat(pUVM, &AddrModInfo, f64Bit ? uMod.Info64.next : uMod.Info32.next);
934 }
935 }
936 else
937 LogRel(("OSXDig: Error reading the 'kmod' variable: %Rrc\n", rc));
938 }
939 else
940 LogRel(("OSXDig: Failed to locate the 'kmod' variable in mach_kernel.\n"));
941
942 pThis->fValid = true;
943 return VINF_SUCCESS;
944 }
945
946 return rc;
947}
948
949
950/**
951 * @copydoc DBGFOSREG::pfnProbe
952 */
953static DECLCALLBACK(bool) dbgDiggerDarwinProbe(PUVM pUVM, void *pvData)
954{
955 PDBGDIGGERDARWIN pThis = (PDBGDIGGERDARWIN)pvData;
956
957 /*
958 * Look for a section + segment combo that normally only occures in
959 * mach_kernel. Follow it up with probing of the rest of the executable
960 * header. We must search a largish area because the more recent versions
961 * of darwin have random load address for security raisins.
962 */
963 static struct { uint64_t uStart, uEnd; } const s_aRanges[] =
964 {
965 /* 64-bit: */
966 { UINT64_C(0xffffff8000000000), UINT64_C(0xffffff81ffffffff), },
967
968 /* 32-bit - always search for this because of the hybrid 32-bit kernel
969 with cpu in long mode that darwin used for a number of versions. */
970 { UINT64_C(0x00001000), UINT64_C(0x0ffff000), }
971 };
972 for (unsigned iRange = DBGFR3CpuGetMode(pUVM, 0 /*idCpu*/) != CPUMMODE_LONG;
973 iRange < RT_ELEMENTS(s_aRanges);
974 iRange++)
975 {
976 DBGFADDRESS KernelAddr;
977 for (DBGFR3AddrFromFlat(pUVM, &KernelAddr, s_aRanges[iRange].uStart);
978 KernelAddr.FlatPtr < s_aRanges[iRange].uEnd;
979 KernelAddr.FlatPtr += X86_PAGE_4K_SIZE)
980 {
981 static const uint8_t s_abNeedle[16 + 16] =
982 {
983 '_','_','t','e','x','t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* section_32_t::sectname */
984 '_','_','K','L','D', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* section_32_t::segname. */
985 };
986
987 int rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &KernelAddr, s_aRanges[iRange].uEnd - KernelAddr.FlatPtr,
988 1, s_abNeedle, sizeof(s_abNeedle), &KernelAddr);
989 if (RT_FAILURE(rc))
990 break;
991 DBGFR3AddrSub(&KernelAddr, KernelAddr.FlatPtr & X86_PAGE_4K_OFFSET_MASK);
992
993 /*
994 * Read the first page of the image and check the headers.
995 */
996 union
997 {
998 uint8_t ab[X86_PAGE_4K_SIZE];
999 mach_header_64_t Hdr64;
1000 mach_header_32_t Hdr32;
1001 } uBuf;
1002 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &KernelAddr, uBuf.ab, X86_PAGE_4K_SIZE);
1003 if (RT_FAILURE(rc))
1004 continue;
1005 AssertCompileMembersSameSizeAndOffset(mach_header_64_t, magic, mach_header_32_t, magic);
1006 if ( uBuf.Hdr64.magic != IMAGE_MACHO64_SIGNATURE
1007 && uBuf.Hdr32.magic != IMAGE_MACHO32_SIGNATURE)
1008 continue;
1009 AssertCompileMembersSameSizeAndOffset(mach_header_64_t, cputype, mach_header_32_t, cputype);
1010 bool f64Bit = uBuf.Hdr64.magic == IMAGE_MACHO64_SIGNATURE;
1011 if (uBuf.Hdr32.cputype != (f64Bit ? CPU_TYPE_X86_64 : CPU_TYPE_I386))
1012 continue;
1013 AssertCompileMembersSameSizeAndOffset(mach_header_64_t, filetype, mach_header_32_t, filetype);
1014 if (uBuf.Hdr32.filetype != MH_EXECUTE)
1015 continue;
1016 AssertCompileMembersSameSizeAndOffset(mach_header_64_t, ncmds, mach_header_32_t, ncmds);
1017 if (uBuf.Hdr32.ncmds > 256)
1018 continue;
1019 AssertCompileMembersSameSizeAndOffset(mach_header_64_t, sizeofcmds, mach_header_32_t, sizeofcmds);
1020 if (uBuf.Hdr32.sizeofcmds > X86_PAGE_4K_SIZE * 2 - sizeof(mach_header_64_t))
1021 continue;
1022
1023 /* Seems good enough for now.
1024
1025 If the above causes false positives, check the segments and make
1026 sure there is a kernel version string in the right one. */
1027 pThis->AddrKernel = KernelAddr;
1028 pThis->f64Bit = f64Bit;
1029
1030 /*
1031 * Finally, find the kernel version string.
1032 */
1033 rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &KernelAddr, 32*_1M, 1, RT_STR_TUPLE("Darwin Kernel Version"),
1034 &pThis->AddrKernelVersion);
1035 if (RT_FAILURE(rc))
1036 DBGFR3AddrFromFlat(pUVM, &pThis->AddrKernelVersion, 0);
1037 return true;
1038 }
1039 }
1040 return false;
1041}
1042
1043
1044/**
1045 * @copydoc DBGFOSREG::pfnDestruct
1046 */
1047static DECLCALLBACK(void) dbgDiggerDarwinDestruct(PUVM pUVM, void *pvData)
1048{
1049 RT_NOREF2(pUVM, pvData);
1050
1051}
1052
1053
1054/**
1055 * @copydoc DBGFOSREG::pfnConstruct
1056 */
1057static DECLCALLBACK(int) dbgDiggerDarwinConstruct(PUVM pUVM, void *pvData)
1058{
1059 RT_NOREF1(pUVM);
1060 PDBGDIGGERDARWIN pThis = (PDBGDIGGERDARWIN)pvData;
1061
1062 pThis->IDmesg.u32Magic = DBGFOSIDMESG_MAGIC;
1063 pThis->IDmesg.pfnQueryKernelLog = dbgDiggerDarwinIDmsg_QueryKernelLog;
1064 pThis->IDmesg.u32EndMagic = DBGFOSIDMESG_MAGIC;
1065
1066 return VINF_SUCCESS;
1067}
1068
1069
1070const DBGFOSREG g_DBGDiggerDarwin =
1071{
1072 /* .u32Magic = */ DBGFOSREG_MAGIC,
1073 /* .fFlags = */ 0,
1074 /* .cbData = */ sizeof(DBGDIGGERDARWIN),
1075 /* .szName = */ "Darwin",
1076 /* .pfnConstruct = */ dbgDiggerDarwinConstruct,
1077 /* .pfnDestruct = */ dbgDiggerDarwinDestruct,
1078 /* .pfnProbe = */ dbgDiggerDarwinProbe,
1079 /* .pfnInit = */ dbgDiggerDarwinInit,
1080 /* .pfnRefresh = */ dbgDiggerDarwinRefresh,
1081 /* .pfnTerm = */ dbgDiggerDarwinTerm,
1082 /* .pfnQueryVersion = */ dbgDiggerDarwinQueryVersion,
1083 /* .pfnQueryInterface = */ dbgDiggerDarwinQueryInterface,
1084 /* .pfnStackUnwindAssist = */ dbgDiggerDarwinStackUnwindAssist,
1085 /* .u32EndMagic = */ DBGFOSREG_MAGIC
1086};
1087
Note: See TracBrowser for help on using the repository browser.

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