VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/ldr/ldrMachO.cpp@ 74750

Last change on this file since 74750 was 74750, checked in by vboxsync, 6 years ago

IPRT/ldr: More Mach-O signing hacking. bugref:9232

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 202.3 KB
Line 
1/* $Id: ldrMachO.cpp 74750 2018-10-10 17:42:48Z vboxsync $ */
2/** @file
3 * kLdr - The Module Interpreter for the MACH-O format.
4 */
5
6/*
7 * Copyright (C) 2018 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 * --------------------------------------------------------------------
26 *
27 * This code is based on:
28 *
29 * Copyright (c) 2006-2013 Knut St. Osmundsen <[email protected]>
30 *
31 * Permission is hereby granted, free of charge, to any person
32 * obtaining a copy of this software and associated documentation
33 * files (the "Software"), to deal in the Software without
34 * restriction, including without limitation the rights to use,
35 * copy, modify, merge, publish, distribute, sublicense, and/or sell
36 * copies of the Software, and to permit persons to whom the
37 * Software is furnished to do so, subject to the following
38 * conditions:
39 *
40 * The above copyright notice and this permission notice shall be
41 * included in all copies or substantial portions of the Software.
42 *
43 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
44 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
45 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
46 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
47 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
48 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
49 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
50 * OTHER DEALINGS IN THE SOFTWARE.
51 */
52
53
54/*********************************************************************************************************************************
55* Header Files *
56*********************************************************************************************************************************/
57#define LOG_GROUP RTLOGGROUP_LDR
58#include <iprt/ldr.h>
59#include "internal/iprt.h"
60
61#include <iprt/asm.h>
62#include <iprt/assert.h>
63#include <iprt/base64.h>
64#include <iprt/ctype.h>
65#include <iprt/err.h>
66#include <iprt/log.h>
67#include <iprt/mem.h>
68#include <iprt/string.h>
69#include <iprt/sha.h>
70#include <iprt/crypto/digest.h>
71
72#include <iprt/formats/mach-o.h>
73#include <iprt/crypto/applecodesign.h>
74#include "internal/ldr.h"
75
76
77/*********************************************************************************************************************************
78* Defined Constants And Macros *
79*********************************************************************************************************************************/
80/** @def RTLDRMODMACHO_STRICT
81 * Define RTLDRMODMACHO_STRICT to enabled strict checks in RTLDRMODMACHO. */
82#define RTLDRMODMACHO_STRICT 1
83
84/** @def RTLDRMODMACHO_ASSERT
85 * Assert that an expression is true when KLDR_STRICT is defined.
86 */
87#ifdef RTLDRMODMACHO_STRICT
88# define RTLDRMODMACHO_ASSERT(expr) Assert(expr)
89#else
90# define RTLDRMODMACHO_ASSERT(expr) do {} while (0)
91#endif
92
93/** @def RTLDRMODMACHO_CHECK_RETURN
94 * Checks that an expression is true and return if it isn't.
95 * This is a debug aid.
96 */
97#ifdef RTLDRMODMACHO_STRICT2
98# define RTLDRMODMACHO_CHECK_RETURN(expr, rc) AssertReturn(expr, rc)
99#else
100# define RTLDRMODMACHO_CHECK_RETURN(expr, rc) do { if (!(expr)) { return (rc); } } while (0)
101#endif
102
103/** @def RTLDRMODMACHO_CHECK_RETURN
104 * Checks that an expression is true and return if it isn't.
105 * This is a debug aid.
106 */
107#ifdef RTLDRMODMACHO_STRICT2
108# define RTLDRMODMACHO_FAILED_RETURN(rc) AssertFailedReturn(rc)
109#else
110# define RTLDRMODMACHO_FAILED_RETURN(rc) return (rc)
111#endif
112
113
114/*********************************************************************************************************************************
115* Structures and Typedefs *
116*********************************************************************************************************************************/
117/**
118 * Mach-O section details.
119 */
120typedef struct RTLDRMODMACHOSECT
121{
122 /** The size of the section (in bytes). */
123 RTLDRADDR cb;
124 /** The link address of this section. */
125 RTLDRADDR LinkAddress;
126 /** The RVA of this section. */
127 RTLDRADDR RVA;
128 /** The file offset of this section.
129 * This is -1 if the section doesn't have a file backing. */
130 RTFOFF offFile;
131 /** The number of fixups. */
132 uint32_t cFixups;
133 /** The array of fixups. (lazy loaded) */
134 macho_relocation_info_t *paFixups;
135 /** The file offset of the fixups for this section.
136 * This is -1 if the section doesn't have any fixups. */
137 RTFOFF offFixups;
138 /** Mach-O section flags. */
139 uint32_t fFlags;
140 /** kLdr segment index. */
141 uint32_t iSegment;
142 /** Pointer to the Mach-O section structure. */
143 void *pvMachoSection;
144} RTLDRMODMACHOSECT, *PRTLDRMODMACHOSECT;
145
146/**
147 * Extra per-segment info.
148 *
149 * This is corresponds to a kLdr segment, not a Mach-O segment!
150 */
151typedef struct RTLDRMODMACHOSEG
152{
153 /** Common segment info. */
154 RTLDRSEG SegInfo;
155
156 /** The orignal segment number (in case we had to resort it). */
157 uint32_t iOrgSegNo;
158 /** The number of sections in the segment. */
159 uint32_t cSections;
160 /** Pointer to the sections belonging to this segment.
161 * The array resides in the big memory chunk allocated for
162 * the module handle, so it doesn't need freeing. */
163 PRTLDRMODMACHOSECT paSections;
164
165} RTLDRMODMACHOSEG, *PRTLDRMODMACHOSEG;
166
167/**
168 * Instance data for the Mach-O MH_OBJECT module interpreter.
169 * @todo interpret the other MH_* formats.
170 */
171typedef struct RTLDRMODMACHO
172{
173 /** Core module structure. */
174 RTLDRMODINTERNAL Core;
175
176 /** The minium cpu this module was built for.
177 * This might not be accurate, so use kLdrModCanExecuteOn() to check. */
178 RTLDRCPU enmCpu;
179 /** The number of segments in the module. */
180 uint32_t cSegments;
181
182 /** Pointer to the RDR file mapping of the raw file bits. NULL if not mapped. */
183 const void *pvBits;
184 /** Pointer to the user mapping. */
185 void *pvMapping;
186 /** The module open flags. */
187 uint32_t fOpenFlags;
188
189 /** The offset of the image. (FAT fun.) */
190 RTFOFF offImage;
191 /** The link address. */
192 RTLDRADDR LinkAddress;
193 /** The size of the mapped image. */
194 RTLDRADDR cbImage;
195 /** Whether we're capable of loading the image. */
196 bool fCanLoad;
197 /** Whether we're creating a global offset table segment.
198 * This dependes on the cputype and image type. */
199 bool fMakeGot;
200 /** The size of a indirect GOT jump stub entry.
201 * This is 0 if not needed. */
202 uint32_t cbJmpStub;
203 /** Effective file type. If the original was a MH_OBJECT file, the
204 * corresponding MH_DSYM needs the segment translation of a MH_OBJECT too.
205 * The MH_DSYM normally has a separate __DWARF segment, but this is
206 * automatically skipped during the transation. */
207 uint32_t uEffFileType;
208 /** Pointer to the load commands. (endian converted) */
209 uint8_t *pbLoadCommands;
210 /** The Mach-O header. (endian converted)
211 * @remark The reserved field is only valid for real 64-bit headers. */
212 mach_header_64_t Hdr;
213
214 /** The offset of the symbol table. */
215 RTFOFF offSymbols;
216 /** The number of symbols. */
217 uint32_t cSymbols;
218 /** The pointer to the loaded symbol table. */
219 void *pvaSymbols;
220 /** The offset of the string table. */
221 RTFOFF offStrings;
222 /** The size of the of the string table. */
223 uint32_t cchStrings;
224 /** Pointer to the loaded string table. */
225 char *pchStrings;
226
227 /** The image UUID, all zeros if not found. */
228 uint8_t abImageUuid[16];
229
230 /** The code signature offset. */
231 uint32_t offCodeSignature;
232 /** The code signature size (0 if not signed). */
233 uint32_t cbCodeSignature;
234 /** Pointer to the code signature blob if loaded. */
235 union
236 {
237 uint8_t *pb;
238 PCRTCRAPLCSSUPERBLOB pSuper;
239 } PtrCodeSignature;
240 /** File offset of segment 0 (relative to Mach-O header). */
241 uint64_t offSeg0ForCodeSign;
242 /** File size of segment 0. */
243 uint64_t cbSeg0ForCodeSign;
244 /** Segment 0 flags. */
245 uint64_t fSeg0ForCodeSign;
246
247 /** The RVA of the Global Offset Table. */
248 RTLDRADDR GotRVA;
249 /** The RVA of the indirect GOT jump stubs. */
250 RTLDRADDR JmpStubsRVA;
251
252 /** The number of sections. */
253 uint32_t cSections;
254 /** Pointer to the section array running in parallel to the Mach-O one. */
255 PRTLDRMODMACHOSECT paSections;
256
257 /** Array of segments parallel to the one in KLDRMOD. */
258 RTLDRMODMACHOSEG aSegments[1];
259} RTLDRMODMACHO;
260/** Pointer instance data for an Mach-O module. */
261typedef RTLDRMODMACHO *PRTLDRMODMACHO;
262
263/**
264 * Code directory data.
265 */
266typedef struct RTLDRMACHCODEDIR
267{
268 PCRTCRAPLCSCODEDIRECTORY pCodeDir;
269 /** The slot type. */
270 uint32_t uSlot;
271 /** The naturalized size. */
272 uint32_t cb;
273 /** The digest type. */
274 RTDIGESTTYPE enmDigest;
275} RTLDRMACHCODEDIR;
276/** Pointer to code directory data. */
277typedef RTLDRMACHCODEDIR *PRTLDRMACHCODEDIR;
278
279/**
280 * Decoded apple Mach-O signature data.
281 * @note The raw signature data lives in RTLDRMODMACHO::PtrCodeSignature.
282 */
283typedef struct RTLDRMACHOSIGNATURE
284{
285 /** Number of code directory slots. */
286 uint32_t cCodeDirs;
287 /** Code directories. */
288 RTLDRMACHCODEDIR aCodeDirs[6];
289
290 /** The index of the PKCS#7 slot. */
291 uint32_t idxPkcs7;
292 /** The size of the PKCS#7 data. */
293 uint32_t cbPkcs7;
294 /** Pointer to the PKCS#7 data. */
295 uint8_t const *pbPkcs7;
296 /** Parsed PKCS#7 data. */
297 RTCRPKCS7CONTENTINFO ContentInfo;
298 /** Pointer to the decoded SignedData inside the ContentInfo member. */
299 PRTCRPKCS7SIGNEDDATA pSignedData;
300} RTLDRMACHOSIGNATURE;
301/** Pointer to decoded apple code signing data. */
302typedef RTLDRMACHOSIGNATURE *PRTLDRMACHOSIGNATURE;
303
304
305
306/*********************************************************************************************************************************
307* Internal Functions *
308*********************************************************************************************************************************/
309#if 0
310static int32_t kldrModMachONumberOfImports(PRTLDRMODINTERNAL pMod, const void *pvBits);
311#endif
312static DECLCALLBACK(int) rtldrMachO_RelocateBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
313 RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser);
314
315
316static int kldrModMachOPreParseLoadCommands(uint8_t *pbLoadCommands, const mach_header_32_t *pHdr, PRTLDRREADER pRdr, RTFOFF offImage,
317 uint32_t fOpenFlags, uint32_t *pcSegments, uint32_t *pcSections, uint32_t *pcbStringPool,
318 bool *pfCanLoad, PRTLDRADDR pLinkAddress, uint8_t *puEffFileType, PRTERRINFO pErrInfo);
319static int kldrModMachOParseLoadCommands(PRTLDRMODMACHO pThis, char *pbStringPool, uint32_t cbStringPool);
320
321static int kldrModMachOLoadObjSymTab(PRTLDRMODMACHO pThis);
322static int kldrModMachOLoadFixups(PRTLDRMODMACHO pThis, RTFOFF offFixups, uint32_t cFixups, macho_relocation_info_t **ppaFixups);
323static int kldrModMachOMapVirginBits(PRTLDRMODMACHO pThis);
324
325static int kldrModMachODoQuerySymbol32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms, const char *pchStrings,
326 uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol, const char *pchSymbol,
327 uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind);
328static int kldrModMachODoQuerySymbol64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms, const char *pchStrings,
329 uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol, const char *pchSymbol,
330 uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind);
331static int kldrModMachODoEnumSymbols32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
332 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
333 uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser);
334static int kldrModMachODoEnumSymbols64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
335 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
336 uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser);
337static int kldrModMachOObjDoImports(PRTLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser);
338static int kldrModMachOObjDoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress);
339static int kldrModMachOFixupSectionGeneric32Bit(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, PRTLDRMODMACHOSECT pFixupSect,
340 macho_nlist_32_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress);
341static int kldrModMachOFixupSectionAMD64(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, PRTLDRMODMACHOSECT pFixupSect,
342 macho_nlist_64_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress);
343
344static int kldrModMachOMakeGOT(PRTLDRMODMACHO pThis, void *pvBits, RTLDRADDR NewBaseAddress);
345
346/*static int kldrModMachODoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress, RTLDRADDR OldBaseAddress);
347static int kldrModMachODoImports(PRTLDRMODMACHO pThis, void *pvMapping, PFNRTLDRIMPORT pfnGetImport, void *pvUser);*/
348
349
350
351/**
352 * Separate function for reading creating the Mach-O module instance to
353 * simplify cleanup on failure.
354 */
355static int kldrModMachODoCreate(PRTLDRREADER pRdr, RTFOFF offImage, uint32_t fOpenFlags,
356 PRTLDRMODMACHO *ppModMachO, PRTERRINFO pErrInfo)
357{
358 *ppModMachO = NULL;
359
360 /*
361 * Read the Mach-O header.
362 */
363 union
364 {
365 mach_header_32_t Hdr32;
366 mach_header_64_t Hdr64;
367 } s;
368 Assert(&s.Hdr32.magic == &s.Hdr64.magic);
369 Assert(&s.Hdr32.flags == &s.Hdr64.flags);
370 int rc = pRdr->pfnRead(pRdr, &s, sizeof(s), offImage);
371 if (rc)
372 return RTErrInfoSetF(pErrInfo, rc, "Error reading Mach-O header at %RTfoff: %Rrc", offImage, rc);
373 if ( s.Hdr32.magic != IMAGE_MACHO32_SIGNATURE
374 && s.Hdr32.magic != IMAGE_MACHO64_SIGNATURE)
375 {
376 if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE
377 || s.Hdr32.magic == IMAGE_MACHO64_SIGNATURE_OE)
378 return VERR_LDRMACHO_OTHER_ENDIAN_NOT_SUPPORTED;
379 return VERR_INVALID_EXE_SIGNATURE;
380 }
381
382 /* sanity checks. */
383 if ( s.Hdr32.sizeofcmds > pRdr->pfnSize(pRdr) - sizeof(mach_header_32_t)
384 || s.Hdr32.sizeofcmds < sizeof(load_command_t) * s.Hdr32.ncmds
385 || (s.Hdr32.flags & ~MH_VALID_FLAGS))
386 return VERR_LDRMACHO_BAD_HEADER;
387
388 bool fMakeGot;
389 uint8_t cbJmpStub;
390 switch (s.Hdr32.cputype)
391 {
392 case CPU_TYPE_X86:
393 fMakeGot = false;
394 cbJmpStub = 0;
395 break;
396 case CPU_TYPE_X86_64:
397 fMakeGot = s.Hdr32.filetype == MH_OBJECT;
398 cbJmpStub = fMakeGot ? 8 : 0;
399 break;
400 default:
401 return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
402 }
403
404 if ( s.Hdr32.filetype != MH_OBJECT
405 && s.Hdr32.filetype != MH_EXECUTE
406 && s.Hdr32.filetype != MH_DYLIB
407 && s.Hdr32.filetype != MH_BUNDLE
408 && s.Hdr32.filetype != MH_DSYM
409 && s.Hdr32.filetype != MH_KEXT_BUNDLE)
410 return VERR_LDRMACHO_UNSUPPORTED_FILE_TYPE;
411
412 /*
413 * Read and pre-parse the load commands to figure out how many segments we'll be needing.
414 */
415 uint8_t *pbLoadCommands = (uint8_t *)RTMemAlloc(s.Hdr32.sizeofcmds);
416 if (!pbLoadCommands)
417 return VERR_NO_MEMORY;
418 rc = pRdr->pfnRead(pRdr, pbLoadCommands, s.Hdr32.sizeofcmds,
419 s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE
420 || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE
421 ? sizeof(mach_header_32_t) + offImage
422 : sizeof(mach_header_64_t) + offImage);
423
424 uint32_t cSegments = 0;
425 uint32_t cSections = 0;
426 uint32_t cbStringPool = 0;
427 bool fCanLoad = true;
428 RTLDRADDR LinkAddress = NIL_RTLDRADDR;
429 uint8_t uEffFileType = 0;
430 if (RT_SUCCESS(rc))
431 rc = kldrModMachOPreParseLoadCommands(pbLoadCommands, &s.Hdr32, pRdr, offImage, fOpenFlags,
432 &cSegments, &cSections, &cbStringPool, &fCanLoad, &LinkAddress, &uEffFileType,
433 pErrInfo);
434 if (RT_FAILURE(rc))
435 {
436 RTMemFree(pbLoadCommands);
437 return rc;
438 }
439 cSegments += fMakeGot;
440
441
442 /*
443 * Calc the instance size, allocate and initialize it.
444 */
445 size_t const cbModAndSegs = RT_ALIGN_Z(RT_UOFFSETOF_DYN(RTLDRMODMACHO, aSegments[cSegments])
446 + sizeof(RTLDRMODMACHOSECT) * cSections, 16);
447 PRTLDRMODMACHO pThis = (PRTLDRMODMACHO)RTMemAlloc(cbModAndSegs + cbStringPool);
448 if (!pThis)
449 return VERR_NO_MEMORY;
450 *ppModMachO = pThis;
451 pThis->pbLoadCommands = pbLoadCommands;
452 pThis->offImage = offImage;
453
454 /* Core & CPU.*/
455 pThis->Core.u32Magic = 0; /* set by caller */
456 pThis->Core.eState = LDR_STATE_OPENED;
457 pThis->Core.pOps = NULL; /* set by caller. */
458 pThis->Core.pReader = pRdr;
459 switch (s.Hdr32.cputype)
460 {
461 case CPU_TYPE_X86:
462 pThis->Core.enmArch = RTLDRARCH_X86_32;
463 pThis->Core.enmEndian = RTLDRENDIAN_LITTLE;
464 switch (s.Hdr32.cpusubtype)
465 {
466 case CPU_SUBTYPE_I386_ALL: /* == CPU_SUBTYPE_386 */
467 pThis->enmCpu = RTLDRCPU_X86_32_BLEND;
468 break;
469 case CPU_SUBTYPE_486:
470 pThis->enmCpu = RTLDRCPU_I486;
471 break;
472 case CPU_SUBTYPE_486SX:
473 pThis->enmCpu = RTLDRCPU_I486SX;
474 break;
475 case CPU_SUBTYPE_PENT: /* == CPU_SUBTYPE_586 */
476 pThis->enmCpu = RTLDRCPU_I586;
477 break;
478 case CPU_SUBTYPE_PENTPRO:
479 case CPU_SUBTYPE_PENTII_M3:
480 case CPU_SUBTYPE_PENTII_M5:
481 case CPU_SUBTYPE_CELERON:
482 case CPU_SUBTYPE_CELERON_MOBILE:
483 case CPU_SUBTYPE_PENTIUM_3:
484 case CPU_SUBTYPE_PENTIUM_3_M:
485 case CPU_SUBTYPE_PENTIUM_3_XEON:
486 pThis->enmCpu = RTLDRCPU_I686;
487 break;
488 case CPU_SUBTYPE_PENTIUM_M:
489 case CPU_SUBTYPE_PENTIUM_4:
490 case CPU_SUBTYPE_PENTIUM_4_M:
491 case CPU_SUBTYPE_XEON:
492 case CPU_SUBTYPE_XEON_MP:
493 pThis->enmCpu = RTLDRCPU_P4;
494 break;
495
496 default:
497 /* Hack for kextutil output. */
498 if ( s.Hdr32.cpusubtype == 0
499 && s.Hdr32.filetype == MH_OBJECT)
500 break;
501 return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
502 }
503 break;
504
505 case CPU_TYPE_X86_64:
506 pThis->Core.enmArch = RTLDRARCH_AMD64;
507 pThis->Core.enmEndian = RTLDRENDIAN_LITTLE;
508 switch (s.Hdr32.cpusubtype & ~CPU_SUBTYPE_MASK)
509 {
510 case CPU_SUBTYPE_X86_64_ALL: pThis->enmCpu = RTLDRCPU_AMD64_BLEND; break;
511 default:
512 return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
513 }
514 break;
515
516 default:
517 return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
518 }
519
520 pThis->Core.enmFormat = RTLDRFMT_MACHO;
521 switch (s.Hdr32.filetype)
522 {
523 case MH_OBJECT: pThis->Core.enmType = RTLDRTYPE_OBJECT; break;
524 case MH_EXECUTE: pThis->Core.enmType = RTLDRTYPE_EXECUTABLE_FIXED; break;
525 case MH_DYLIB: pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
526 case MH_BUNDLE: pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
527 case MH_KEXT_BUNDLE:pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
528 case MH_DSYM: pThis->Core.enmType = RTLDRTYPE_DEBUG_INFO; break;
529 default:
530 return VERR_LDRMACHO_UNSUPPORTED_FILE_TYPE;
531 }
532
533 /* RTLDRMODMACHO */
534 pThis->cSegments = cSegments;
535 pThis->pvBits = NULL;
536 pThis->pvMapping = NULL;
537 pThis->fOpenFlags = fOpenFlags;
538 pThis->Hdr = s.Hdr64;
539 if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE
540 || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE)
541 pThis->Hdr.reserved = 0;
542 pThis->LinkAddress = LinkAddress;
543 pThis->cbImage = 0;
544 pThis->fCanLoad = fCanLoad;
545 pThis->fMakeGot = fMakeGot;
546 pThis->cbJmpStub = cbJmpStub;
547 pThis->uEffFileType = uEffFileType;
548 pThis->offSymbols = 0;
549 pThis->cSymbols = 0;
550 pThis->pvaSymbols = NULL;
551 pThis->offStrings = 0;
552 pThis->cchStrings = 0;
553 pThis->pchStrings = NULL;
554 memset(pThis->abImageUuid, 0, sizeof(pThis->abImageUuid));
555 pThis->offCodeSignature = 0;
556 pThis->cbCodeSignature = 0;
557 pThis->PtrCodeSignature.pb = NULL;
558 pThis->GotRVA = NIL_RTLDRADDR;
559 pThis->JmpStubsRVA = NIL_RTLDRADDR;
560 pThis->cSections = cSections;
561 pThis->paSections = (PRTLDRMODMACHOSECT)&pThis->aSegments[pThis->cSegments];
562
563 /*
564 * Setup the KLDRMOD segment array.
565 */
566 rc = kldrModMachOParseLoadCommands(pThis, (char *)pThis + cbModAndSegs, cbStringPool);
567
568 /*
569 * We're done.
570 */
571 return rc;
572}
573
574
575/**
576 * Converts, validates and preparses the load commands before we carve
577 * out the module instance.
578 *
579 * The conversion that's preformed is format endian to host endian. The
580 * preparsing has to do with segment counting, section counting and string pool
581 * sizing.
582 *
583 * Segment are created in two different ways, depending on the file type.
584 *
585 * For object files there is only one segment command without a given segment
586 * name. The sections inside that segment have different segment names and are
587 * not sorted by their segname attribute. We create one segment for each
588 * section, with the segment name being 'segname.sectname' in order to hopefully
589 * keep the names unique. Debug sections does not get segments.
590 *
591 * For non-object files, one kLdr segment is created for each Mach-O segment.
592 * Debug segments is not exposed by kLdr via the kLdr segment table, but via the
593 * debug enumeration callback API.
594 *
595 * @returns IPRT status code.
596 * @param pbLoadCommands The load commands to parse.
597 * @param pHdr The header.
598 * @param pRdr The file reader.
599 * @param offImage The image header (FAT fun).
600 * @param fOpenFlags RTLDR_O_XXX.
601 * @param pcSegments Where to store the segment count.
602 * @param pcSections Where to store the section count.
603 * @param pcbStringPool Where to store the string pool size.
604 * @param pfCanLoad Where to store the can-load-image indicator.
605 * @param pLinkAddress Where to store the image link address (i.e. the
606 * lowest segment address).
607 * @param puEffFileType Where to store the effective file type.
608 * @param pErrInfo Where to return additional error info. Optional.
609 */
610static int kldrModMachOPreParseLoadCommands(uint8_t *pbLoadCommands, const mach_header_32_t *pHdr, PRTLDRREADER pRdr,
611 RTFOFF offImage, uint32_t fOpenFlags, uint32_t *pcSegments, uint32_t *pcSections,
612 uint32_t *pcbStringPool, bool *pfCanLoad, PRTLDRADDR pLinkAddress,
613 uint8_t *puEffFileType, PRTERRINFO pErrInfo)
614{
615 union
616 {
617 uint8_t *pb;
618 load_command_t *pLoadCmd;
619 segment_command_32_t *pSeg32;
620 segment_command_64_t *pSeg64;
621 thread_command_t *pThread;
622 symtab_command_t *pSymTab;
623 uuid_command_t *pUuid;
624 } u;
625 const uint64_t cbFile = pRdr->pfnSize(pRdr) - offImage;
626 uint32_t cSegments = 0;
627 uint32_t cSections = 0;
628 size_t cbStringPool = 0;
629 uint32_t cLeft = pHdr->ncmds;
630 uint32_t cbLeft = pHdr->sizeofcmds;
631 uint8_t *pb = pbLoadCommands;
632 int cSegmentCommands = 0;
633 int cSymbolTabs = 0;
634 int fConvertEndian = pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
635 || pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE;
636 uint8_t uEffFileType = *puEffFileType = pHdr->filetype;
637
638 *pcSegments = 0;
639 *pcSections = 0;
640 *pcbStringPool = 0;
641 *pfCanLoad = true;
642 *pLinkAddress = ~(RTLDRADDR)0;
643
644 while (cLeft-- > 0)
645 {
646 u.pb = pb;
647
648 /*
649 * Convert and validate command header.
650 */
651 RTLDRMODMACHO_CHECK_RETURN(cbLeft >= sizeof(load_command_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
652 if (fConvertEndian)
653 {
654 u.pLoadCmd->cmd = RT_BSWAP_U32(u.pLoadCmd->cmd);
655 u.pLoadCmd->cmdsize = RT_BSWAP_U32(u.pLoadCmd->cmdsize);
656 }
657 RTLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize <= cbLeft, VERR_LDRMACHO_BAD_LOAD_COMMAND);
658 cbLeft -= u.pLoadCmd->cmdsize;
659 pb += u.pLoadCmd->cmdsize;
660
661 /*
662 * Convert endian if needed, parse and validate the command.
663 */
664 switch (u.pLoadCmd->cmd)
665 {
666 case LC_SEGMENT_32:
667 {
668 segment_command_32_t *pSrcSeg = (segment_command_32_t *)u.pLoadCmd;
669 section_32_t *pFirstSect = (section_32_t *)(pSrcSeg + 1);
670 section_32_t *pSect = pFirstSect;
671 uint32_t cSectionsLeft = pSrcSeg->nsects;
672 uint64_t offSect = 0;
673
674 /* Convert and verify the segment. */
675 RTLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_32_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
676 RTLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
677 || pHdr->magic == IMAGE_MACHO32_SIGNATURE, VERR_LDRMACHO_BIT_MIX);
678 if (fConvertEndian)
679 {
680 pSrcSeg->vmaddr = RT_BSWAP_U32(pSrcSeg->vmaddr);
681 pSrcSeg->vmsize = RT_BSWAP_U32(pSrcSeg->vmsize);
682 pSrcSeg->fileoff = RT_BSWAP_U32(pSrcSeg->fileoff);
683 pSrcSeg->filesize = RT_BSWAP_U32(pSrcSeg->filesize);
684 pSrcSeg->maxprot = RT_BSWAP_U32(pSrcSeg->maxprot);
685 pSrcSeg->initprot = RT_BSWAP_U32(pSrcSeg->initprot);
686 pSrcSeg->nsects = RT_BSWAP_U32(pSrcSeg->nsects);
687 pSrcSeg->flags = RT_BSWAP_U32(pSrcSeg->flags);
688 }
689
690 /* Validation code shared with the 64-bit variant. */
691#define VALIDATE_AND_ADD_SEGMENT(a_cBits) \
692 do { \
693 bool fSkipSeg = !strcmp(pSrcSeg->segname, "__DWARF") /* Note: Not for non-object files. */ \
694 || ( !strcmp(pSrcSeg->segname, "__CTF") /* Their CTF tool did/does weird things, */ \
695 && pSrcSeg->vmsize == 0) /* overlapping vmaddr and zero vmsize. */ \
696 || (cSectionsLeft > 0 && (pFirstSect->flags & S_ATTR_DEBUG)); \
697 \
698 /* MH_DSYM files for MH_OBJECT files must have MH_OBJECT segment translation. */ \
699 if ( uEffFileType == MH_DSYM \
700 && cSegmentCommands == 0 \
701 && pSrcSeg->segname[0] == '\0') \
702 *puEffFileType = uEffFileType = MH_OBJECT; \
703 \
704 RTLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize == 0 \
705 || ( pSrcSeg->fileoff <= cbFile \
706 && (uint64_t)pSrcSeg->fileoff + pSrcSeg->filesize <= cbFile), \
707 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
708 RTLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize <= pSrcSeg->vmsize \
709 || (fSkipSeg && !strcmp(pSrcSeg->segname, "__CTF") /* see above */), \
710 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
711 RTLDRMODMACHO_CHECK_RETURN(!(~pSrcSeg->maxprot & pSrcSeg->initprot), \
712 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
713 RTLDRMODMACHO_CHECK_RETURN(!(pSrcSeg->flags & ~(SG_HIGHVM | SG_FVMLIB | SG_NORELOC | SG_PROTECTED_VERSION_1)), \
714 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
715 RTLDRMODMACHO_CHECK_RETURN( pSrcSeg->nsects * sizeof(section_##a_cBits##_t) \
716 <= u.pLoadCmd->cmdsize - sizeof(segment_command_##a_cBits##_t), \
717 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
718 RTLDRMODMACHO_CHECK_RETURN( uEffFileType != MH_OBJECT \
719 || cSegmentCommands == 0 \
720 || ( cSegmentCommands == 1 \
721 && uEffFileType == MH_OBJECT \
722 && pHdr->filetype == MH_DSYM \
723 && fSkipSeg), \
724 VERR_LDRMACHO_BAD_OBJECT_FILE); \
725 cSegmentCommands++; \
726 \
727 /* Add the segment, if not object file. */ \
728 if (!fSkipSeg && uEffFileType != MH_OBJECT) \
729 { \
730 cbStringPool += RTStrNLen(&pSrcSeg->segname[0], sizeof(pSrcSeg->segname)) + 1; \
731 cSegments++; \
732 if (cSegments == 1) /* The link address is set by the first segment. */ \
733 *pLinkAddress = pSrcSeg->vmaddr; \
734 } \
735 } while (0)
736
737 VALIDATE_AND_ADD_SEGMENT(32);
738
739 /*
740 * Convert, validate and parse the sections.
741 */
742 cSectionsLeft = pSrcSeg->nsects;
743 pFirstSect = pSect = (section_32_t *)(pSrcSeg + 1);
744 while (cSectionsLeft-- > 0)
745 {
746 if (fConvertEndian)
747 {
748 pSect->addr = RT_BSWAP_U32(pSect->addr);
749 pSect->size = RT_BSWAP_U32(pSect->size);
750 pSect->offset = RT_BSWAP_U32(pSect->offset);
751 pSect->align = RT_BSWAP_U32(pSect->align);
752 pSect->reloff = RT_BSWAP_U32(pSect->reloff);
753 pSect->nreloc = RT_BSWAP_U32(pSect->nreloc);
754 pSect->flags = RT_BSWAP_U32(pSect->flags);
755 pSect->reserved1 = RT_BSWAP_U32(pSect->reserved1);
756 pSect->reserved2 = RT_BSWAP_U32(pSect->reserved2);
757 }
758
759 /* Validation code shared with the 64-bit variant. */
760 #define VALIDATE_AND_ADD_SECTION(a_cBits) \
761 do { \
762 int fFileBits; \
763 \
764 /* validate */ \
765 if (uEffFileType != MH_OBJECT) \
766 RTLDRMODMACHO_CHECK_RETURN(!strcmp(pSect->segname, pSrcSeg->segname),\
767 VERR_LDRMACHO_BAD_SECTION); \
768 \
769 switch (pSect->flags & SECTION_TYPE) \
770 { \
771 case S_ZEROFILL: \
772 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
773 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
774 fFileBits = 0; \
775 break; \
776 case S_REGULAR: \
777 case S_CSTRING_LITERALS: \
778 case S_COALESCED: \
779 case S_4BYTE_LITERALS: \
780 case S_8BYTE_LITERALS: \
781 case S_16BYTE_LITERALS: \
782 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
783 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
784 fFileBits = 1; \
785 break; \
786 \
787 case S_SYMBOL_STUBS: \
788 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
789 /* reserved2 == stub size. 0 has been seen (corecrypto.kext) */ \
790 RTLDRMODMACHO_CHECK_RETURN(pSect->reserved2 < 64, VERR_LDRMACHO_BAD_SECTION); \
791 fFileBits = 1; \
792 break; \
793 \
794 case S_NON_LAZY_SYMBOL_POINTERS: \
795 case S_LAZY_SYMBOL_POINTERS: \
796 case S_LAZY_DYLIB_SYMBOL_POINTERS: \
797 /* (reserved 1 = is indirect symbol table index) */ \
798 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
799 *pfCanLoad = false; \
800 fFileBits = -1; /* __DATA.__got in the 64-bit mach_kernel has bits, any things without bits? */ \
801 break; \
802 \
803 case S_MOD_INIT_FUNC_POINTERS: \
804 /** @todo this requires a query API or flag... (e.g. C++ constructors) */ \
805 RTLDRMODMACHO_CHECK_RETURN(fOpenFlags & RTLDR_O_FOR_DEBUG, \
806 VERR_LDRMACHO_UNSUPPORTED_INIT_SECTION); \
807 RT_FALL_THRU(); \
808 case S_MOD_TERM_FUNC_POINTERS: \
809 /** @todo this requires a query API or flag... (e.g. C++ destructors) */ \
810 RTLDRMODMACHO_CHECK_RETURN(fOpenFlags & RTLDR_O_FOR_DEBUG, \
811 VERR_LDRMACHO_UNSUPPORTED_TERM_SECTION); \
812 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
813 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
814 fFileBits = 1; \
815 break; /* ignored */ \
816 \
817 case S_LITERAL_POINTERS: \
818 case S_DTRACE_DOF: \
819 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
820 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
821 fFileBits = 1; \
822 break; \
823 \
824 case S_INTERPOSING: \
825 case S_GB_ZEROFILL: \
826 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNSUPPORTED_SECTION); \
827 \
828 default: \
829 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNKNOWN_SECTION); \
830 } \
831 RTLDRMODMACHO_CHECK_RETURN(!(pSect->flags & ~( S_ATTR_PURE_INSTRUCTIONS | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS \
832 | S_ATTR_NO_DEAD_STRIP | S_ATTR_LIVE_SUPPORT | S_ATTR_SELF_MODIFYING_CODE \
833 | S_ATTR_DEBUG | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_EXT_RELOC \
834 | S_ATTR_LOC_RELOC | SECTION_TYPE)), \
835 VERR_LDRMACHO_BAD_SECTION); \
836 RTLDRMODMACHO_CHECK_RETURN((pSect->flags & S_ATTR_DEBUG) == (pFirstSect->flags & S_ATTR_DEBUG), \
837 VERR_LDRMACHO_MIXED_DEBUG_SECTION_FLAGS); \
838 \
839 RTLDRMODMACHO_CHECK_RETURN(pSect->addr - pSrcSeg->vmaddr <= pSrcSeg->vmsize, \
840 VERR_LDRMACHO_BAD_SECTION); \
841 RTLDRMODMACHO_CHECK_RETURN( pSect->addr - pSrcSeg->vmaddr + pSect->size <= pSrcSeg->vmsize \
842 || !strcmp(pSrcSeg->segname, "__CTF") /* see above */, \
843 VERR_LDRMACHO_BAD_SECTION); \
844 RTLDRMODMACHO_CHECK_RETURN(pSect->align < 31, \
845 VERR_LDRMACHO_BAD_SECTION); \
846 /* Workaround for buggy ld64 (or as, llvm, ++) that produces a misaligned __TEXT.__unwind_info. */ \
847 /* Seen: pSect->align = 4, pSect->addr = 0x5ebe14. Just adjust the alignment down. */ \
848 if ( ((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSect->addr) \
849 && pSect->align == 4 \
850 && strcmp(pSect->sectname, "__unwind_info") == 0) \
851 pSect->align = 2; \
852 RTLDRMODMACHO_CHECK_RETURN(!((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSect->addr), \
853 VERR_LDRMACHO_BAD_SECTION); \
854 RTLDRMODMACHO_CHECK_RETURN(!((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSrcSeg->vmaddr), \
855 VERR_LDRMACHO_BAD_SECTION); \
856 \
857 /* Adjust the section offset before we check file offset. */ \
858 offSect = (offSect + RT_BIT_64(pSect->align) - UINT64_C(1)) & ~(RT_BIT_64(pSect->align) - UINT64_C(1)); \
859 if (pSect->addr) \
860 { \
861 RTLDRMODMACHO_CHECK_RETURN(offSect <= pSect->addr - pSrcSeg->vmaddr, VERR_LDRMACHO_BAD_SECTION); \
862 if (offSect < pSect->addr - pSrcSeg->vmaddr) \
863 offSect = pSect->addr - pSrcSeg->vmaddr; \
864 } \
865 \
866 if (fFileBits && pSect->offset == 0 && pSrcSeg->fileoff == 0 && pHdr->filetype == MH_DSYM) \
867 fFileBits = 0; \
868 if (fFileBits) \
869 { \
870 if (uEffFileType != MH_OBJECT) \
871 { \
872 RTLDRMODMACHO_CHECK_RETURN(pSect->offset == pSrcSeg->fileoff + offSect, \
873 VERR_LDRMACHO_NON_CONT_SEG_BITS); \
874 RTLDRMODMACHO_CHECK_RETURN(pSect->offset - pSrcSeg->fileoff <= pSrcSeg->filesize, \
875 VERR_LDRMACHO_BAD_SECTION); \
876 } \
877 RTLDRMODMACHO_CHECK_RETURN(pSect->offset <= cbFile, \
878 VERR_LDRMACHO_BAD_SECTION); \
879 RTLDRMODMACHO_CHECK_RETURN((uint64_t)pSect->offset + pSect->size <= cbFile, \
880 VERR_LDRMACHO_BAD_SECTION); \
881 } \
882 else \
883 RTLDRMODMACHO_CHECK_RETURN(pSect->offset == 0, VERR_LDRMACHO_BAD_SECTION); \
884 \
885 if (!pSect->nreloc) \
886 RTLDRMODMACHO_CHECK_RETURN(!pSect->reloff, \
887 VERR_LDRMACHO_BAD_SECTION); \
888 else \
889 { \
890 RTLDRMODMACHO_CHECK_RETURN(pSect->reloff <= cbFile, \
891 VERR_LDRMACHO_BAD_SECTION); \
892 RTLDRMODMACHO_CHECK_RETURN( (uint64_t)pSect->reloff \
893 + (RTFOFF)pSect->nreloc * sizeof(macho_relocation_info_t) \
894 <= cbFile, \
895 VERR_LDRMACHO_BAD_SECTION); \
896 } \
897 \
898 /* Validate against file type (pointless?) and count the section, for object files add segment. */ \
899 switch (uEffFileType) \
900 { \
901 case MH_OBJECT: \
902 if ( !(pSect->flags & S_ATTR_DEBUG) \
903 && strcmp(pSect->segname, "__DWARF")) \
904 { \
905 cbStringPool += RTStrNLen(&pSect->segname[0], sizeof(pSect->segname)) + 1; \
906 cbStringPool += RTStrNLen(&pSect->sectname[0], sizeof(pSect->sectname)) + 1; \
907 cSegments++; \
908 if (cSegments == 1) /* The link address is set by the first segment. */ \
909 *pLinkAddress = pSect->addr; \
910 } \
911 RT_FALL_THRU(); \
912 case MH_EXECUTE: \
913 case MH_DYLIB: \
914 case MH_BUNDLE: \
915 case MH_DSYM: \
916 case MH_KEXT_BUNDLE: \
917 cSections++; \
918 break; \
919 default: \
920 RTLDRMODMACHO_FAILED_RETURN(VERR_INVALID_PARAMETER); \
921 } \
922 \
923 /* Advance the section offset, since we're also aligning it. */ \
924 offSect += pSect->size; \
925 } while (0) /* VALIDATE_AND_ADD_SECTION */
926
927 VALIDATE_AND_ADD_SECTION(32);
928
929 /* next */
930 pSect++;
931 }
932 break;
933 }
934
935 case LC_SEGMENT_64:
936 {
937 segment_command_64_t *pSrcSeg = (segment_command_64_t *)u.pLoadCmd;
938 section_64_t *pFirstSect = (section_64_t *)(pSrcSeg + 1);
939 section_64_t *pSect = pFirstSect;
940 uint32_t cSectionsLeft = pSrcSeg->nsects;
941 uint64_t offSect = 0;
942
943 /* Convert and verify the segment. */
944 RTLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_64_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
945 RTLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE
946 || pHdr->magic == IMAGE_MACHO64_SIGNATURE, VERR_LDRMACHO_BIT_MIX);
947 if (fConvertEndian)
948 {
949 pSrcSeg->vmaddr = RT_BSWAP_U64(pSrcSeg->vmaddr);
950 pSrcSeg->vmsize = RT_BSWAP_U64(pSrcSeg->vmsize);
951 pSrcSeg->fileoff = RT_BSWAP_U64(pSrcSeg->fileoff);
952 pSrcSeg->filesize = RT_BSWAP_U64(pSrcSeg->filesize);
953 pSrcSeg->maxprot = RT_BSWAP_U32(pSrcSeg->maxprot);
954 pSrcSeg->initprot = RT_BSWAP_U32(pSrcSeg->initprot);
955 pSrcSeg->nsects = RT_BSWAP_U32(pSrcSeg->nsects);
956 pSrcSeg->flags = RT_BSWAP_U32(pSrcSeg->flags);
957 }
958
959 VALIDATE_AND_ADD_SEGMENT(64);
960
961 /*
962 * Convert, validate and parse the sections.
963 */
964 while (cSectionsLeft-- > 0)
965 {
966 if (fConvertEndian)
967 {
968 pSect->addr = RT_BSWAP_U64(pSect->addr);
969 pSect->size = RT_BSWAP_U64(pSect->size);
970 pSect->offset = RT_BSWAP_U32(pSect->offset);
971 pSect->align = RT_BSWAP_U32(pSect->align);
972 pSect->reloff = RT_BSWAP_U32(pSect->reloff);
973 pSect->nreloc = RT_BSWAP_U32(pSect->nreloc);
974 pSect->flags = RT_BSWAP_U32(pSect->flags);
975 pSect->reserved1 = RT_BSWAP_U32(pSect->reserved1);
976 pSect->reserved2 = RT_BSWAP_U32(pSect->reserved2);
977 }
978
979 VALIDATE_AND_ADD_SECTION(64);
980
981 /* next */
982 pSect++;
983 }
984 break;
985 } /* LC_SEGMENT_64 */
986
987
988 case LC_SYMTAB:
989 {
990 size_t cbSym;
991 if (fConvertEndian)
992 {
993 u.pSymTab->symoff = RT_BSWAP_U32(u.pSymTab->symoff);
994 u.pSymTab->nsyms = RT_BSWAP_U32(u.pSymTab->nsyms);
995 u.pSymTab->stroff = RT_BSWAP_U32(u.pSymTab->stroff);
996 u.pSymTab->strsize = RT_BSWAP_U32(u.pSymTab->strsize);
997 }
998
999 /* verify */
1000 cbSym = pHdr->magic == IMAGE_MACHO32_SIGNATURE
1001 || pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
1002 ? sizeof(macho_nlist_32_t)
1003 : sizeof(macho_nlist_64_t);
1004 if ( u.pSymTab->symoff >= cbFile
1005 || (uint64_t)u.pSymTab->symoff + u.pSymTab->nsyms * cbSym > cbFile)
1006 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
1007 if ( u.pSymTab->stroff >= cbFile
1008 || (uint64_t)u.pSymTab->stroff + u.pSymTab->strsize > cbFile)
1009 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
1010
1011 /* only one string in objects, please. */
1012 cSymbolTabs++;
1013 if ( uEffFileType == MH_OBJECT
1014 && cSymbolTabs != 1)
1015 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_OBJECT_FILE);
1016 break;
1017 }
1018
1019 case LC_DYSYMTAB:
1020 /** @todo deal with this! */
1021 break;
1022
1023 case LC_THREAD:
1024 case LC_UNIXTHREAD:
1025 {
1026 uint32_t *pu32 = (uint32_t *)(u.pb + sizeof(load_command_t));
1027 uint32_t cItemsLeft = (u.pThread->cmdsize - sizeof(load_command_t)) / sizeof(uint32_t);
1028 while (cItemsLeft)
1029 {
1030 /* convert & verify header items ([0] == flavor, [1] == uint32_t count). */
1031 if (cItemsLeft < 2)
1032 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
1033 if (fConvertEndian)
1034 {
1035 pu32[0] = RT_BSWAP_U32(pu32[0]);
1036 pu32[1] = RT_BSWAP_U32(pu32[1]);
1037 }
1038 if (pu32[1] + 2 > cItemsLeft)
1039 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
1040
1041 /* convert & verify according to flavor. */
1042 switch (pu32[0])
1043 {
1044 /** @todo */
1045 default:
1046 break;
1047 }
1048
1049 /* next */
1050 cItemsLeft -= pu32[1] + 2;
1051 pu32 += pu32[1] + 2;
1052 }
1053 break;
1054 }
1055
1056 case LC_UUID:
1057 if (u.pUuid->cmdsize != sizeof(uuid_command_t))
1058 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
1059 /** @todo Check anything here need converting? */
1060 break;
1061
1062 case LC_CODE_SIGNATURE:
1063 if (u.pUuid->cmdsize != sizeof(linkedit_data_command_t))
1064 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
1065 break;
1066
1067 case LC_VERSION_MIN_MACOSX:
1068 case LC_VERSION_MIN_IPHONEOS:
1069 if (u.pUuid->cmdsize != sizeof(version_min_command_t))
1070 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
1071 break;
1072
1073 case LC_SOURCE_VERSION: /* Harmless. It just gives a clue regarding the source code revision/version. */
1074 case LC_DATA_IN_CODE: /* Ignore */
1075 case LC_DYLIB_CODE_SIGN_DRS:/* Ignore */
1076 /** @todo valid command size. */
1077 break;
1078
1079 case LC_FUNCTION_STARTS: /** @todo dylib++ */
1080 /* Ignore for now. */
1081 break;
1082 case LC_ID_DYLIB: /** @todo dylib */
1083 case LC_LOAD_DYLIB: /** @todo dylib */
1084 case LC_LOAD_DYLINKER: /** @todo dylib */
1085 case LC_TWOLEVEL_HINTS: /** @todo dylib */
1086 case LC_LOAD_WEAK_DYLIB: /** @todo dylib */
1087 case LC_ID_DYLINKER: /** @todo dylib */
1088 case LC_RPATH: /** @todo dylib */
1089 case LC_SEGMENT_SPLIT_INFO: /** @todo dylib++ */
1090 case LC_REEXPORT_DYLIB: /** @todo dylib */
1091 case LC_DYLD_INFO: /** @todo dylib */
1092 case LC_DYLD_INFO_ONLY: /** @todo dylib */
1093 case LC_LOAD_UPWARD_DYLIB: /** @todo dylib */
1094 case LC_DYLD_ENVIRONMENT: /** @todo dylib */
1095 case LC_MAIN: /** @todo parse this and find and entry point or smth. */
1096 /** @todo valid command size. */
1097 if (!(fOpenFlags & RTLDR_O_FOR_DEBUG))
1098 RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_UNSUPPORTED_LOAD_COMMAND,
1099 "cmd=%#x", u.pLoadCmd->cmd));
1100 *pfCanLoad = false;
1101 break;
1102
1103 case LC_LOADFVMLIB:
1104 case LC_IDFVMLIB:
1105 case LC_IDENT:
1106 case LC_FVMFILE:
1107 case LC_PREPAGE:
1108 case LC_PREBOUND_DYLIB:
1109 case LC_ROUTINES:
1110 case LC_ROUTINES_64:
1111 case LC_SUB_FRAMEWORK:
1112 case LC_SUB_UMBRELLA:
1113 case LC_SUB_CLIENT:
1114 case LC_SUB_LIBRARY:
1115 case LC_PREBIND_CKSUM:
1116 case LC_SYMSEG:
1117 RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_UNSUPPORTED_LOAD_COMMAND,
1118 "cmd=%#x", u.pLoadCmd->cmd));
1119
1120 default:
1121 RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_UNKNOWN_LOAD_COMMAND,
1122 "cmd=%#x", u.pLoadCmd->cmd));
1123 }
1124 }
1125
1126 /* be strict. */
1127 if (cbLeft)
1128 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
1129
1130 switch (uEffFileType)
1131 {
1132 case MH_OBJECT:
1133 case MH_EXECUTE:
1134 case MH_DYLIB:
1135 case MH_BUNDLE:
1136 case MH_DSYM:
1137 case MH_KEXT_BUNDLE:
1138 if (!cSegments)
1139 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_OBJECT_FILE);
1140 break;
1141 }
1142
1143 *pcSegments = cSegments;
1144 *pcSections = cSections;
1145 *pcbStringPool = (uint32_t)cbStringPool;
1146
1147 return VINF_SUCCESS;
1148}
1149
1150
1151/**
1152 * Parses the load commands after we've carved out the module instance.
1153 *
1154 * This fills in the segment table and perhaps some other properties.
1155 *
1156 * @returns IPRT status code.
1157 * @param pThis The module.
1158 * @param pbStringPool The string pool
1159 * @param cbStringPool The size of the string pool.
1160 */
1161static int kldrModMachOParseLoadCommands(PRTLDRMODMACHO pThis, char *pbStringPool, uint32_t cbStringPool)
1162{
1163 union
1164 {
1165 const uint8_t *pb;
1166 const load_command_t *pLoadCmd;
1167 const segment_command_32_t *pSeg32;
1168 const segment_command_64_t *pSeg64;
1169 const symtab_command_t *pSymTab;
1170 const uuid_command_t *pUuid;
1171 const linkedit_data_command_t *pData;
1172 } u;
1173 uint32_t cLeft = pThis->Hdr.ncmds;
1174 uint32_t cbLeft = pThis->Hdr.sizeofcmds;
1175 const uint8_t *pb = pThis->pbLoadCommands;
1176 PRTLDRMODMACHOSEG pDstSeg = &pThis->aSegments[0];
1177 PRTLDRMODMACHOSECT pSectExtra = pThis->paSections;
1178 const uint32_t cSegments = pThis->cSegments;
1179 PRTLDRMODMACHOSEG pSegItr;
1180 bool fFirstSeg = true;
1181 RT_NOREF(cbStringPool);
1182
1183 while (cLeft-- > 0)
1184 {
1185 u.pb = pb;
1186 cbLeft -= u.pLoadCmd->cmdsize;
1187 pb += u.pLoadCmd->cmdsize;
1188
1189 /*
1190 * Convert endian if needed, parse and validate the command.
1191 */
1192 switch (u.pLoadCmd->cmd)
1193 {
1194 case LC_SEGMENT_32:
1195 {
1196 const segment_command_32_t *pSrcSeg = (const segment_command_32_t *)u.pLoadCmd;
1197 section_32_t *pFirstSect = (section_32_t *)(pSrcSeg + 1);
1198 section_32_t *pSect = pFirstSect;
1199 uint32_t cSectionsLeft = pSrcSeg->nsects;
1200
1201 /* Adds a segment, used by the macro below and thus shared with the 64-bit segment variant. */
1202#define NEW_SEGMENT(a_cBits, a_achName1, a_fObjFile, a_achName2, a_SegAddr, a_cbSeg, a_fFileBits, a_offFile, a_cbFile) \
1203 do { \
1204 pDstSeg->SegInfo.pszName = pbStringPool; \
1205 pDstSeg->SegInfo.cchName = (uint32_t)RTStrNLen(a_achName1, sizeof(a_achName1)); \
1206 memcpy(pbStringPool, a_achName1, pDstSeg->SegInfo.cchName); \
1207 pbStringPool += pDstSeg->SegInfo.cchName; \
1208 if (a_fObjFile) \
1209 { /* MH_OBJECT: Add '.sectname' - sections aren't sorted by segments. */ \
1210 size_t cchName2 = RTStrNLen(a_achName2, sizeof(a_achName2)); \
1211 *pbStringPool++ = '.'; \
1212 memcpy(pbStringPool, a_achName2, cchName2); \
1213 pbStringPool += cchName2; \
1214 pDstSeg->SegInfo.cchName += (uint32_t)cchName2; \
1215 } \
1216 *pbStringPool++ = '\0'; \
1217 pDstSeg->SegInfo.SelFlat = 0; \
1218 pDstSeg->SegInfo.Sel16bit = 0; \
1219 pDstSeg->SegInfo.fFlags = 0; \
1220 pDstSeg->SegInfo.fProt = RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC; /** @todo fixme! */ \
1221 pDstSeg->SegInfo.cb = (a_cbSeg); \
1222 pDstSeg->SegInfo.Alignment = 1; /* updated while parsing sections. */ \
1223 pDstSeg->SegInfo.LinkAddress = (a_SegAddr); \
1224 if (a_fFileBits) \
1225 { \
1226 pDstSeg->SegInfo.offFile = (RTFOFF)((a_offFile) + pThis->offImage); \
1227 pDstSeg->SegInfo.cbFile = (RTFOFF)(a_cbFile); \
1228 } \
1229 else \
1230 { \
1231 pDstSeg->SegInfo.offFile = -1; \
1232 pDstSeg->SegInfo.cbFile = -1; \
1233 } \
1234 pDstSeg->SegInfo.RVA = (a_SegAddr) - pThis->LinkAddress; \
1235 pDstSeg->SegInfo.cbMapped = 0; \
1236 \
1237 pDstSeg->iOrgSegNo = (uint32_t)(pDstSeg - &pThis->aSegments[0]); \
1238 pDstSeg->cSections = 0; \
1239 pDstSeg->paSections = pSectExtra; \
1240 } while (0)
1241
1242 /* Closes the new segment - part of NEW_SEGMENT. */
1243#define CLOSE_SEGMENT() \
1244 do { \
1245 pDstSeg->cSections = (uint32_t)(pSectExtra - pDstSeg->paSections); \
1246 pDstSeg++; \
1247 } while (0)
1248
1249
1250 /* Shared with the 64-bit variant. */
1251#define ADD_SEGMENT_AND_ITS_SECTIONS(a_cBits) \
1252 do { \
1253 bool fAddSegOuter = false; \
1254 \
1255 /* \
1256 * Check that the segment name is unique. We couldn't do that \
1257 * in the preparsing stage. \
1258 */ \
1259 if (pThis->uEffFileType != MH_OBJECT) \
1260 for (pSegItr = &pThis->aSegments[0]; pSegItr != pDstSeg; pSegItr++) \
1261 if (!strncmp(pSegItr->SegInfo.pszName, pSrcSeg->segname, sizeof(pSrcSeg->segname))) \
1262 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_DUPLICATE_SEGMENT_NAME); \
1263 \
1264 /* \
1265 * Create a new segment, unless we're supposed to skip this one. \
1266 */ \
1267 if ( pThis->uEffFileType != MH_OBJECT \
1268 && (cSectionsLeft == 0 || !(pFirstSect->flags & S_ATTR_DEBUG)) \
1269 && strcmp(pSrcSeg->segname, "__DWARF") \
1270 && strcmp(pSrcSeg->segname, "__CTF") ) \
1271 { \
1272 NEW_SEGMENT(a_cBits, pSrcSeg->segname, false /*a_fObjFile*/, "" /*a_achName2*/, \
1273 pSrcSeg->vmaddr, pSrcSeg->vmsize, \
1274 pSrcSeg->filesize != 0, pSrcSeg->fileoff, pSrcSeg->filesize); \
1275 fAddSegOuter = true; \
1276 } \
1277 \
1278 /* \
1279 * Convert and parse the sections. \
1280 */ \
1281 while (cSectionsLeft-- > 0) \
1282 { \
1283 /* New segment if object file. */ \
1284 bool fAddSegInner = false; \
1285 if ( pThis->uEffFileType == MH_OBJECT \
1286 && !(pSect->flags & S_ATTR_DEBUG) \
1287 && strcmp(pSrcSeg->segname, "__DWARF") \
1288 && strcmp(pSrcSeg->segname, "__CTF") ) \
1289 { \
1290 Assert(!fAddSegOuter); \
1291 NEW_SEGMENT(a_cBits, pSect->segname, true /*a_fObjFile*/, pSect->sectname, \
1292 pSect->addr, pSect->size, \
1293 pSect->offset != 0, pSect->offset, pSect->size); \
1294 fAddSegInner = true; \
1295 } \
1296 \
1297 /* Section data extract. */ \
1298 pSectExtra->cb = pSect->size; \
1299 pSectExtra->RVA = pSect->addr - pDstSeg->SegInfo.LinkAddress; \
1300 pSectExtra->LinkAddress = pSect->addr; \
1301 if (pSect->offset) \
1302 pSectExtra->offFile = pSect->offset + pThis->offImage; \
1303 else \
1304 pSectExtra->offFile = -1; \
1305 pSectExtra->cFixups = pSect->nreloc; \
1306 pSectExtra->paFixups = NULL; \
1307 if (pSect->nreloc) \
1308 pSectExtra->offFixups = pSect->reloff + pThis->offImage; \
1309 else \
1310 pSectExtra->offFixups = -1; \
1311 pSectExtra->fFlags = pSect->flags; \
1312 pSectExtra->iSegment = (uint32_t)(pDstSeg - &pThis->aSegments[0]); \
1313 pSectExtra->pvMachoSection = pSect; \
1314 \
1315 /* Update the segment alignment, if we're not skipping it. */ \
1316 if ( (fAddSegOuter || fAddSegInner) \
1317 && pDstSeg->SegInfo.Alignment < ((RTLDRADDR)1 << pSect->align)) \
1318 pDstSeg->SegInfo.Alignment = (RTLDRADDR)1 << pSect->align; \
1319 \
1320 /* Next section, and if object file next segment. */ \
1321 pSectExtra++; \
1322 pSect++; \
1323 if (fAddSegInner) \
1324 CLOSE_SEGMENT(); \
1325 } \
1326 \
1327 /* Close the segment and advance. */ \
1328 if (fAddSegOuter) \
1329 CLOSE_SEGMENT(); \
1330 \
1331 /* Take down 'execSeg' info for signing */ \
1332 if (fFirstSeg) \
1333 { \
1334 fFirstSeg = false; \
1335 pThis->offSeg0ForCodeSign = pSrcSeg->fileoff; \
1336 pThis->cbSeg0ForCodeSign = pSrcSeg->filesize; /** @todo file or vm size? */ \
1337 pThis->fSeg0ForCodeSign = pSrcSeg->flags; \
1338 } \
1339 } while (0) /* ADD_SEGMENT_AND_ITS_SECTIONS */
1340
1341 ADD_SEGMENT_AND_ITS_SECTIONS(32);
1342 break;
1343 }
1344
1345 case LC_SEGMENT_64:
1346 {
1347 const segment_command_64_t *pSrcSeg = (const segment_command_64_t *)u.pLoadCmd;
1348 section_64_t *pFirstSect = (section_64_t *)(pSrcSeg + 1);
1349 section_64_t *pSect = pFirstSect;
1350 uint32_t cSectionsLeft = pSrcSeg->nsects;
1351
1352 ADD_SEGMENT_AND_ITS_SECTIONS(64);
1353 break;
1354 }
1355
1356 case LC_SYMTAB:
1357 switch (pThis->uEffFileType)
1358 {
1359 case MH_OBJECT:
1360 case MH_EXECUTE:
1361 case MH_DYLIB: /** @todo ??? */
1362 case MH_BUNDLE: /** @todo ??? */
1363 case MH_DSYM:
1364 case MH_KEXT_BUNDLE:
1365 pThis->offSymbols = u.pSymTab->symoff + pThis->offImage;
1366 pThis->cSymbols = u.pSymTab->nsyms;
1367 pThis->offStrings = u.pSymTab->stroff + pThis->offImage;
1368 pThis->cchStrings = u.pSymTab->strsize;
1369 break;
1370 }
1371 break;
1372
1373 case LC_UUID:
1374 memcpy(pThis->abImageUuid, u.pUuid->uuid, sizeof(pThis->abImageUuid));
1375 break;
1376
1377 case LC_CODE_SIGNATURE:
1378 pThis->offCodeSignature = u.pData->dataoff;
1379 pThis->cbCodeSignature = u.pData->datasize;
1380 break;
1381
1382 default:
1383 break;
1384 } /* command switch */
1385 } /* while more commands */
1386
1387 Assert(pDstSeg == &pThis->aSegments[cSegments - pThis->fMakeGot]);
1388
1389 /*
1390 * Adjust mapping addresses calculating the image size.
1391 */
1392 {
1393 bool fLoadLinkEdit = false;
1394 PRTLDRMODMACHOSECT pSectExtraItr;
1395 RTLDRADDR uNextRVA = 0;
1396 RTLDRADDR cb;
1397 uint32_t cSegmentsToAdjust = cSegments - pThis->fMakeGot;
1398 uint32_t c;
1399
1400 for (;;)
1401 {
1402 /* Check if there is __DWARF segment at the end and make sure it's left
1403 out of the RVA negotiations and image loading. */
1404 if ( cSegmentsToAdjust > 0
1405 && !strcmp(pThis->aSegments[cSegmentsToAdjust - 1].SegInfo.pszName, "__DWARF"))
1406 {
1407 cSegmentsToAdjust--;
1408 pThis->aSegments[cSegmentsToAdjust].SegInfo.RVA = NIL_RTLDRADDR;
1409 pThis->aSegments[cSegmentsToAdjust].SegInfo.cbMapped = 0;
1410 continue;
1411 }
1412
1413 /* If we're skipping the __LINKEDIT segment, check for it and adjust
1414 the number of segments we'll be messing with here. ASSUMES it's
1415 last (by now anyway). */
1416 if ( !fLoadLinkEdit
1417 && cSegmentsToAdjust > 0
1418 && !strcmp(pThis->aSegments[cSegmentsToAdjust - 1].SegInfo.pszName, "__LINKEDIT"))
1419 {
1420 cSegmentsToAdjust--;
1421 pThis->aSegments[cSegmentsToAdjust].SegInfo.RVA = NIL_RTLDRADDR;
1422 pThis->aSegments[cSegmentsToAdjust].SegInfo.cbMapped = 0;
1423 continue;
1424 }
1425 break;
1426 }
1427
1428 /* Adjust RVAs. */
1429 c = cSegmentsToAdjust;
1430 for (pDstSeg = &pThis->aSegments[0]; c-- > 0; pDstSeg++)
1431 {
1432 uNextRVA = RTLDR_ALIGN_ADDR(uNextRVA, pDstSeg->SegInfo.Alignment);
1433 cb = pDstSeg->SegInfo.RVA - uNextRVA;
1434 if (cb >= 0x00100000) /* 1MB */
1435 {
1436 pDstSeg->SegInfo.RVA = uNextRVA;
1437 //pThis->pMod->fFlags |= KLDRMOD_FLAGS_NON_CONTIGUOUS_LINK_ADDRS;
1438 }
1439 uNextRVA = pDstSeg->SegInfo.RVA + pDstSeg->SegInfo.cb;
1440 }
1441
1442 /* Calculate the cbMapping members. */
1443 c = cSegmentsToAdjust;
1444 for (pDstSeg = &pThis->aSegments[0]; c-- > 1; pDstSeg++)
1445 {
1446
1447 cb = pDstSeg[1].SegInfo.RVA - pDstSeg->SegInfo.RVA;
1448 pDstSeg->SegInfo.cbMapped = (size_t)cb == cb ? (size_t)cb : ~(size_t)0;
1449 }
1450
1451 cb = RTLDR_ALIGN_ADDR(pDstSeg->SegInfo.cb, pDstSeg->SegInfo.Alignment);
1452 pDstSeg->SegInfo.cbMapped = (size_t)cb == cb ? (size_t)cb : ~(size_t)0;
1453
1454 /* Set the image size. */
1455 pThis->cbImage = pDstSeg->SegInfo.RVA + cb;
1456
1457 /* Fixup the section RVAs (internal). */
1458 c = cSegmentsToAdjust;
1459 uNextRVA = pThis->cbImage;
1460 pDstSeg = &pThis->aSegments[0];
1461 for (pSectExtraItr = pThis->paSections; pSectExtraItr != pSectExtra; pSectExtraItr++)
1462 {
1463 if (pSectExtraItr->iSegment < c)
1464 pSectExtraItr->RVA += pDstSeg[pSectExtraItr->iSegment].SegInfo.RVA;
1465 else
1466 {
1467 pSectExtraItr->RVA = uNextRVA;
1468 uNextRVA += RTLDR_ALIGN_ADDR(pSectExtraItr->cb, 64);
1469 }
1470 }
1471 }
1472
1473 /*
1474 * Make the GOT segment if necessary.
1475 */
1476 if (pThis->fMakeGot)
1477 {
1478 uint32_t cbPtr = ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1479 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1480 ? sizeof(uint32_t)
1481 : sizeof(uint64_t);
1482 uint32_t cbGot = pThis->cSymbols * cbPtr;
1483 uint32_t cbJmpStubs;
1484
1485 pThis->GotRVA = pThis->cbImage;
1486
1487 if (pThis->cbJmpStub)
1488 {
1489 cbGot = RT_ALIGN_Z(cbGot, 64);
1490 pThis->JmpStubsRVA = pThis->GotRVA + cbGot;
1491 cbJmpStubs = pThis->cbJmpStub * pThis->cSymbols;
1492 }
1493 else
1494 {
1495 pThis->JmpStubsRVA = NIL_RTLDRADDR;
1496 cbJmpStubs = 0;
1497 }
1498
1499 pDstSeg = &pThis->aSegments[cSegments - 1];
1500 pDstSeg->SegInfo.pszName = "GOT";
1501 pDstSeg->SegInfo.cchName = 3;
1502 pDstSeg->SegInfo.SelFlat = 0;
1503 pDstSeg->SegInfo.Sel16bit = 0;
1504 pDstSeg->SegInfo.fFlags = 0;
1505 pDstSeg->SegInfo.fProt = RTMEM_PROT_READ;
1506 pDstSeg->SegInfo.cb = cbGot + cbJmpStubs;
1507 pDstSeg->SegInfo.Alignment = 64;
1508 pDstSeg->SegInfo.LinkAddress = pThis->LinkAddress + pThis->GotRVA;
1509 pDstSeg->SegInfo.offFile = -1;
1510 pDstSeg->SegInfo.cbFile = -1;
1511 pDstSeg->SegInfo.RVA = pThis->GotRVA;
1512 pDstSeg->SegInfo.cbMapped = (size_t)RTLDR_ALIGN_ADDR(cbGot + cbJmpStubs, pDstSeg->SegInfo.Alignment);
1513
1514 pDstSeg->iOrgSegNo = UINT32_MAX;
1515 pDstSeg->cSections = 0;
1516 pDstSeg->paSections = NULL;
1517
1518 pThis->cbImage += pDstSeg->SegInfo.cbMapped;
1519 }
1520
1521 return VINF_SUCCESS;
1522}
1523
1524
1525/**
1526 * @interface_method_impl{RTLDROPS,pfnClose}
1527 */
1528static DECLCALLBACK(int) rtldrMachO_Close(PRTLDRMODINTERNAL pMod)
1529{
1530 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
1531 RTLDRMODMACHO_ASSERT(!pThis->pvMapping);
1532
1533 uint32_t i = pThis->cSegments;
1534 while (i-- > 0)
1535 {
1536 uint32_t j = pThis->aSegments[i].cSections;
1537 while (j-- > 0)
1538 {
1539 RTMemFree(pThis->aSegments[i].paSections[j].paFixups);
1540 pThis->aSegments[i].paSections[j].paFixups = NULL;
1541 }
1542 }
1543
1544 RTMemFree(pThis->pbLoadCommands);
1545 pThis->pbLoadCommands = NULL;
1546 RTMemFree(pThis->pchStrings);
1547 pThis->pchStrings = NULL;
1548 RTMemFree(pThis->pvaSymbols);
1549 pThis->pvaSymbols = NULL;
1550 RTMemFree(pThis->PtrCodeSignature.pb);
1551 pThis->PtrCodeSignature.pb = NULL;
1552
1553 return VINF_SUCCESS;
1554}
1555
1556
1557/**
1558 * Gets the right base address.
1559 *
1560 * @param pThis The interpreter module instance
1561 * @param pBaseAddress The base address, IN & OUT. Optional.
1562 */
1563static void kldrModMachOAdjustBaseAddress(PRTLDRMODMACHO pThis, PRTLDRADDR pBaseAddress)
1564{
1565 /*
1566 * Adjust the base address.
1567 */
1568 if (*pBaseAddress == RTLDR_BASEADDRESS_LINK)
1569 *pBaseAddress = pThis->LinkAddress;
1570}
1571
1572
1573/**
1574 * Resolves a linker generated symbol.
1575 *
1576 * The Apple linker generates symbols indicating the start and end of sections
1577 * and segments. This function checks for these and returns the right value.
1578 *
1579 * @returns VINF_SUCCESS or VERR_SYMBOL_NOT_FOUND.
1580 * @param pThis The interpreter module instance.
1581 * @param pchSymbol The symbol.
1582 * @param cchSymbol The length of the symbol.
1583 * @param BaseAddress The base address to apply when calculating the
1584 * value.
1585 * @param puValue Where to return the symbol value.
1586 */
1587static int kldrModMachOQueryLinkerSymbol(PRTLDRMODMACHO pThis, const char *pchSymbol, size_t cchSymbol,
1588 RTLDRADDR BaseAddress, PRTLDRADDR puValue)
1589{
1590 /*
1591 * Match possible name prefixes.
1592 */
1593 static const struct
1594 {
1595 const char *pszPrefix;
1596 uint32_t cchPrefix;
1597 bool fSection;
1598 bool fStart;
1599 } s_aPrefixes[] =
1600 {
1601 { "section$start$", (uint8_t)sizeof("section$start$") - 1, true, true },
1602 { "section$end$", (uint8_t)sizeof("section$end$") - 1, true, false},
1603 { "segment$start$", (uint8_t)sizeof("segment$start$") - 1, false, true },
1604 { "segment$end$", (uint8_t)sizeof("segment$end$") - 1, false, false},
1605 };
1606 size_t cchSectName = 0;
1607 const char *pchSectName = "";
1608 size_t cchSegName = 0;
1609 const char *pchSegName = NULL;
1610 uint32_t iPrefix = RT_ELEMENTS(s_aPrefixes) - 1;
1611 uint32_t iSeg;
1612 RTLDRADDR uValue;
1613
1614 for (;;)
1615 {
1616 uint8_t const cchPrefix = s_aPrefixes[iPrefix].cchPrefix;
1617 if ( cchSymbol > cchPrefix
1618 && strncmp(pchSymbol, s_aPrefixes[iPrefix].pszPrefix, cchPrefix) == 0)
1619 {
1620 pchSegName = pchSymbol + cchPrefix;
1621 cchSegName = cchSymbol - cchPrefix;
1622 break;
1623 }
1624
1625 /* next */
1626 if (!iPrefix)
1627 return VERR_SYMBOL_NOT_FOUND;
1628 iPrefix--;
1629 }
1630
1631 /*
1632 * Split the remainder into segment and section name, if necessary.
1633 */
1634 if (s_aPrefixes[iPrefix].fSection)
1635 {
1636 pchSectName = (const char *)memchr(pchSegName, '$', cchSegName);
1637 if (!pchSectName)
1638 return VERR_SYMBOL_NOT_FOUND;
1639 cchSegName = pchSectName - pchSegName;
1640 pchSectName++;
1641 cchSectName = cchSymbol - (pchSectName - pchSymbol);
1642 }
1643
1644 /*
1645 * Locate the segment.
1646 */
1647 if (!pThis->cSegments)
1648 return VERR_SYMBOL_NOT_FOUND;
1649 for (iSeg = 0; iSeg < pThis->cSegments; iSeg++)
1650 {
1651 if ( pThis->aSegments[iSeg].SegInfo.cchName >= cchSegName
1652 && memcmp(pThis->aSegments[iSeg].SegInfo.pszName, pchSegName, cchSegName) == 0)
1653 {
1654 section_32_t const *pSect;
1655 if ( pThis->aSegments[iSeg].SegInfo.cchName == cchSegName
1656 && pThis->Hdr.filetype != MH_OBJECT /* Good enough for __DWARF segs in MH_DHSYM, I hope. */)
1657 break;
1658
1659 pSect = (section_32_t *)pThis->aSegments[iSeg].paSections[0].pvMachoSection;
1660 if ( pThis->uEffFileType == MH_OBJECT
1661 && pThis->aSegments[iSeg].SegInfo.cchName > cchSegName + 1
1662 && pThis->aSegments[iSeg].SegInfo.pszName[cchSegName] == '.'
1663 && strncmp(&pThis->aSegments[iSeg].SegInfo.pszName[cchSegName + 1], pSect->sectname, sizeof(pSect->sectname)) == 0
1664 && pThis->aSegments[iSeg].SegInfo.cchName - cchSegName - 1 <= sizeof(pSect->sectname) )
1665 break;
1666 }
1667 }
1668 if (iSeg >= pThis->cSegments)
1669 return VERR_SYMBOL_NOT_FOUND;
1670
1671 if (!s_aPrefixes[iPrefix].fSection)
1672 {
1673 /*
1674 * Calculate the segment start/end address.
1675 */
1676 uValue = pThis->aSegments[iSeg].SegInfo.RVA;
1677 if (!s_aPrefixes[iPrefix].fStart)
1678 uValue += pThis->aSegments[iSeg].SegInfo.cb;
1679 }
1680 else
1681 {
1682 /*
1683 * Locate the section.
1684 */
1685 uint32_t iSect = pThis->aSegments[iSeg].cSections;
1686 if (!iSect)
1687 return VERR_SYMBOL_NOT_FOUND;
1688 for (;;)
1689 {
1690 section_32_t *pSect = (section_32_t *)pThis->aSegments[iSeg].paSections[iSect].pvMachoSection;
1691 if ( cchSectName <= sizeof(pSect->sectname)
1692 && memcmp(pSect->sectname, pchSectName, cchSectName) == 0
1693 && ( cchSectName == sizeof(pSect->sectname)
1694 || pSect->sectname[cchSectName] == '\0') )
1695 break;
1696 /* next */
1697 if (!iSect)
1698 return VERR_SYMBOL_NOT_FOUND;
1699 iSect--;
1700 }
1701
1702 uValue = pThis->aSegments[iSeg].paSections[iSect].RVA;
1703 if (!s_aPrefixes[iPrefix].fStart)
1704 uValue += pThis->aSegments[iSeg].paSections[iSect].cb;
1705 }
1706
1707 /*
1708 * Convert from RVA to load address.
1709 */
1710 uValue += BaseAddress;
1711 if (puValue)
1712 *puValue = uValue;
1713
1714 return VINF_SUCCESS;
1715}
1716
1717
1718/**
1719 * @interface_method_impl{RTLDROPS,pfnGetSymbolEx}
1720 */
1721static DECLCALLBACK(int) rtldrMachO_GetSymbolEx(PRTLDRMODINTERNAL pMod, const void *pvBits, RTUINTPTR BaseAddress,
1722 uint32_t iOrdinal, const char *pszSymbol, RTUINTPTR *pValue)
1723{
1724 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
1725 RT_NOREF(pvBits);
1726 //RT_NOREF(pszVersion);
1727 //RT_NOREF(pfnGetForwarder);
1728 //RT_NOREF(pvUser);
1729 uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
1730 uint32_t *pfKind = &fKind;
1731 size_t cchSymbol = pszSymbol ? strlen(pszSymbol) : 0;
1732
1733 /*
1734 * Resolve defaults.
1735 */
1736 kldrModMachOAdjustBaseAddress(pThis, &BaseAddress);
1737
1738 /*
1739 * Refuse segmented requests for now.
1740 */
1741 RTLDRMODMACHO_CHECK_RETURN( !pfKind
1742 || (*pfKind & RTLDRSYMKIND_REQ_TYPE_MASK) == RTLDRSYMKIND_REQ_FLAT,
1743 VERR_LDRMACHO_TODO);
1744
1745 /*
1746 * Take action according to file type.
1747 */
1748 int rc;
1749 if ( pThis->Hdr.filetype == MH_OBJECT
1750 || pThis->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
1751 || pThis->Hdr.filetype == MH_DYLIB
1752 || pThis->Hdr.filetype == MH_BUNDLE
1753 || pThis->Hdr.filetype == MH_DSYM
1754 || pThis->Hdr.filetype == MH_KEXT_BUNDLE)
1755 {
1756 rc = kldrModMachOLoadObjSymTab(pThis);
1757 if (RT_SUCCESS(rc))
1758 {
1759 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1760 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1761 rc = kldrModMachODoQuerySymbol32Bit(pThis, (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols,
1762 pThis->pchStrings, pThis->cchStrings, BaseAddress, iOrdinal, pszSymbol,
1763 (uint32_t)cchSymbol, pValue, pfKind);
1764 else
1765 rc = kldrModMachODoQuerySymbol64Bit(pThis, (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols,
1766 pThis->pchStrings, pThis->cchStrings, BaseAddress, iOrdinal, pszSymbol,
1767 (uint32_t)cchSymbol, pValue, pfKind);
1768 }
1769
1770 /*
1771 * Check for link-editor generated symbols and supply what we can.
1772 *
1773 * As small service to clients that insists on adding a '_' prefix
1774 * before querying symbols, we will ignore the prefix.
1775 */
1776 if ( rc == VERR_SYMBOL_NOT_FOUND
1777 && cchSymbol > sizeof("section$end$") - 1
1778 && ( pszSymbol[0] == 's'
1779 || (pszSymbol[1] == 's' && pszSymbol[0] == '_') )
1780 && memchr(pszSymbol, '$', cchSymbol) )
1781 {
1782 if (pszSymbol[0] == '_')
1783 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol + 1, cchSymbol - 1, BaseAddress, pValue);
1784 else
1785 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, pValue);
1786 }
1787 }
1788 else
1789 rc = VERR_LDRMACHO_TODO;
1790
1791 return rc;
1792}
1793
1794
1795/**
1796 * Lookup a symbol in a 32-bit symbol table.
1797 *
1798 * @returns IPRT status code.
1799 * @param pThis
1800 * @param paSyms Pointer to the symbol table.
1801 * @param cSyms Number of symbols in the table.
1802 * @param pchStrings Pointer to the string table.
1803 * @param cchStrings Size of the string table.
1804 * @param BaseAddress Adjusted base address, see kLdrModQuerySymbol.
1805 * @param iSymbol See kLdrModQuerySymbol.
1806 * @param pchSymbol See kLdrModQuerySymbol.
1807 * @param cchSymbol See kLdrModQuerySymbol.
1808 * @param puValue See kLdrModQuerySymbol.
1809 * @param pfKind See kLdrModQuerySymbol.
1810 */
1811static int kldrModMachODoQuerySymbol32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
1812 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol,
1813 const char *pchSymbol, uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind)
1814{
1815 /*
1816 * Find a valid symbol matching the search criteria.
1817 */
1818 if (iSymbol == UINT32_MAX)
1819 {
1820 /* simplify validation. */
1821 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
1822 if (cchStrings <= cchSymbol + 1)
1823 return VERR_SYMBOL_NOT_FOUND;
1824 cchStrings -= cchSymbol + 1;
1825
1826 /* external symbols are usually at the end, so search the other way. */
1827 for (iSymbol = cSyms - 1; iSymbol != UINT32_MAX; iSymbol--)
1828 {
1829 const char *psz;
1830
1831 /* Skip irrellevant and non-public symbols. */
1832 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1833 continue;
1834 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1835 continue;
1836 if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
1837 continue;
1838 if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
1839 continue;
1840
1841 /* get name */
1842 if (!paSyms[iSymbol].n_un.n_strx)
1843 continue;
1844 if ((uint32_t)paSyms[iSymbol].n_un.n_strx >= cchStrings)
1845 continue;
1846 psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
1847 if (psz[cchSymbol + 1])
1848 continue;
1849 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
1850 if (*psz != '_' || memcmp(psz + 1, pchSymbol, cchSymbol))
1851 continue;
1852
1853 /* match! */
1854 break;
1855 }
1856 if (iSymbol == UINT32_MAX)
1857 return VERR_SYMBOL_NOT_FOUND;
1858 }
1859 else
1860 {
1861 if (iSymbol >= cSyms)
1862 return VERR_SYMBOL_NOT_FOUND;
1863 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1864 return VERR_SYMBOL_NOT_FOUND;
1865 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1866 return VERR_SYMBOL_NOT_FOUND;
1867 }
1868
1869 /*
1870 * Calc the return values.
1871 */
1872 if (pfKind)
1873 {
1874 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1875 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1876 *pfKind = RTLDRSYMKIND_32BIT | RTLDRSYMKIND_NO_TYPE;
1877 else
1878 *pfKind = RTLDRSYMKIND_64BIT | RTLDRSYMKIND_NO_TYPE;
1879 if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
1880 *pfKind |= RTLDRSYMKIND_WEAK;
1881 }
1882
1883 switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
1884 {
1885 case MACHO_N_SECT:
1886 {
1887 PRTLDRMODMACHOSECT pSect;
1888 RTLDRADDR offSect;
1889 RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSymbol].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
1890 pSect = &pThis->paSections[paSyms[iSymbol].n_sect - 1];
1891
1892 offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
1893 RTLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
1894 || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
1895 && offSect == 0U - pSect->RVA
1896 && pThis->uEffFileType != MH_OBJECT),
1897 VERR_LDRMACHO_BAD_SYMBOL);
1898 if (puValue)
1899 *puValue = BaseAddress + pSect->RVA + offSect;
1900
1901 if ( pfKind
1902 && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
1903 *pfKind = (*pfKind & ~RTLDRSYMKIND_TYPE_MASK) | RTLDRSYMKIND_CODE;
1904 break;
1905 }
1906
1907 case MACHO_N_ABS:
1908 if (puValue)
1909 *puValue = paSyms[iSymbol].n_value;
1910 /*if (pfKind)
1911 pfKind |= RTLDRSYMKIND_ABS;*/
1912 break;
1913
1914 case MACHO_N_PBUD:
1915 case MACHO_N_INDR:
1916 /** @todo implement indirect and prebound symbols. */
1917 default:
1918 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
1919 }
1920
1921 return VINF_SUCCESS;
1922}
1923
1924
1925/**
1926 * Lookup a symbol in a 64-bit symbol table.
1927 *
1928 * @returns IPRT status code.
1929 * @param pThis
1930 * @param paSyms Pointer to the symbol table.
1931 * @param cSyms Number of symbols in the table.
1932 * @param pchStrings Pointer to the string table.
1933 * @param cchStrings Size of the string table.
1934 * @param BaseAddress Adjusted base address, see kLdrModQuerySymbol.
1935 * @param iSymbol See kLdrModQuerySymbol.
1936 * @param pchSymbol See kLdrModQuerySymbol.
1937 * @param cchSymbol See kLdrModQuerySymbol.
1938 * @param puValue See kLdrModQuerySymbol.
1939 * @param pfKind See kLdrModQuerySymbol.
1940 */
1941static int kldrModMachODoQuerySymbol64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
1942 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol,
1943 const char *pchSymbol, uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind)
1944{
1945 /*
1946 * Find a valid symbol matching the search criteria.
1947 */
1948 if (iSymbol == UINT32_MAX)
1949 {
1950 /* simplify validation. */
1951 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
1952 if (cchStrings <= cchSymbol + 1)
1953 return VERR_SYMBOL_NOT_FOUND;
1954 cchStrings -= cchSymbol + 1;
1955
1956 /* external symbols are usually at the end, so search the other way. */
1957 for (iSymbol = cSyms - 1; iSymbol != UINT32_MAX; iSymbol--)
1958 {
1959 const char *psz;
1960
1961 /* Skip irrellevant and non-public symbols. */
1962 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1963 continue;
1964 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1965 continue;
1966 if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
1967 continue;
1968 if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
1969 continue;
1970
1971 /* get name */
1972 if (!paSyms[iSymbol].n_un.n_strx)
1973 continue;
1974 if ((uint32_t)paSyms[iSymbol].n_un.n_strx >= cchStrings)
1975 continue;
1976 psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
1977 if (psz[cchSymbol + 1])
1978 continue;
1979 if (*psz != '_' || memcmp(psz + 1, pchSymbol, cchSymbol))
1980 continue;
1981
1982 /* match! */
1983 break;
1984 }
1985 if (iSymbol == UINT32_MAX)
1986 return VERR_SYMBOL_NOT_FOUND;
1987 }
1988 else
1989 {
1990 if (iSymbol >= cSyms)
1991 return VERR_SYMBOL_NOT_FOUND;
1992 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1993 return VERR_SYMBOL_NOT_FOUND;
1994 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1995 return VERR_SYMBOL_NOT_FOUND;
1996 }
1997
1998 /*
1999 * Calc the return values.
2000 */
2001 if (pfKind)
2002 {
2003 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
2004 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
2005 *pfKind = RTLDRSYMKIND_32BIT | RTLDRSYMKIND_NO_TYPE;
2006 else
2007 *pfKind = RTLDRSYMKIND_64BIT | RTLDRSYMKIND_NO_TYPE;
2008 if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
2009 *pfKind |= RTLDRSYMKIND_WEAK;
2010 }
2011
2012 switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
2013 {
2014 case MACHO_N_SECT:
2015 {
2016 PRTLDRMODMACHOSECT pSect;
2017 RTLDRADDR offSect;
2018 RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSymbol].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2019 pSect = &pThis->paSections[paSyms[iSymbol].n_sect - 1];
2020
2021 offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
2022 RTLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
2023 || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
2024 && offSect == 0U - pSect->RVA
2025 && pThis->uEffFileType != MH_OBJECT),
2026 VERR_LDRMACHO_BAD_SYMBOL);
2027 if (puValue)
2028 *puValue = BaseAddress + pSect->RVA + offSect;
2029
2030 if ( pfKind
2031 && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
2032 *pfKind = (*pfKind & ~RTLDRSYMKIND_TYPE_MASK) | RTLDRSYMKIND_CODE;
2033 break;
2034 }
2035
2036 case MACHO_N_ABS:
2037 if (puValue)
2038 *puValue = paSyms[iSymbol].n_value;
2039 /*if (pfKind)
2040 pfKind |= RTLDRSYMKIND_ABS;*/
2041 break;
2042
2043 case MACHO_N_PBUD:
2044 case MACHO_N_INDR:
2045 /** @todo implement indirect and prebound symbols. */
2046 default:
2047 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2048 }
2049
2050 return VINF_SUCCESS;
2051}
2052
2053
2054/**
2055 * @interface_method_impl{RTLDROPS,pfnEnumSymbols}
2056 */
2057static DECLCALLBACK(int) rtldrMachO_EnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFlags, const void *pvBits,
2058 RTUINTPTR BaseAddress, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
2059{
2060 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2061 RT_NOREF(pvBits);
2062
2063 /*
2064 * Resolve defaults.
2065 */
2066 kldrModMachOAdjustBaseAddress(pThis, &BaseAddress);
2067
2068 /*
2069 * Take action according to file type.
2070 */
2071 int rc;
2072 if ( pThis->Hdr.filetype == MH_OBJECT
2073 || pThis->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
2074 || pThis->Hdr.filetype == MH_DYLIB
2075 || pThis->Hdr.filetype == MH_BUNDLE
2076 || pThis->Hdr.filetype == MH_DSYM
2077 || pThis->Hdr.filetype == MH_KEXT_BUNDLE)
2078 {
2079 rc = kldrModMachOLoadObjSymTab(pThis);
2080 if (RT_SUCCESS(rc))
2081 {
2082 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
2083 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
2084 rc = kldrModMachODoEnumSymbols32Bit(pThis, (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols,
2085 pThis->pchStrings, pThis->cchStrings, BaseAddress,
2086 fFlags, pfnCallback, pvUser);
2087 else
2088 rc = kldrModMachODoEnumSymbols64Bit(pThis, (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols,
2089 pThis->pchStrings, pThis->cchStrings, BaseAddress,
2090 fFlags, pfnCallback, pvUser);
2091 }
2092 }
2093 else
2094 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2095
2096 return rc;
2097}
2098
2099
2100/**
2101 * Enum a 32-bit symbol table.
2102 *
2103 * @returns IPRT status code.
2104 * @param pThis
2105 * @param paSyms Pointer to the symbol table.
2106 * @param cSyms Number of symbols in the table.
2107 * @param pchStrings Pointer to the string table.
2108 * @param cchStrings Size of the string table.
2109 * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols.
2110 * @param fFlags See kLdrModEnumSymbols.
2111 * @param pfnCallback See kLdrModEnumSymbols.
2112 * @param pvUser See kLdrModEnumSymbols.
2113 */
2114static int kldrModMachODoEnumSymbols32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
2115 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
2116 uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
2117{
2118 const uint32_t fKindBase = pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
2119 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
2120 ? RTLDRSYMKIND_32BIT : RTLDRSYMKIND_64BIT;
2121 uint32_t iSym;
2122 int rc;
2123
2124 /*
2125 * Iterate the symbol table.
2126 */
2127 for (iSym = 0; iSym < cSyms; iSym++)
2128 {
2129 uint32_t fKind;
2130 RTLDRADDR uValue;
2131 const char *psz;
2132 size_t cch;
2133
2134 /* Skip debug symbols and undefined symbols. */
2135 if (paSyms[iSym].n_type & MACHO_N_STAB)
2136 continue;
2137 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2138 continue;
2139
2140 /* Skip non-public symbols unless they are requested explicitly. */
2141 if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_ALL))
2142 {
2143 if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
2144 continue;
2145 if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
2146 continue;
2147 if (!paSyms[iSym].n_un.n_strx)
2148 continue;
2149 }
2150
2151 /*
2152 * Gather symbol info
2153 */
2154
2155 /* name */
2156 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2157 psz = &pchStrings[paSyms[iSym].n_un.n_strx];
2158 cch = strlen(psz);
2159 if (!cch)
2160 psz = NULL;
2161
2162 /* kind & value */
2163 fKind = fKindBase;
2164 if (paSyms[iSym].n_desc & N_WEAK_DEF)
2165 fKind |= RTLDRSYMKIND_WEAK;
2166 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
2167 {
2168 case MACHO_N_SECT:
2169 {
2170 PRTLDRMODMACHOSECT pSect;
2171 RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSym].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2172 pSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
2173
2174 uValue = paSyms[iSym].n_value - pSect->LinkAddress;
2175 RTLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
2176 || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
2177 && uValue == 0U - pSect->RVA
2178 && pThis->uEffFileType != MH_OBJECT),
2179 VERR_LDRMACHO_BAD_SYMBOL);
2180 uValue += BaseAddress + pSect->RVA;
2181
2182 if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
2183 fKind |= RTLDRSYMKIND_CODE;
2184 else
2185 fKind |= RTLDRSYMKIND_NO_TYPE;
2186 break;
2187 }
2188
2189 case MACHO_N_ABS:
2190 uValue = paSyms[iSym].n_value;
2191 fKind |= RTLDRSYMKIND_NO_TYPE /*RTLDRSYMKIND_ABS*/;
2192 break;
2193
2194 case MACHO_N_PBUD:
2195 case MACHO_N_INDR:
2196 /** @todo implement indirect and prebound symbols. */
2197 default:
2198 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2199 }
2200
2201 /*
2202 * Do callback.
2203 */
2204 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
2205 if (cch > 1 && *psz == '_')
2206 psz++;
2207 rc = pfnCallback(&pThis->Core, psz, iSym, uValue/*, fKind*/, pvUser);
2208 if (rc != VINF_SUCCESS)
2209 return rc;
2210 }
2211 return VINF_SUCCESS;
2212}
2213
2214
2215/**
2216 * Enum a 64-bit symbol table.
2217 *
2218 * @returns IPRT status code.
2219 * @param pThis
2220 * @param paSyms Pointer to the symbol table.
2221 * @param cSyms Number of symbols in the table.
2222 * @param pchStrings Pointer to the string table.
2223 * @param cchStrings Size of the string table.
2224 * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols.
2225 * @param fFlags See kLdrModEnumSymbols.
2226 * @param pfnCallback See kLdrModEnumSymbols.
2227 * @param pvUser See kLdrModEnumSymbols.
2228 */
2229static int kldrModMachODoEnumSymbols64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
2230 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
2231 uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
2232{
2233 const uint32_t fKindBase = pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
2234 || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE
2235 ? RTLDRSYMKIND_64BIT : RTLDRSYMKIND_32BIT;
2236 uint32_t iSym;
2237 int rc;
2238
2239 /*
2240 * Iterate the symbol table.
2241 */
2242 for (iSym = 0; iSym < cSyms; iSym++)
2243 {
2244 uint32_t fKind;
2245 RTLDRADDR uValue;
2246 const char *psz;
2247 size_t cch;
2248
2249 /* Skip debug symbols and undefined symbols. */
2250 if (paSyms[iSym].n_type & MACHO_N_STAB)
2251 continue;
2252 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2253 continue;
2254
2255 /* Skip non-public symbols unless they are requested explicitly. */
2256 if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_ALL))
2257 {
2258 if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
2259 continue;
2260 if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
2261 continue;
2262 if (!paSyms[iSym].n_un.n_strx)
2263 continue;
2264 }
2265
2266 /*
2267 * Gather symbol info
2268 */
2269
2270 /* name */
2271 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2272 psz = &pchStrings[paSyms[iSym].n_un.n_strx];
2273 cch = strlen(psz);
2274 if (!cch)
2275 psz = NULL;
2276
2277 /* kind & value */
2278 fKind = fKindBase;
2279 if (paSyms[iSym].n_desc & N_WEAK_DEF)
2280 fKind |= RTLDRSYMKIND_WEAK;
2281 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
2282 {
2283 case MACHO_N_SECT:
2284 {
2285 PRTLDRMODMACHOSECT pSect;
2286 RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSym].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2287 pSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
2288
2289 uValue = paSyms[iSym].n_value - pSect->LinkAddress;
2290 RTLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
2291 || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
2292 && uValue == 0U - pSect->RVA
2293 && pThis->uEffFileType != MH_OBJECT),
2294 VERR_LDRMACHO_BAD_SYMBOL);
2295 uValue += BaseAddress + pSect->RVA;
2296
2297 if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
2298 fKind |= RTLDRSYMKIND_CODE;
2299 else
2300 fKind |= RTLDRSYMKIND_NO_TYPE;
2301 break;
2302 }
2303
2304 case MACHO_N_ABS:
2305 uValue = paSyms[iSym].n_value;
2306 fKind |= RTLDRSYMKIND_NO_TYPE /*RTLDRSYMKIND_ABS*/;
2307 break;
2308
2309 case MACHO_N_PBUD:
2310 case MACHO_N_INDR:
2311 /** @todo implement indirect and prebound symbols. */
2312 default:
2313 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2314 }
2315
2316 /*
2317 * Do callback.
2318 */
2319 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
2320 if (cch > 1 && *psz == '_')
2321 psz++;
2322 rc = pfnCallback(&pThis->Core, psz, iSym, uValue/*, fKind*/, pvUser);
2323 if (rc != VINF_SUCCESS)
2324 return rc;
2325 }
2326 return VINF_SUCCESS;
2327}
2328
2329#if 0
2330
2331/** @copydoc kLdrModGetImport */
2332static int kldrModMachOGetImport(PRTLDRMODINTERNAL pMod, const void *pvBits, uint32_t iImport, char *pszName, size_t cchName)
2333{
2334 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2335 RT_NOREF(pvBits);
2336 RT_NOREF(iImport);
2337 RT_NOREF(pszName);
2338 RT_NOREF(cchName);
2339
2340 if (pThis->Hdr.filetype == MH_OBJECT)
2341 return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
2342
2343 /* later */
2344 return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
2345}
2346
2347
2348
2349/** @copydoc kLdrModNumberOfImports */
2350static int32_t kldrModMachONumberOfImports(PRTLDRMODINTERNAL pMod, const void *pvBits)
2351{
2352 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2353 RT_NOREF(pvBits);
2354
2355 if (pThis->Hdr.filetype == MH_OBJECT)
2356 return VINF_SUCCESS;
2357
2358 /* later */
2359 return VINF_SUCCESS;
2360}
2361
2362
2363/** @copydoc kLdrModGetStackInfo */
2364static int kldrModMachOGetStackInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
2365{
2366 /*PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);*/
2367 RT_NOREF(pMod);
2368 RT_NOREF(pvBits);
2369 RT_NOREF(BaseAddress);
2370
2371 pStackInfo->Address = NIL_RTLDRADDR;
2372 pStackInfo->LinkAddress = NIL_RTLDRADDR;
2373 pStackInfo->cbStack = pStackInfo->cbStackThread = 0;
2374 /* later */
2375
2376 return VINF_SUCCESS;
2377}
2378
2379
2380/** @copydoc kLdrModQueryMainEntrypoint */
2381static int kldrModMachOQueryMainEntrypoint(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PRTLDRADDR pMainEPAddress)
2382{
2383#if 0
2384 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2385 int rc;
2386
2387 /*
2388 * Resolve base address alias if any.
2389 */
2390 rc = kldrModMachOBitsAndBaseAddress(pThis, NULL, &BaseAddress);
2391 if (RT_FAILURE(rc))
2392 return rc;
2393
2394 /*
2395 * Convert the address from the header.
2396 */
2397 *pMainEPAddress = pThis->Hdrs.OptionalHeader.AddressOfEntryPoint
2398 ? BaseAddress + pThis->Hdrs.OptionalHeader.AddressOfEntryPoint
2399 : NIL_RTLDRADDR;
2400#else
2401 *pMainEPAddress = NIL_RTLDRADDR;
2402 RT_NOREF(pvBits);
2403 RT_NOREF(BaseAddress);
2404 RT_NOREF(pMod);
2405#endif
2406 return VINF_SUCCESS;
2407}
2408
2409#endif
2410
2411
2412/**
2413 * @interface_method_impl{RTLDROPS,pfnEnumDbgInfo}
2414 */
2415static DECLCALLBACK(int) rtldrMachO_EnumDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, PFNRTLDRENUMDBG pfnCallback, void *pvUser)
2416{
2417 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2418 int rc = VINF_SUCCESS;
2419 uint32_t iSect;
2420 RT_NOREF(pvBits);
2421
2422 for (iSect = 0; iSect < pThis->cSections; iSect++)
2423 {
2424 /* (32-bit & 64-bit starts the same way) */
2425 section_32_t *pMachOSect = (section_32_t *)pThis->paSections[iSect].pvMachoSection;
2426 char szTmp[sizeof(pMachOSect->sectname) + 1];
2427
2428 if (strcmp(pMachOSect->segname, "__DWARF"))
2429 continue;
2430
2431 memcpy(szTmp, pMachOSect->sectname, sizeof(pMachOSect->sectname));
2432 szTmp[sizeof(pMachOSect->sectname)] = '\0';
2433
2434 RTLDRDBGINFO DbgInfo;
2435 DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF;
2436 DbgInfo.iDbgInfo = iSect;
2437 DbgInfo.LinkAddress = pThis->paSections[iSect].LinkAddress;
2438 DbgInfo.cb = pThis->paSections[iSect].cb;
2439 DbgInfo.pszExtFile = NULL;
2440 DbgInfo.u.Dwarf.pszSection = szTmp;
2441 rc = pfnCallback(&pThis->Core, &DbgInfo, pvUser);
2442 if (rc != VINF_SUCCESS)
2443 break;
2444 }
2445
2446 return rc;
2447}
2448
2449#if 0
2450
2451/** @copydoc kLdrModHasDbgInfo */
2452static int kldrModMachOHasDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits)
2453{
2454 /*PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);*/
2455
2456#if 0
2457 /*
2458 * Base this entirely on the presence of a debug directory.
2459 */
2460 if ( pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size
2461 < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */
2462 || !pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)
2463 return KLDR_ERR_NO_DEBUG_INFO;
2464 return VINF_SUCCESS;
2465#else
2466 RT_NOREF(pMod);
2467 RT_NOREF(pvBits);
2468 return VERR_LDR_NO_DEBUG_INFO;
2469#endif
2470}
2471
2472
2473/** @copydoc kLdrModMap */
2474static int kldrModMachOMap(PRTLDRMODINTERNAL pMod)
2475{
2476 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2477 unsigned fFixed;
2478 uint32_t i;
2479 void *pvBase;
2480 int rc;
2481
2482 if (!pThis->fCanLoad)
2483 return VERR_LDRMACHO_TODO;
2484
2485 /*
2486 * Already mapped?
2487 */
2488 if (pThis->pvMapping)
2489 return KLDR_ERR_ALREADY_MAPPED;
2490
2491 /*
2492 * Map it.
2493 */
2494 /* fixed image? */
2495 fFixed = pMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2496 || pMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2497 if (!fFixed)
2498 pvBase = NULL;
2499 else
2500 {
2501 pvBase = (void *)(uintptr_t)pMod->aSegments[0].LinkAddress;
2502 if ((uintptr_t)pvBase != pMod->aSegments[0].LinkAddress)
2503 return VERR_LDR_ADDRESS_OVERFLOW;
2504 }
2505
2506 /* try do the prepare */
2507 rc = kRdrMap(pMod->pRdr, &pvBase, pMod->cSegments, pMod->aSegments, fFixed);
2508 if (RT_FAILURE(rc))
2509 return rc;
2510
2511 /*
2512 * Update the segments with their map addresses.
2513 */
2514 for (i = 0; i < pMod->cSegments; i++)
2515 {
2516 if (pMod->aSegments[i].RVA != NIL_RTLDRADDR)
2517 pMod->aSegments[i].MapAddress = (uintptr_t)pvBase + (uintptr_t)pMod->aSegments[i].RVA;
2518 }
2519 pThis->pvMapping = pvBase;
2520
2521 return VINF_SUCCESS;
2522}
2523
2524
2525/** @copydoc kLdrModUnmap */
2526static int kldrModMachOUnmap(PRTLDRMODINTERNAL pMod)
2527{
2528 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2529 uint32_t i;
2530 int rc;
2531
2532 /*
2533 * Mapped?
2534 */
2535 if (!pThis->pvMapping)
2536 return KLDR_ERR_NOT_MAPPED;
2537
2538 /*
2539 * Try unmap the image.
2540 */
2541 rc = kRdrUnmap(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
2542 if (RT_FAILURE(rc))
2543 return rc;
2544
2545 /*
2546 * Update the segments to reflect that they aren't mapped any longer.
2547 */
2548 pThis->pvMapping = NULL;
2549 for (i = 0; i < pMod->cSegments; i++)
2550 pMod->aSegments[i].MapAddress = 0;
2551
2552 return VINF_SUCCESS;
2553}
2554
2555
2556/** @copydoc kLdrModAllocTLS */
2557static int kldrModMachOAllocTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
2558{
2559 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2560
2561 /*
2562 * Mapped?
2563 */
2564 if ( pvMapping == KLDRMOD_INT_MAP
2565 && !pThis->pvMapping )
2566 return KLDR_ERR_NOT_MAPPED;
2567 return VINF_SUCCESS;
2568}
2569
2570
2571/** @copydoc kLdrModFreeTLS */
2572static void kldrModMachOFreeTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
2573{
2574 RT_NOREF(pMod);
2575 RT_NOREF(pvMapping);
2576}
2577
2578
2579
2580/** @copydoc kLdrModReload */
2581static int kldrModMachOReload(PRTLDRMODINTERNAL pMod)
2582{
2583 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2584
2585 /*
2586 * Mapped?
2587 */
2588 if (!pThis->pvMapping)
2589 return KLDR_ERR_NOT_MAPPED;
2590
2591 /* the file provider does it all */
2592 return kRdrRefresh(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
2593}
2594
2595
2596/** @copydoc kLdrModFixupMapping */
2597static int kldrModMachOFixupMapping(PRTLDRMODINTERNAL pMod, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
2598{
2599 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2600 int rc, rc2;
2601
2602 /*
2603 * Mapped?
2604 */
2605 if (!pThis->pvMapping)
2606 return KLDR_ERR_NOT_MAPPED;
2607
2608 /*
2609 * Before doing anything we'll have to make all pages writable.
2610 */
2611 rc = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */);
2612 if (RT_FAILURE(rc))
2613 return rc;
2614
2615 /*
2616 * Resolve imports and apply base relocations.
2617 */
2618 rc = rtldrMachO_RelocateBits(pMod, pThis->pvMapping, (uintptr_t)pThis->pvMapping, pThis->LinkAddress,
2619 pfnGetImport, pvUser);
2620
2621 /*
2622 * Restore protection.
2623 */
2624 rc2 = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */);
2625 if (RT_SUCCESS(rc) && RT_FAILURE(rc2)
2626 rc = rc2;
2627 return rc;
2628}
2629
2630#endif
2631
2632/**
2633 * MH_OBJECT: Resolves undefined symbols (imports).
2634 *
2635 * @returns IPRT status code.
2636 * @param pThis The Mach-O module interpreter instance.
2637 * @param BaseAddress The module base address.
2638 * @param pfnGetImport The callback for resolving an imported symbol.
2639 * @param pvUser User argument to the callback.
2640 */
2641static int kldrModMachOObjDoImports(PRTLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
2642{
2643 const uint32_t cSyms = pThis->cSymbols;
2644 uint32_t iSym;
2645
2646 /*
2647 * Ensure that we've got the symbol table and section fixups handy.
2648 */
2649 int rc = kldrModMachOLoadObjSymTab(pThis);
2650 if (RT_FAILURE(rc))
2651 return rc;
2652
2653 /*
2654 * Iterate the symbol table and resolve undefined symbols.
2655 * We currently ignore REFERENCE_TYPE.
2656 */
2657 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
2658 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
2659 {
2660 macho_nlist_32_t *paSyms = (macho_nlist_32_t *)pThis->pvaSymbols;
2661 for (iSym = 0; iSym < cSyms; iSym++)
2662 {
2663 /* skip stabs */
2664 if (paSyms[iSym].n_type & MACHO_N_STAB)
2665 continue;
2666
2667 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2668 {
2669 const char *pszSymbol;
2670 size_t cchSymbol;
2671 //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
2672 RTLDRADDR Value = NIL_RTLDRADDR;
2673
2674 /** @todo Implement N_REF_TO_WEAK. */
2675 RTLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
2676
2677 /* Get the symbol name. */
2678 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2679 pszSymbol = &pThis->pchStrings[paSyms[iSym].n_un.n_strx];
2680 cchSymbol = strlen(pszSymbol);
2681
2682 /* Check for linker defined symbols relating to sections and segments. */
2683 if ( cchSymbol > sizeof("section$end$") - 1
2684 && *pszSymbol == 's'
2685 && memchr(pszSymbol, '$', cchSymbol))
2686 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
2687 else
2688 rc = VERR_SYMBOL_NOT_FOUND;
2689
2690 /* Ask the user for an address to the symbol. */
2691 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
2692 if (RT_FAILURE_NP(rc))
2693 rc = pfnGetImport(&pThis->Core, NULL /*pszModule*/, pszSymbol + (pszSymbol[0] == '_'),
2694 UINT32_MAX, &Value/*, &fKind*/, pvUser);
2695 if (RT_FAILURE(rc))
2696 {
2697 /* weak reference? */
2698 if (!(paSyms[iSym].n_desc & N_WEAK_REF))
2699 break;
2700 Value = 0;
2701 }
2702
2703 /* Update the symbol. */
2704 paSyms[iSym].n_value = (uint32_t)Value;
2705 if (paSyms[iSym].n_value != Value)
2706 {
2707 rc = VERR_LDR_ADDRESS_OVERFLOW;
2708 break;
2709 }
2710 }
2711 else if (paSyms[iSym].n_desc & N_WEAK_DEF)
2712 {
2713 /** @todo implement weak symbols. */
2714 /*return VERR_LDRMACHO_TODO; - ignored for now. */
2715 }
2716 }
2717 }
2718 else
2719 {
2720 /* (Identical to the 32-bit code, just different paSym type. (and n_strx is unsigned)) */
2721 macho_nlist_64_t *paSyms = (macho_nlist_64_t *)pThis->pvaSymbols;
2722 for (iSym = 0; iSym < cSyms; iSym++)
2723 {
2724 /* skip stabs */
2725 if (paSyms[iSym].n_type & MACHO_N_STAB)
2726 continue;
2727
2728 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2729 {
2730 const char *pszSymbol;
2731 size_t cchSymbol;
2732 //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
2733 RTLDRADDR Value = NIL_RTLDRADDR;
2734
2735 /** @todo Implement N_REF_TO_WEAK. */
2736 RTLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
2737
2738 /* Get the symbol name. */
2739 RTLDRMODMACHO_CHECK_RETURN(paSyms[iSym].n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2740 pszSymbol = &pThis->pchStrings[paSyms[iSym].n_un.n_strx];
2741 cchSymbol = strlen(pszSymbol);
2742
2743 /* Check for linker defined symbols relating to sections and segments. */
2744 if ( cchSymbol > sizeof("section$end$") - 1
2745 && *pszSymbol == 's'
2746 && memchr(pszSymbol, '$', cchSymbol))
2747 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
2748 else
2749 rc = VERR_SYMBOL_NOT_FOUND;
2750
2751 /* Ask the user for an address to the symbol. */
2752 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
2753 if (RT_FAILURE_NP(rc))
2754 rc = pfnGetImport(&pThis->Core, NULL, pszSymbol + (*pszSymbol == '_'),
2755 UINT32_MAX, &Value, /*&fKind,*/ pvUser);
2756 if (RT_FAILURE(rc))
2757 {
2758 /* weak reference? */
2759 if (!(paSyms[iSym].n_desc & N_WEAK_REF))
2760 break;
2761 Value = 0;
2762 }
2763
2764 /* Update the symbol. */
2765 paSyms[iSym].n_value = Value;
2766 if (paSyms[iSym].n_value != Value)
2767 {
2768 rc = VERR_LDR_ADDRESS_OVERFLOW;
2769 break;
2770 }
2771 }
2772 else if (paSyms[iSym].n_desc & N_WEAK_DEF)
2773 {
2774 /** @todo implement weak symbols. */
2775 /*return VERR_LDRMACHO_TODO; - ignored for now. */
2776 }
2777 }
2778 }
2779
2780 return rc;
2781}
2782
2783
2784/**
2785 * MH_OBJECT: Applies base relocations to a (unprotected) image mapping.
2786 *
2787 * @returns IPRT status code.
2788 * @param pThis The Mach-O module interpreter instance.
2789 * @param pvMapping The mapping to fixup.
2790 * @param NewBaseAddress The address to fixup the mapping to.
2791 */
2792static int kldrModMachOObjDoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress)
2793{
2794 /*
2795 * Ensure that we've got the symbol table and section fixups handy.
2796 */
2797 int rc = kldrModMachOLoadObjSymTab(pThis);
2798 if (RT_FAILURE(rc))
2799 return rc;
2800
2801 /*
2802 * Iterate over the segments and their sections and apply fixups.
2803 */
2804 rc = VINF_SUCCESS;
2805 for (uint32_t iSeg = 0; RT_SUCCESS(rc) && iSeg < pThis->cSegments; iSeg++)
2806 {
2807 PRTLDRMODMACHOSEG pSeg = &pThis->aSegments[iSeg];
2808 for (uint32_t iSect = 0; iSect < pSeg->cSections; iSect++)
2809 {
2810 PRTLDRMODMACHOSECT pSect = &pSeg->paSections[iSect];
2811
2812 /* skip sections without fixups. */
2813 if (!pSect->cFixups)
2814 continue;
2815
2816 /* lazy load (and endian convert) the fixups. */
2817 if (!pSect->paFixups)
2818 {
2819 rc = kldrModMachOLoadFixups(pThis, pSect->offFixups, pSect->cFixups, &pSect->paFixups);
2820 if (RT_FAILURE(rc))
2821 break;
2822 }
2823
2824 /*
2825 * Apply the fixups.
2826 */
2827 uint8_t *pbSectBits = (uint8_t *)pvMapping + (uintptr_t)pSect->RVA;
2828 if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE) /** @todo this aint right. */
2829 rc = kldrModMachOFixupSectionGeneric32Bit(pThis, pbSectBits, pSect,
2830 (macho_nlist_32_t *)pThis->pvaSymbols,
2831 pThis->cSymbols, NewBaseAddress);
2832 else if ( pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
2833 && pThis->Hdr.cputype == CPU_TYPE_X86_64)
2834 rc = kldrModMachOFixupSectionAMD64(pThis, pbSectBits, pSect,
2835 (macho_nlist_64_t *)pThis->pvaSymbols,
2836 pThis->cSymbols, NewBaseAddress);
2837 else
2838 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2839 if (RT_FAILURE(rc))
2840 break;
2841 }
2842 }
2843
2844 return rc;
2845}
2846
2847
2848/**
2849 * Applies generic fixups to a section in an image of the same endian-ness
2850 * as the host CPU.
2851 *
2852 * @returns IPRT status code.
2853 * @param pThis The Mach-O module interpreter instance.
2854 * @param pbSectBits Pointer to the section bits.
2855 * @param pFixupSect The section being fixed up.
2856 * @param paSyms Pointer to the symbol table.
2857 * @param cSyms Number of symbols.
2858 * @param NewBaseAddress The new base image address.
2859 */
2860static int kldrModMachOFixupSectionGeneric32Bit(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, PRTLDRMODMACHOSECT pFixupSect,
2861 macho_nlist_32_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
2862{
2863 const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
2864 const uint32_t cFixups = pFixupSect->cFixups;
2865 size_t cbSectBits = (size_t)pFixupSect->cb;
2866 const uint8_t *pbSectVirginBits;
2867 uint32_t iFixup;
2868 RTLDRADDR SymAddr = ~(RTLDRADDR)0;
2869 int rc;
2870
2871 /*
2872 * Find the virgin bits.
2873 */
2874 if (pFixupSect->offFile != -1)
2875 {
2876 rc = kldrModMachOMapVirginBits(pThis);
2877 if (RT_FAILURE(rc))
2878 return rc;
2879 pbSectVirginBits = (const uint8_t *)pThis->pvBits + pFixupSect->offFile;
2880 }
2881 else
2882 pbSectVirginBits = NULL;
2883
2884 /*
2885 * Iterate the fixups and apply them.
2886 */
2887 for (iFixup = 0; iFixup < cFixups; iFixup++)
2888 {
2889 RTPTRUNION uFix;
2890 RTPTRUNION uFixVirgin;
2891 union
2892 {
2893 macho_relocation_info_t r;
2894 scattered_relocation_info_t s;
2895 } Fixup;
2896 Fixup.r = paFixups[iFixup];
2897
2898 if (!(Fixup.r.r_address & R_SCATTERED))
2899 {
2900 /* sanity */
2901 if ((uint32_t)Fixup.r.r_address >= cbSectBits)
2902 return VERR_LDR_BAD_FIXUP;
2903
2904 /* calc fixup addresses. */
2905 uFix.pv = pbSectBits + Fixup.r.r_address;
2906 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.r.r_address : 0;
2907
2908 /*
2909 * Calc the symbol value.
2910 */
2911 /* Calc the linked symbol address / addend. */
2912 switch (Fixup.r.r_length)
2913 {
2914 /** @todo Deal with unaligned accesses on non x86 platforms. */
2915 case 0: SymAddr = *uFixVirgin.pi8; break;
2916 case 1: SymAddr = *uFixVirgin.pi16; break;
2917 case 2: SymAddr = *uFixVirgin.pi32; break;
2918 case 3: SymAddr = *uFixVirgin.pi64; break;
2919 }
2920 if (Fixup.r.r_pcrel)
2921 SymAddr += Fixup.r.r_address + pFixupSect->LinkAddress;
2922
2923 /* Add symbol / section address. */
2924 if (Fixup.r.r_extern)
2925 {
2926 const macho_nlist_32_t *pSym;
2927 if (Fixup.r.r_symbolnum >= cSyms)
2928 return VERR_LDR_BAD_FIXUP;
2929 pSym = &paSyms[Fixup.r.r_symbolnum];
2930
2931 if (pSym->n_type & MACHO_N_STAB)
2932 return VERR_LDR_BAD_FIXUP;
2933
2934 switch (pSym->n_type & MACHO_N_TYPE)
2935 {
2936 case MACHO_N_SECT:
2937 {
2938 PRTLDRMODMACHOSECT pSymSect;
2939 RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2940 pSymSect = &pThis->paSections[pSym->n_sect - 1];
2941
2942 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
2943 break;
2944 }
2945
2946 case MACHO_N_UNDF:
2947 case MACHO_N_ABS:
2948 SymAddr += pSym->n_value;
2949 break;
2950
2951 case MACHO_N_INDR:
2952 case MACHO_N_PBUD:
2953 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2954 default:
2955 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
2956 }
2957 }
2958 else if (Fixup.r.r_symbolnum != R_ABS)
2959 {
2960 PRTLDRMODMACHOSECT pSymSect;
2961 if (Fixup.r.r_symbolnum > pThis->cSections)
2962 return VERR_LDR_BAD_FIXUP;
2963 pSymSect = &pThis->paSections[Fixup.r.r_symbolnum - 1];
2964
2965 SymAddr -= pSymSect->LinkAddress;
2966 SymAddr += pSymSect->RVA + NewBaseAddress;
2967 }
2968
2969 /* adjust for PC relative */
2970 if (Fixup.r.r_pcrel)
2971 SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
2972 }
2973 else
2974 {
2975 PRTLDRMODMACHOSECT pSymSect;
2976 uint32_t iSymSect;
2977 RTLDRADDR Value;
2978
2979 /* sanity */
2980 RTLDRMODMACHO_ASSERT(Fixup.s.r_scattered);
2981 if ((uint32_t)Fixup.s.r_address >= cbSectBits)
2982 return VERR_LDR_BAD_FIXUP;
2983
2984 /* calc fixup addresses. */
2985 uFix.pv = pbSectBits + Fixup.s.r_address;
2986 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.s.r_address : 0;
2987
2988 /*
2989 * Calc the symbol value.
2990 */
2991 /* The addend is stored in the code. */
2992 switch (Fixup.s.r_length)
2993 {
2994 case 0: SymAddr = *uFixVirgin.pi8; break;
2995 case 1: SymAddr = *uFixVirgin.pi16; break;
2996 case 2: SymAddr = *uFixVirgin.pi32; break;
2997 case 3: SymAddr = *uFixVirgin.pi64; break;
2998 }
2999 if (Fixup.s.r_pcrel)
3000 SymAddr += Fixup.s.r_address;
3001 Value = Fixup.s.r_value;
3002 SymAddr -= Value; /* (-> addend only) */
3003
3004 /* Find the section number from the r_value. */
3005 pSymSect = NULL;
3006 for (iSymSect = 0; iSymSect < pThis->cSections; iSymSect++)
3007 {
3008 RTLDRADDR off = Value - pThis->paSections[iSymSect].LinkAddress;
3009 if (off < pThis->paSections[iSymSect].cb)
3010 {
3011 pSymSect = &pThis->paSections[iSymSect];
3012 break;
3013 }
3014 else if (off == pThis->paSections[iSymSect].cb) /* edge case */
3015 pSymSect = &pThis->paSections[iSymSect];
3016 }
3017 if (!pSymSect)
3018 return VERR_LDR_BAD_FIXUP;
3019
3020 /* Calc the symbol address. */
3021 SymAddr += Value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3022 if (Fixup.s.r_pcrel)
3023 SymAddr -= Fixup.s.r_address + pFixupSect->RVA + NewBaseAddress;
3024
3025 Fixup.r.r_length = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_length;
3026 Fixup.r.r_type = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_type;
3027 }
3028
3029 /*
3030 * Write back the fixed up value.
3031 */
3032 if (Fixup.r.r_type == GENERIC_RELOC_VANILLA)
3033 {
3034 switch (Fixup.r.r_length)
3035 {
3036 case 0: *uFix.pu8 = (uint8_t)SymAddr; break;
3037 case 1: *uFix.pu16 = (uint16_t)SymAddr; break;
3038 case 2: *uFix.pu32 = (uint32_t)SymAddr; break;
3039 case 3: *uFix.pu64 = (uint64_t)SymAddr; break;
3040 }
3041 }
3042 else if (Fixup.r.r_type <= GENERIC_RELOC_LOCAL_SECTDIFF)
3043 return VERR_LDRMACHO_UNSUPPORTED_FIXUP_TYPE;
3044 else
3045 return VERR_LDR_BAD_FIXUP;
3046 }
3047
3048 return VINF_SUCCESS;
3049}
3050
3051
3052/**
3053 * Applies AMD64 fixups to a section.
3054 *
3055 * @returns IPRT status code.
3056 * @param pThis The Mach-O module interpreter instance.
3057 * @param pbSectBits Pointer to the section bits.
3058 * @param pFixupSect The section being fixed up.
3059 * @param paSyms Pointer to the symbol table.
3060 * @param cSyms Number of symbols.
3061 * @param NewBaseAddress The new base image address.
3062 */
3063static int kldrModMachOFixupSectionAMD64(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, PRTLDRMODMACHOSECT pFixupSect,
3064 macho_nlist_64_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
3065{
3066 const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
3067 const uint32_t cFixups = pFixupSect->cFixups;
3068 size_t cbSectBits = (size_t)pFixupSect->cb;
3069 const uint8_t *pbSectVirginBits;
3070 uint32_t iFixup;
3071 RTLDRADDR SymAddr;
3072 int rc;
3073
3074 /*
3075 * Find the virgin bits.
3076 */
3077 if (pFixupSect->offFile != -1)
3078 {
3079 rc = kldrModMachOMapVirginBits(pThis);
3080 if (RT_FAILURE(rc))
3081 return rc;
3082 pbSectVirginBits = (const uint8_t *)pThis->pvBits + pFixupSect->offFile;
3083 }
3084 else
3085 pbSectVirginBits = NULL;
3086
3087 /*
3088 * Iterate the fixups and apply them.
3089 */
3090 for (iFixup = 0; iFixup < cFixups; iFixup++)
3091 {
3092 union
3093 {
3094 macho_relocation_info_t r;
3095 scattered_relocation_info_t s;
3096 } Fixup;
3097 Fixup.r = paFixups[iFixup];
3098
3099 /* AMD64 doesn't use scattered fixups. */
3100 RTLDRMODMACHO_CHECK_RETURN(!(Fixup.r.r_address & R_SCATTERED), VERR_LDR_BAD_FIXUP);
3101
3102 /* sanity */
3103 RTLDRMODMACHO_CHECK_RETURN((uint32_t)Fixup.r.r_address < cbSectBits, VERR_LDR_BAD_FIXUP);
3104
3105 /* calc fixup addresses. */
3106 RTPTRUNION uFix;
3107 uFix.pv = pbSectBits + Fixup.r.r_address;
3108 RTPTRUNION uFixVirgin;
3109 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.r.r_address : 0;
3110
3111 /*
3112 * Calc the symbol value.
3113 */
3114 /* Calc the linked symbol address / addend. */
3115 switch (Fixup.r.r_length)
3116 {
3117 /** @todo Deal with unaligned accesses on non x86 platforms. */
3118 case 2: SymAddr = *uFixVirgin.pi32; break;
3119 case 3: SymAddr = *uFixVirgin.pi64; break;
3120 default:
3121 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3122 }
3123
3124 /* Add symbol / section address. */
3125 if (Fixup.r.r_extern)
3126 {
3127 const macho_nlist_64_t *pSym;
3128
3129 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
3130 pSym = &paSyms[Fixup.r.r_symbolnum];
3131 RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_BAD_FIXUP);
3132
3133 switch (Fixup.r.r_type)
3134 {
3135 /* GOT references just needs to have their symbol verified.
3136 Later, we'll optimize GOT building here using a parallel sym->got array. */
3137 case X86_64_RELOC_GOT_LOAD:
3138 case X86_64_RELOC_GOT:
3139 switch (pSym->n_type & MACHO_N_TYPE)
3140 {
3141 case MACHO_N_SECT:
3142 case MACHO_N_UNDF:
3143 case MACHO_N_ABS:
3144 break;
3145 case MACHO_N_INDR:
3146 case MACHO_N_PBUD:
3147 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3148 default:
3149 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3150 }
3151 SymAddr = sizeof(uint64_t) * Fixup.r.r_symbolnum + pThis->GotRVA + NewBaseAddress;
3152 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_BAD_FIXUP);
3153 SymAddr -= 4;
3154 break;
3155
3156 /* Verify the r_pcrel field for signed fixups on the way into the default case. */
3157 case X86_64_RELOC_BRANCH:
3158 case X86_64_RELOC_SIGNED:
3159 case X86_64_RELOC_SIGNED_1:
3160 case X86_64_RELOC_SIGNED_2:
3161 case X86_64_RELOC_SIGNED_4:
3162 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3163 RT_FALL_THRU();
3164 default:
3165 {
3166 /* Adjust with fixup specific addend and vierfy unsigned/r_pcrel. */
3167 switch (Fixup.r.r_type)
3168 {
3169 case X86_64_RELOC_UNSIGNED:
3170 RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3171 break;
3172 case X86_64_RELOC_BRANCH:
3173 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_BAD_FIXUP);
3174 SymAddr -= 4;
3175 break;
3176 case X86_64_RELOC_SIGNED:
3177 case X86_64_RELOC_SIGNED_1:
3178 case X86_64_RELOC_SIGNED_2:
3179 case X86_64_RELOC_SIGNED_4:
3180 SymAddr -= 4;
3181 break;
3182 default:
3183 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3184 }
3185
3186 switch (pSym->n_type & MACHO_N_TYPE)
3187 {
3188 case MACHO_N_SECT:
3189 {
3190 PRTLDRMODMACHOSECT pSymSect;
3191 RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3192 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3193 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3194 break;
3195 }
3196
3197 case MACHO_N_UNDF:
3198 /* branch to an external symbol may have to take a short detour. */
3199 if ( Fixup.r.r_type == X86_64_RELOC_BRANCH
3200 && SymAddr + Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress
3201 - pSym->n_value
3202 + UINT64_C(0x80000000)
3203 >= UINT64_C(0xffffff20))
3204 SymAddr += pThis->cbJmpStub * Fixup.r.r_symbolnum + pThis->JmpStubsRVA + NewBaseAddress;
3205 else
3206 SymAddr += pSym->n_value;
3207 break;
3208
3209 case MACHO_N_ABS:
3210 SymAddr += pSym->n_value;
3211 break;
3212
3213 case MACHO_N_INDR:
3214 case MACHO_N_PBUD:
3215 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3216 default:
3217 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3218 }
3219 break;
3220 }
3221
3222 /*
3223 * This is a weird customer, it will always be follows by an UNSIGNED fixup.
3224 */
3225 case X86_64_RELOC_SUBTRACTOR:
3226 {
3227 macho_relocation_info_t Fixup2;
3228
3229 /* Deal with the SUBTRACT symbol first, by subtracting it from SymAddr. */
3230 switch (pSym->n_type & MACHO_N_TYPE)
3231 {
3232 case MACHO_N_SECT:
3233 {
3234 PRTLDRMODMACHOSECT pSymSect;
3235 RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3236 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3237 SymAddr -= pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3238 break;
3239 }
3240
3241 case MACHO_N_UNDF:
3242 case MACHO_N_ABS:
3243 SymAddr -= pSym->n_value;
3244 break;
3245
3246 case MACHO_N_INDR:
3247 case MACHO_N_PBUD:
3248 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3249 default:
3250 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3251 }
3252
3253 /* Load the 2nd fixup, check sanity. */
3254 iFixup++;
3255 RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel && iFixup < cFixups, VERR_LDR_BAD_FIXUP);
3256 Fixup2 = paFixups[iFixup];
3257 RTLDRMODMACHO_CHECK_RETURN( Fixup2.r_address == Fixup.r.r_address
3258 && Fixup2.r_length == Fixup.r.r_length
3259 && Fixup2.r_type == X86_64_RELOC_UNSIGNED
3260 && !Fixup2.r_pcrel
3261 && Fixup2.r_symbolnum < cSyms,
3262 VERR_LDR_BAD_FIXUP);
3263
3264 if (Fixup2.r_extern)
3265 {
3266 RTLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
3267 pSym = &paSyms[Fixup2.r_symbolnum];
3268 RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_BAD_FIXUP);
3269
3270 /* Add it's value to SymAddr. */
3271 switch (pSym->n_type & MACHO_N_TYPE)
3272 {
3273 case MACHO_N_SECT:
3274 {
3275 PRTLDRMODMACHOSECT pSymSect;
3276 RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3277 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3278 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3279 break;
3280 }
3281
3282 case MACHO_N_UNDF:
3283 case MACHO_N_ABS:
3284 SymAddr += pSym->n_value;
3285 break;
3286
3287 case MACHO_N_INDR:
3288 case MACHO_N_PBUD:
3289 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3290 default:
3291 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3292 }
3293 }
3294 else if (Fixup2.r_symbolnum != R_ABS)
3295 {
3296 PRTLDRMODMACHOSECT pSymSect;
3297 RTLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
3298 pSymSect = &pThis->paSections[Fixup2.r_symbolnum - 1];
3299 SymAddr += pSymSect->RVA + NewBaseAddress;
3300 }
3301 else
3302 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3303 }
3304 break;
3305 }
3306 }
3307 else
3308 {
3309 /* verify against fixup type and make adjustments */
3310 switch (Fixup.r.r_type)
3311 {
3312 case X86_64_RELOC_UNSIGNED:
3313 RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3314 break;
3315 case X86_64_RELOC_BRANCH:
3316 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3317 SymAddr += 4; /* dunno what the assmbler/linker really is doing here... */
3318 break;
3319 case X86_64_RELOC_SIGNED:
3320 case X86_64_RELOC_SIGNED_1:
3321 case X86_64_RELOC_SIGNED_2:
3322 case X86_64_RELOC_SIGNED_4:
3323 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3324 break;
3325 /*case X86_64_RELOC_GOT_LOAD:*/
3326 /*case X86_64_RELOC_GOT: */
3327 /*case X86_64_RELOC_SUBTRACTOR: - must be r_extern=1 says as. */
3328 default:
3329 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3330 }
3331 if (Fixup.r.r_symbolnum != R_ABS)
3332 {
3333 PRTLDRMODMACHOSECT pSymSect;
3334 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
3335 pSymSect = &pThis->paSections[Fixup.r.r_symbolnum - 1];
3336
3337 SymAddr -= pSymSect->LinkAddress;
3338 SymAddr += pSymSect->RVA + NewBaseAddress;
3339 if (Fixup.r.r_pcrel)
3340 SymAddr += Fixup.r.r_address;
3341 }
3342 }
3343
3344 /* adjust for PC relative */
3345 if (Fixup.r.r_pcrel)
3346 SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
3347
3348 /*
3349 * Write back the fixed up value.
3350 */
3351 switch (Fixup.r.r_length)
3352 {
3353 case 3:
3354 *uFix.pu64 = (uint64_t)SymAddr;
3355 break;
3356 case 2:
3357 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel || Fixup.r.r_type == X86_64_RELOC_SUBTRACTOR, VERR_LDR_BAD_FIXUP);
3358 RTLDRMODMACHO_CHECK_RETURN((int32_t)SymAddr == (int64_t)SymAddr, VERR_LDR_ADDRESS_OVERFLOW);
3359 *uFix.pu32 = (uint32_t)SymAddr;
3360 break;
3361 default:
3362 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3363 }
3364 }
3365
3366 return VINF_SUCCESS;
3367}
3368
3369
3370/**
3371 * Loads the symbol table for a MH_OBJECT file.
3372 *
3373 * The symbol table is pointed to by RTLDRMODMACHO::pvaSymbols.
3374 *
3375 * @returns IPRT status code.
3376 * @param pThis The Mach-O module interpreter instance.
3377 */
3378static int kldrModMachOLoadObjSymTab(PRTLDRMODMACHO pThis)
3379{
3380 int rc = VINF_SUCCESS;
3381
3382 if ( !pThis->pvaSymbols
3383 && pThis->cSymbols)
3384 {
3385 size_t cbSyms;
3386 size_t cbSym;
3387 void *pvSyms;
3388 void *pvStrings;
3389
3390 /* sanity */
3391 RTLDRMODMACHO_CHECK_RETURN( pThis->offSymbols
3392 && (!pThis->cchStrings || pThis->offStrings),
3393 VERR_LDRMACHO_BAD_OBJECT_FILE);
3394
3395 /* allocate */
3396 cbSym = pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
3397 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
3398 ? sizeof(macho_nlist_32_t)
3399 : sizeof(macho_nlist_64_t);
3400 cbSyms = pThis->cSymbols * cbSym;
3401 RTLDRMODMACHO_CHECK_RETURN(cbSyms / cbSym == pThis->cSymbols, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
3402 rc = VERR_NO_MEMORY;
3403 pvSyms = RTMemAlloc(cbSyms);
3404 if (pvSyms)
3405 {
3406 if (pThis->cchStrings)
3407 pvStrings = RTMemAlloc(pThis->cchStrings);
3408 else
3409 pvStrings = RTMemAllocZ(4);
3410 if (pvStrings)
3411 {
3412 /* read */
3413 rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvSyms, cbSyms, pThis->offSymbols);
3414 if (RT_SUCCESS(rc) && pThis->cchStrings)
3415 rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvStrings,
3416 pThis->cchStrings, pThis->offStrings);
3417 if (RT_SUCCESS(rc))
3418 {
3419 pThis->pvaSymbols = pvSyms;
3420 pThis->pchStrings = (char *)pvStrings;
3421
3422 /* perform endian conversion? */
3423 if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
3424 {
3425 uint32_t cLeft = pThis->cSymbols;
3426 macho_nlist_32_t *pSym = (macho_nlist_32_t *)pvSyms;
3427 while (cLeft-- > 0)
3428 {
3429 pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
3430 pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
3431 pSym->n_value = RT_BSWAP_U32(pSym->n_value);
3432 pSym++;
3433 }
3434 }
3435 else if (pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
3436 {
3437 uint32_t cLeft = pThis->cSymbols;
3438 macho_nlist_64_t *pSym = (macho_nlist_64_t *)pvSyms;
3439 while (cLeft-- > 0)
3440 {
3441 pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
3442 pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
3443 pSym->n_value = RT_BSWAP_U64(pSym->n_value);
3444 pSym++;
3445 }
3446 }
3447
3448 return VINF_SUCCESS;
3449 }
3450 RTMemFree(pvStrings);
3451 }
3452 RTMemFree(pvSyms);
3453 }
3454 }
3455 else
3456 RTLDRMODMACHO_ASSERT(pThis->pchStrings || pThis->Hdr.filetype == MH_DSYM);
3457
3458 return rc;
3459}
3460
3461
3462/**
3463 * Loads the fixups at the given address and performs endian
3464 * conversion if necessary.
3465 *
3466 * @returns IPRT status code.
3467 * @param pThis The Mach-O module interpreter instance.
3468 * @param offFixups The file offset of the fixups.
3469 * @param cFixups The number of fixups to load.
3470 * @param ppaFixups Where to put the pointer to the allocated fixup array.
3471 */
3472static int kldrModMachOLoadFixups(PRTLDRMODMACHO pThis, RTFOFF offFixups, uint32_t cFixups, macho_relocation_info_t **ppaFixups)
3473{
3474 macho_relocation_info_t *paFixups;
3475 size_t cbFixups;
3476
3477 /* allocate the memory. */
3478 cbFixups = cFixups * sizeof(*paFixups);
3479 RTLDRMODMACHO_CHECK_RETURN(cbFixups / sizeof(*paFixups) == cFixups, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
3480 paFixups = (macho_relocation_info_t *)RTMemAlloc(cbFixups);
3481 if (!paFixups)
3482 return VERR_NO_MEMORY;
3483
3484 /* read the fixups. */
3485 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, paFixups, cbFixups, offFixups);
3486 if (RT_SUCCESS(rc))
3487 {
3488 *ppaFixups = paFixups;
3489
3490 /* do endian conversion if necessary. */
3491 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
3492 || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
3493 {
3494 uint32_t iFixup;
3495 for (iFixup = 0; iFixup < cFixups; iFixup++)
3496 {
3497 uint32_t *pu32 = (uint32_t *)&paFixups[iFixup];
3498 pu32[0] = RT_BSWAP_U32(pu32[0]);
3499 pu32[1] = RT_BSWAP_U32(pu32[1]);
3500 }
3501 }
3502 }
3503 else
3504 RTMemFree(paFixups);
3505 return rc;
3506}
3507
3508
3509/**
3510 * Maps the virgin file bits into memory if not already done.
3511 *
3512 * @returns IPRT status code.
3513 * @param pThis The Mach-O module interpreter instance.
3514 */
3515static int kldrModMachOMapVirginBits(PRTLDRMODMACHO pThis)
3516{
3517 int rc = VINF_SUCCESS;
3518 if (!pThis->pvBits)
3519 rc = pThis->Core.pReader->pfnMap(pThis->Core.pReader, &pThis->pvBits);
3520 return rc;
3521}
3522
3523#if 0
3524
3525/** @copydoc kLdrModCallInit */
3526static int kldrModMachOCallInit(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
3527{
3528 /* later */
3529 RT_NOREF(pMod);
3530 RT_NOREF(pvMapping);
3531 RT_NOREF(uHandle);
3532 return VINF_SUCCESS;
3533}
3534
3535
3536/** @copydoc kLdrModCallTerm */
3537static int kldrModMachOCallTerm(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
3538{
3539 /* later */
3540 RT_NOREF(pMod);
3541 RT_NOREF(pvMapping);
3542 RT_NOREF(uHandle);
3543 return VINF_SUCCESS;
3544}
3545
3546
3547/** @copydoc kLdrModCallThread */
3548static int kldrModMachOCallThread(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle, unsigned fAttachingOrDetaching)
3549{
3550 /* Relevant for Mach-O? */
3551 RT_NOREF(pMod);
3552 RT_NOREF(pvMapping);
3553 RT_NOREF(uHandle);
3554 RT_NOREF(fAttachingOrDetaching);
3555 return VINF_SUCCESS;
3556}
3557
3558#endif
3559
3560
3561/**
3562 * @interface_method_impl{RTLDROPS,pfnGetImageSize}
3563 */
3564static DECLCALLBACK(size_t) rtldrMachO_GetImageSize(PRTLDRMODINTERNAL pMod)
3565{
3566 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3567 return pThis->cbImage;
3568}
3569
3570
3571/**
3572 * @interface_method_impl{RTLDROPS,pfnGetBits}
3573 */
3574static DECLCALLBACK(int) rtldrMachO_GetBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR BaseAddress,
3575 PFNRTLDRIMPORT pfnGetImport, void *pvUser)
3576{
3577 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3578
3579 if (!pThis->fCanLoad)
3580 return VERR_LDRMACHO_TODO;
3581
3582 /*
3583 * Zero the entire buffer first to simplify things.
3584 */
3585 memset(pvBits, 0, (size_t)pThis->cbImage);
3586
3587 /*
3588 * When possible use the segment table to load the data.
3589 */
3590 for (uint32_t i = 0; i < pThis->cSegments; i++)
3591 {
3592 /* skip it? */
3593 if ( pThis->aSegments[i].SegInfo.cbFile == -1
3594 || pThis->aSegments[i].SegInfo.offFile == -1
3595 || pThis->aSegments[i].SegInfo.LinkAddress == NIL_RTLDRADDR
3596 || !pThis->aSegments[i].SegInfo.Alignment)
3597 continue;
3598 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader,
3599 (uint8_t *)pvBits + pThis->aSegments[i].SegInfo.RVA,
3600 pThis->aSegments[i].SegInfo.cbFile,
3601 pThis->aSegments[i].SegInfo.offFile);
3602 if (RT_FAILURE(rc))
3603 return rc;
3604 }
3605
3606 /*
3607 * Perform relocations.
3608 */
3609 return rtldrMachO_RelocateBits(pMod, pvBits, BaseAddress, pThis->LinkAddress, pfnGetImport, pvUser);
3610}
3611
3612
3613/**
3614 * @interface_method_impl{RTLDROPS,pfnRelocate}
3615 */
3616static DECLCALLBACK(int) rtldrMachO_RelocateBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
3617 RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
3618{
3619 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3620 int rc;
3621 RT_NOREF(OldBaseAddress);
3622
3623 /*
3624 * Call workers to do the jobs.
3625 */
3626 if (pThis->Hdr.filetype == MH_OBJECT)
3627 {
3628 rc = kldrModMachOObjDoImports(pThis, NewBaseAddress, pfnGetImport, pvUser);
3629 if (RT_SUCCESS(rc))
3630 rc = kldrModMachOObjDoFixups(pThis, pvBits, NewBaseAddress);
3631
3632 }
3633 else
3634 rc = VERR_LDRMACHO_TODO;
3635 /*{
3636 rc = kldrModMachODoFixups(pThis, pvBits, NewBaseAddress, OldBaseAddress, pfnGetImport, pvUser);
3637 if (RT_SUCCESS(rc))
3638 rc = kldrModMachODoImports(pThis, pvBits, pfnGetImport, pvUser);
3639 }*/
3640
3641 /*
3642 * Construct the global offset table if necessary, it's always the last
3643 * segment when present.
3644 */
3645 if (RT_SUCCESS(rc) && pThis->fMakeGot)
3646 rc = kldrModMachOMakeGOT(pThis, pvBits, NewBaseAddress);
3647
3648 return rc;
3649}
3650
3651
3652/**
3653 * Builds the GOT.
3654 *
3655 * Assumes the symbol table has all external symbols resolved correctly and that
3656 * the bits has been cleared up front.
3657 */
3658static int kldrModMachOMakeGOT(PRTLDRMODMACHO pThis, void *pvBits, RTLDRADDR NewBaseAddress)
3659{
3660 uint32_t iSym = pThis->cSymbols;
3661 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
3662 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
3663 {
3664 macho_nlist_32_t const *paSyms = (macho_nlist_32_t const *)pThis->pvaSymbols;
3665 uint32_t *paGOT = (uint32_t *)((uint8_t *)pvBits + pThis->GotRVA);
3666 while (iSym-- > 0)
3667 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
3668 {
3669 case MACHO_N_SECT:
3670 {
3671 PRTLDRMODMACHOSECT pSymSect;
3672 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3673 pSymSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
3674 paGOT[iSym] = (uint32_t)(paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress);
3675 break;
3676 }
3677
3678 case MACHO_N_UNDF:
3679 case MACHO_N_ABS:
3680 paGOT[iSym] = paSyms[iSym].n_value;
3681 break;
3682 }
3683 }
3684 else
3685 {
3686 macho_nlist_64_t const *paSyms = (macho_nlist_64_t const *)pThis->pvaSymbols;
3687 uint64_t *paGOT = (uint64_t *)((uint8_t *)pvBits + pThis->GotRVA);
3688 while (iSym-- > 0)
3689 {
3690 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
3691 {
3692 case MACHO_N_SECT:
3693 {
3694 PRTLDRMODMACHOSECT pSymSect;
3695 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3696 pSymSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
3697 paGOT[iSym] = paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3698 break;
3699 }
3700
3701 case MACHO_N_UNDF:
3702 case MACHO_N_ABS:
3703 paGOT[iSym] = paSyms[iSym].n_value;
3704 break;
3705 }
3706 }
3707
3708 if (pThis->JmpStubsRVA != NIL_RTLDRADDR)
3709 {
3710 iSym = pThis->cSymbols;
3711 switch (pThis->Hdr.cputype)
3712 {
3713 /*
3714 * AMD64 is simple since the GOT and the indirect jmps are parallel
3715 * arrays with entries of the same size. The relative offset will
3716 * be the the same for each entry, kind of nice. :-)
3717 */
3718 case CPU_TYPE_X86_64:
3719 {
3720 uint64_t *paJmps = (uint64_t *)((uint8_t *)pvBits + pThis->JmpStubsRVA);
3721 int32_t off;
3722 uint64_t u64Tmpl;
3723 union
3724 {
3725 uint8_t ab[8];
3726 uint64_t u64;
3727 } Tmpl;
3728
3729 /* create the template. */
3730 off = (int32_t)(pThis->GotRVA - (pThis->JmpStubsRVA + 6));
3731 Tmpl.ab[0] = 0xff; /* jmp [GOT-entry wrt RIP] */
3732 Tmpl.ab[1] = 0x25;
3733 Tmpl.ab[2] = off & 0xff;
3734 Tmpl.ab[3] = (off >> 8) & 0xff;
3735 Tmpl.ab[4] = (off >> 16) & 0xff;
3736 Tmpl.ab[5] = (off >> 24) & 0xff;
3737 Tmpl.ab[6] = 0xcc;
3738 Tmpl.ab[7] = 0xcc;
3739 u64Tmpl = Tmpl.u64;
3740
3741 /* copy the template to every jmp table entry. */
3742 while (iSym-- > 0)
3743 paJmps[iSym] = u64Tmpl;
3744 break;
3745 }
3746
3747 default:
3748 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3749 }
3750 }
3751 }
3752 return VINF_SUCCESS;
3753}
3754
3755
3756/**
3757 * @interface_method_impl{RTLDROPS,pfnEnumSegments}
3758 */
3759static DECLCALLBACK(int) rtldrMachO_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
3760{
3761 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3762 uint32_t const cSegments = pThis->cSegments;
3763 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3764 {
3765 int rc = pfnCallback(pMod, &pThis->aSegments[iSeg].SegInfo, pvUser);
3766 if (rc != VINF_SUCCESS)
3767 return rc;
3768 }
3769
3770 return VINF_SUCCESS;
3771}
3772
3773
3774/**
3775 * @interface_method_impl{RTLDROPS,pfnLinkAddressToSegOffset}
3776 */
3777static DECLCALLBACK(int) rtldrMachO_LinkAddressToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress,
3778 uint32_t *piSeg, PRTLDRADDR poffSeg)
3779{
3780 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3781 uint32_t const cSegments = pThis->cSegments;
3782 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3783 {
3784 RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
3785 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3786 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3787 {
3788 *piSeg = iSeg;
3789 *poffSeg = offSeg;
3790 return VINF_SUCCESS;
3791 }
3792 }
3793
3794 return VERR_LDR_INVALID_LINK_ADDRESS;
3795}
3796
3797
3798/**
3799 * @interface_method_impl{RTLDROPS,pfnLinkAddressToRva}
3800 */
3801static DECLCALLBACK(int) rtldrMachO_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
3802{
3803 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3804 uint32_t const cSegments = pThis->cSegments;
3805 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3806 {
3807 RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
3808 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3809 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3810 {
3811 *pRva = pThis->aSegments[iSeg].SegInfo.RVA + offSeg;
3812 return VINF_SUCCESS;
3813 }
3814 }
3815
3816 return VERR_LDR_INVALID_RVA;
3817}
3818
3819
3820/**
3821 * @interface_method_impl{RTLDROPS,pfnSegOffsetToRva}
3822 */
3823static DECLCALLBACK(int) rtldrMachO_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg, PRTLDRADDR pRva)
3824{
3825 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3826
3827 if (iSeg >= pThis->cSegments)
3828 return VERR_LDR_INVALID_SEG_OFFSET;
3829 RTLDRMODMACHOSEG const *pSegment = &pThis->aSegments[iSeg];
3830
3831 if ( offSeg > pSegment->SegInfo.cbMapped
3832 && offSeg > pSegment->SegInfo.cb
3833 && ( pSegment->SegInfo.cbFile < 0
3834 || offSeg > (uint64_t)pSegment->SegInfo.cbFile))
3835 return VERR_LDR_INVALID_SEG_OFFSET;
3836
3837 *pRva = pSegment->SegInfo.RVA + offSeg;
3838 return VINF_SUCCESS;
3839}
3840
3841
3842/**
3843 * @interface_method_impl{RTLDROPS,pfnRvaToSegOffset}
3844 */
3845static DECLCALLBACK(int) rtldrMachO_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva, uint32_t *piSeg, PRTLDRADDR poffSeg)
3846{
3847 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3848 uint32_t const cSegments = pThis->cSegments;
3849 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3850 {
3851 RTLDRADDR offSeg = Rva - pThis->aSegments[iSeg].SegInfo.RVA;
3852 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3853 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3854 {
3855 *piSeg = iSeg;
3856 *poffSeg = offSeg;
3857 return VINF_SUCCESS;
3858 }
3859 }
3860
3861 return VERR_LDR_INVALID_RVA;
3862}
3863
3864
3865/**
3866 * @interface_method_impl{RTLDROPS,pfnReadDbgInfo}
3867 */
3868static DECLCALLBACK(int) rtldrMachO_ReadDbgInfo(PRTLDRMODINTERNAL pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void *pvBuf)
3869{
3870 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3871
3872 /** @todo May have to apply fixups here. */
3873 if (iDbgInfo < pThis->cSections)
3874 return pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvBuf, cb, off);
3875 return VERR_OUT_OF_RANGE;
3876}
3877
3878
3879/**
3880 * Loads the code signing blob if necessary (RTLDRMODMACHO::PtrCodeSignature).
3881 *
3882 * @returns IPRT status code.
3883 * @param pThis The mach-o instance.
3884 */
3885static int rtldrMachO_LoadSignatureBlob(PRTLDRMODMACHO pThis)
3886{
3887 Assert(pThis->cbCodeSignature > 0);
3888 if (pThis->PtrCodeSignature.pb != NULL)
3889 return VINF_SUCCESS;
3890
3891 if ( pThis->cbCodeSignature > sizeof(RTCRAPLCSHDR)
3892 && pThis->cbCodeSignature <= _1M)
3893 {
3894 /* Allocate and read. */
3895 void *pv = RTMemAllocZ(RT_ALIGN_Z(pThis->cbCodeSignature, 16));
3896 AssertReturn(pv, VERR_NO_MEMORY);
3897 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pv, pThis->cbCodeSignature,
3898 pThis->offImage + pThis->offCodeSignature);
3899 if (RT_SUCCESS(rc))
3900 {
3901 /* Check blob signature. */
3902 PCRTCRAPLCSSUPERBLOB pSuper = (PCRTCRAPLCSSUPERBLOB)pv;
3903 if (pSuper->Hdr.uMagic == RTCRAPLCS_MAGIC_EMBEDDED_SIGNATURE)
3904 {
3905 uint32_t cbHdr = RT_BE2H_U32(pSuper->Hdr.cb);
3906 uint32_t cSlots = RT_BE2H_U32(pSuper->cSlots);
3907 if ( cbHdr <= pThis->cbCodeSignature
3908 && cbHdr > RT_UOFFSETOF(RTCRAPLCSSUPERBLOB, aSlots)
3909 && cSlots > 0
3910 && cSlots < 128
3911 && RT_UOFFSETOF_DYN(RTCRAPLCSSUPERBLOB, aSlots[cSlots]) <= cbHdr)
3912 {
3913 pThis->PtrCodeSignature.pSuper = pSuper;
3914 return VINF_SUCCESS;
3915 }
3916 rc = VERR_LDRVI_BAD_CERT_HDR_LENGTH;
3917 }
3918 else
3919 rc = VERR_LDRVI_BAD_CERT_HDR_TYPE;
3920 }
3921 RTMemFree(pv);
3922 return rc;
3923 }
3924 return VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY;
3925}
3926
3927
3928/**
3929 * Handles a RTLDRPROP_PKCS7_SIGNED_DATA query.
3930 */
3931static int rtldrMachO_QueryPkcs7SignedData(PRTLDRMODMACHO pThis, void *pvBuf, size_t cbBuf, size_t *pcbRet)
3932{
3933 int rc = rtldrMachO_LoadSignatureBlob(pThis);
3934 if (RT_SUCCESS(rc))
3935 {
3936 /*
3937 * Locate the signature slot.
3938 */
3939 uint32_t iSlot = RT_BE2H_U32(pThis->PtrCodeSignature.pSuper->cSlots);
3940 PCRTCRAPLCSBLOBSLOT pSlot = &pThis->PtrCodeSignature.pSuper->aSlots[iSlot];
3941 while (iSlot-- > 0)
3942 {
3943 pSlot--;
3944 if (pSlot->uType == RTCRAPLCS_SLOT_SIGNATURE)
3945 {
3946 /*
3947 * Validate the data offset.
3948 */
3949 uint32_t offData = RT_BE2H_U32(pSlot->offData);
3950 if ( offData < pThis->cbCodeSignature - sizeof(RTCRAPLCSHDR)
3951 || !(offData & 3) )
3952 {
3953 /*
3954 * The data is prefixed by a header with magic set to blob wrapper.
3955 * Check that the size is within the bounds of the code signing blob.
3956 */
3957 PCRTCRAPLCSHDR pHdr = (PCRTCRAPLCSHDR)&pThis->PtrCodeSignature.pb[offData];
3958 if (pHdr->uMagic == RTCRAPLCS_MAGIC_BLOBWRAPPER)
3959 {
3960 uint32_t cbData = RT_BE2H_U32(pHdr->cb);
3961 uint32_t cbMax = pThis->cbCodeSignature - offData ;
3962 if ( cbData <= cbMax
3963 && cbData > sizeof(RTCRAPLCSHDR))
3964 {
3965 /*
3966 * Copy out the requested data.
3967 */
3968 *pcbRet = cbData;
3969 if (cbData <= cbBuf)
3970 {
3971 memcpy(pvBuf, pHdr + 1, cbData);
3972 return VINF_SUCCESS;
3973 }
3974 memcpy(pvBuf, pHdr + 1, cbBuf);
3975 return VERR_BUFFER_OVERFLOW;
3976 }
3977 }
3978 }
3979 return VERR_LDRVI_BAD_CERT_FORMAT;
3980 }
3981 }
3982 rc = VERR_NOT_FOUND;
3983 }
3984 return rc;
3985}
3986
3987
3988/** @interface_method_impl{RTLDROPS,pfnQueryProp} */
3989static DECLCALLBACK(int) rtldrMachO_QueryProp(PRTLDRMODINTERNAL pMod, RTLDRPROP enmProp, void const *pvBits,
3990 void *pvBuf, size_t cbBuf, size_t *pcbRet)
3991{
3992 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3993 int rc = VERR_NOT_FOUND;
3994 switch (enmProp)
3995 {
3996 case RTLDRPROP_UUID:
3997 Assert(cbBuf >= sizeof(pThis->abImageUuid));
3998 if (!ASMMemIsZero(pThis->abImageUuid, sizeof(pThis->abImageUuid)))
3999 {
4000 *pcbRet = sizeof(pThis->abImageUuid);
4001 memcpy(pvBuf, pThis->abImageUuid, sizeof(pThis->abImageUuid));
4002 return VINF_SUCCESS;
4003 }
4004 break;
4005
4006 case RTLDRPROP_FILE_OFF_HEADER:
4007 Assert(cbBuf == sizeof(uint32_t) || cbBuf == sizeof(uint64_t));
4008 if (cbBuf == sizeof(uint32_t))
4009 *(uint32_t *)pvBuf = pThis->offImage;
4010 else
4011 *(uint64_t *)pvBuf = pThis->offImage;
4012 return VINF_SUCCESS;
4013
4014 case RTLDRPROP_IS_SIGNED:
4015 Assert(cbBuf == sizeof(bool));
4016 Assert(*pcbRet == cbBuf);
4017 *(bool *)pvBuf = pThis->cbCodeSignature > 0;
4018 return VINF_SUCCESS;
4019
4020 case RTLDRPROP_PKCS7_SIGNED_DATA:
4021 if (pThis->cbCodeSignature > 0)
4022 return rtldrMachO_QueryPkcs7SignedData(pThis, pvBuf, cbBuf, pcbRet);
4023 break;
4024
4025
4026#if 0 /** @todo return LC_ID_DYLIB */
4027 case RTLDRPROP_INTERNAL_NAME:
4028#endif
4029
4030 default:
4031 break;
4032 }
4033 NOREF(cbBuf);
4034 RT_NOREF_PV(pvBits);
4035 return rc;
4036}
4037
4038
4039#ifndef IPRT_WITHOUT_LDR_VERIFY
4040
4041/**
4042 * Decodes the signature blob at RTLDRMODMACHO::PtrCodeSignature.
4043 *
4044 * @returns IPRT status code.
4045 * @param pThis The Mach-O module instance.
4046 * @param ppSignature Where to return the decoded signature data.
4047 * @param pErrInfo Where to supply extra error details. Optional.
4048 */
4049static int rtldrMachO_VerifySignatureDecode(PRTLDRMODMACHO pThis, PRTLDRMACHOSIGNATURE *ppSignature, PRTERRINFO pErrInfo)
4050{
4051 Assert(pThis->PtrCodeSignature.pSuper != NULL);
4052
4053 /*
4054 * Allocate and init decoded signature data structure.
4055 */
4056 PRTLDRMACHOSIGNATURE pSignature = (PRTLDRMACHOSIGNATURE)RTMemTmpAllocZ(sizeof(*pSignature));
4057 *ppSignature = pSignature;
4058 if (!pSignature)
4059 return VERR_NO_TMP_MEMORY;
4060 pSignature->idxPkcs7 = UINT32_MAX;
4061
4062 /*
4063 * Parse the slots, validating the slot headers.
4064 */
4065 PCRTCRAPLCSSUPERBLOB pSuper = pThis->PtrCodeSignature.pSuper;
4066 uint32_t const cSlots = RT_BE2H_U32(pSuper->cSlots);
4067 uint32_t const offFirst = RT_UOFFSETOF_DYN(RTCRAPLCSSUPERBLOB, aSlots[cSlots]);
4068 uint32_t const cbBlob = RT_BE2H_U32(pSuper->Hdr.cb);
4069 for (uint32_t iSlot = 0; iSlot < cSlots; iSlot++)
4070 {
4071 uint32_t const offData = RT_BE2H_U32(pSuper->aSlots[iSlot].offData);
4072 if ( offData < offFirst
4073 || offData > cbBlob - sizeof(RTCRAPLCSHDR)
4074 || (offData & 3))
4075 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4076 "Slot #%u has an invalid data offset: %#x (min %#x, max %#x-4)",
4077 iSlot, offData, offFirst, cbBlob);
4078 uint32_t const cbMaxData = cbBlob - offData;
4079
4080 /*
4081 * PKCS#7/CMS signature.
4082 */
4083 if (pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_SIGNATURE)
4084 {
4085 if (pSignature->idxPkcs7 != UINT32_MAX)
4086 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4087 "Slot #%u: Already have PKCS#7 data in slot %#u", iSlot, pSignature->idxPkcs7);
4088 PCRTCRAPLCSHDR pHdr = (PCRTCRAPLCSHDR)&pThis->PtrCodeSignature.pb[offData];
4089 if (pHdr->uMagic != RTCRAPLCS_MAGIC_BLOBWRAPPER)
4090 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4091 "Slot #%u: Invalid PKCS#7 wrapper magic: %#x", iSlot, RT_BE2H_U32(pHdr->uMagic));
4092 uint32_t const cb = RT_BE2H_U32(pHdr->cb);
4093 if (cb > cbMaxData || cb < sizeof(*pHdr) + 2U)
4094 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4095 "Slot #%u: Invalid PKCS#7 size is out of bound: %#x (min %#x, max %#x)",
4096 iSlot, cb, sizeof(*pHdr) + 2, cbMaxData);
4097 pSignature->idxPkcs7 = iSlot;
4098 pSignature->pbPkcs7 = (uint8_t const *)(pHdr + 1);
4099 pSignature->cbPkcs7 = cb - sizeof(*pHdr);
4100 }
4101 /*
4102 * Code directories.
4103 */
4104 else if ( pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_CODEDIRECTORY
4105 || ( RT_BE2H_U32(pSuper->aSlots[iSlot].uType) - RT_BE2H_U32_C(RTCRAPLCS_SLOT_ALTERNATE_CODEDIRECTORIES)
4106 < RTCRAPLCS_SLOT_ALTERNATE_CODEDIRECTORIES_COUNT))
4107 {
4108 /* Make sure we don't get too many code directories and that the first one is a regular one. */
4109 if (pSignature->cCodeDirs >= RT_ELEMENTS(pSignature->aCodeDirs))
4110 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4111 "Slot #%u: Too many code directory slots (%u found thus far)",
4112 iSlot, pSignature->cCodeDirs + 1);
4113 if ( pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_CODEDIRECTORY
4114 && pSignature->cCodeDirs > 0)
4115 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4116 "Slot #%u: Already have primary code directory in slot #%u",
4117 iSlot, pSignature->aCodeDirs[0].uSlot);
4118 if ( pSuper->aSlots[iSlot].uType != RTCRAPLCS_SLOT_CODEDIRECTORY /* lazy bird */
4119 && pSignature->cCodeDirs == 0)
4120 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4121 "Slot #%u: Expected alternative code directory after the primary one", iSlot);
4122
4123 /* Check data header: */
4124 if (cbMaxData < RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, uUnused1))
4125 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4126 "Slot #%u: Insufficient data vailable for code directory (max %#x)", iSlot, cbMaxData);
4127
4128 PCRTCRAPLCSCODEDIRECTORY pCodeDir = (PCRTCRAPLCSCODEDIRECTORY)&pThis->PtrCodeSignature.pb[offData];
4129 if (pCodeDir->Hdr.uMagic != RTCRAPLCS_MAGIC_CODEDIRECTORY)
4130 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4131 "Slot #%u: Invalid code directory magic: %#x", iSlot, RT_BE2H_U32(pCodeDir->Hdr.uMagic));
4132 uint32_t const cbCodeDir = RT_BE2H_U32(pCodeDir->Hdr.cb);
4133 if (cbCodeDir > cbMaxData || cbCodeDir < RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, offScatter))
4134 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4135 "Slot #%u: Code directory size is out of bound: %#x (min %#x, max %#x)",
4136 iSlot, cbCodeDir, RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, offScatter), cbMaxData);
4137 pSignature->aCodeDirs[pSignature->cCodeDirs].pCodeDir = pCodeDir;
4138 pSignature->aCodeDirs[pSignature->cCodeDirs].cb = cbCodeDir;
4139
4140 /* Check Version: */
4141 uint32_t const uVersion = RT_BE2H_U32(pCodeDir->uVersion);
4142 if ( uVersion < RTCRAPLCS_VER_2_0
4143 || uVersion >= RT_MAKE_U32(0, 3))
4144 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4145 "Slot #%u: Code directory version is out of bounds: %#07x", iSlot, uVersion);
4146 uint32_t cbSelf = uVersion >= RTCRAPLCS_VER_SUPPORTS_EXEC_SEG ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, fExecSeg)
4147 : uVersion >= RTCRAPLCS_VER_SUPPORTS_CODE_LIMIT_64 ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, cbCodeLimit64)
4148 : uVersion >= RTCRAPLCS_VER_SUPPORTS_TEAMID ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offTeamId)
4149 : uVersion >= RTCRAPLCS_VER_SUPPORTS_SCATTER ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offScatter)
4150 : RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, uUnused1);
4151 if (cbSelf > cbCodeDir)
4152 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4153 "Slot #%u: Code directory size is out of bound: %#x (min %#x, max %#x)",
4154 iSlot, cbCodeDir, cbSelf, cbCodeDir);
4155
4156 /* hash type and size. */
4157 uint8_t cbHash;
4158 RTDIGESTTYPE enmDigest;
4159 switch (pCodeDir->bHashType)
4160 {
4161 case RTCRAPLCS_HASHTYPE_SHA1:
4162 enmDigest = RTDIGESTTYPE_SHA1;
4163 cbHash = RTSHA1_HASH_SIZE;
4164 break;
4165 case RTCRAPLCS_HASHTYPE_SHA256:
4166 enmDigest = RTDIGESTTYPE_SHA256;
4167 cbHash = RTSHA256_HASH_SIZE;
4168 break;
4169 case RTCRAPLCS_HASHTYPE_SHA256_TRUNCATED:
4170 enmDigest = RTDIGESTTYPE_SHA256;
4171 cbHash = RTSHA1_HASH_SIZE; /* truncated to SHA-1 size. */
4172 break;
4173 case RTCRAPLCS_HASHTYPE_SHA384:
4174 enmDigest = RTDIGESTTYPE_SHA384;
4175 cbHash = RTSHA384_HASH_SIZE;
4176 break;
4177 default:
4178 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Slot #%u: Unknown hash type %#x (LB %#x)",
4179 iSlot, pCodeDir->bHashType, pCodeDir->cbHash);
4180 }
4181 pSignature->aCodeDirs[pSignature->cCodeDirs].enmDigest = enmDigest;
4182 if (pCodeDir->cbHash != cbHash)
4183 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4184 "Slot #%u: Unexpected hash size for %s: %#x, expected %#x",
4185 iSlot, RTCrDigestTypeToName(enmDigest), pCodeDir->cbHash, cbHash);
4186
4187 /* Hash slot offset and counts. Special slots are counted backwards from offHashSlots. */
4188 uint32_t const cSpecialSlots = RT_BE2H_U32(pCodeDir->cSpecialSlots);
4189 if (cSpecialSlots > 256)
4190 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4191 "Slot #%u: Too many special slots: %#x", iSlot, cSpecialSlots);
4192 uint32_t const cCodeSlots = RT_BE2H_U32(pCodeDir->cCodeSlots);
4193 if ( cCodeSlots >= UINT32_MAX / 2
4194 || cCodeSlots + cSpecialSlots > (cbCodeDir - cbHash) / cbHash)
4195 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Slot #%u: Too many code slots: %#x + %#x (max %#x)",
4196 iSlot, cCodeSlots, cSpecialSlots, (cbCodeDir - cbHash) / cbHash);
4197 uint32_t const offHashSlots = RT_BE2H_U32(pCodeDir->offHashSlots);
4198 if ( offHashSlots > cbCodeDir - cCodeSlots * cbHash
4199 || offHashSlots < cbSelf + cSpecialSlots * cbHash)
4200 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4201 "Slot #%u: Code directory hash offset is out of bounds: %#x (min: %#x, max: %#x)",
4202 iSlot, offHashSlots, cbSelf + cSpecialSlots * cbHash, cbCodeDir - cCodeSlots * cbHash);
4203
4204 /* page shift */
4205 if (pCodeDir->cPageShift == 0)
4206 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4207 "Slot #%u: Unsupported page shift of zero in code directory", iSlot);
4208 uint32_t cMaxPageShift;
4209 if ( pThis->Core.enmArch == RTLDRARCH_AMD64
4210 || pThis->Core.enmArch == RTLDRARCH_X86_32
4211 || pThis->Core.enmArch == RTLDRARCH_ARM32)
4212 cMaxPageShift = 12;
4213 else if (pThis->Core.enmArch == RTLDRARCH_ARM64)
4214 cMaxPageShift = 16; /* 16KB */
4215 else
4216 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Unsupported architecture: %d", pThis->Core.enmArch);
4217 if ( pCodeDir->cPageShift < 12 /* */
4218 || pCodeDir->cPageShift > cMaxPageShift)
4219 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4220 "Slot #%u: Page shift in code directory is out of range: %d (min: 12, max: %d)",
4221 iSlot, pCodeDir->cPageShift, cMaxPageShift);
4222
4223 /* code limit vs page shift and code hash slots */
4224 uint32_t const cbCodeLimit32 = RT_BE2H_U32(pCodeDir->cbCodeLimit32);
4225 uint32_t const cExpectedCodeHashes = pCodeDir->cPageShift == 0 ? 1
4226 : (cbCodeLimit32 + RT_BIT_32(pCodeDir->cPageShift) - 1) >> pCodeDir->cPageShift;
4227 if (cExpectedCodeHashes != cCodeSlots)
4228 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4229 "Slot #%u: Code limit and page shift value does not match code hash slots: cbCodeLimit32=%#x cPageShift=%u -> %#x; cCodeSlots=%#x",
4230 iSlot, cbCodeLimit32, pCodeDir->cPageShift, cExpectedCodeHashes, cCodeSlots);
4231
4232 /* Identifier offset: */
4233 if (pCodeDir->offIdentifier)
4234 {
4235 uint32_t const offIdentifier = RT_BE2H_U32(pCodeDir->offIdentifier);
4236 if ( offIdentifier < cbSelf
4237 || offIdentifier >= cbCodeDir)
4238 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4239 "Slot #%u: Identifier offset is out of bounds: %#x (min: %#x, max: %#x)",
4240 iSlot, offIdentifier, cbSelf, cbCodeDir - 1);
4241 int rc = RTStrValidateEncodingEx((char const *)pCodeDir + offIdentifier, cbCodeDir - offIdentifier,
4242 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
4243 if (RT_FAILURE(rc))
4244 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4245 "Slot #%u: Malformed identifier string: %Rrc", iSlot, rc);
4246 }
4247
4248 /* Team identifier: */
4249 if (cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offTeamId) && pCodeDir->offTeamId)
4250 {
4251 uint32_t const offTeamId = RT_BE2H_U32(pCodeDir->offTeamId);
4252 if ( offTeamId < cbSelf
4253 || offTeamId >= cbCodeDir)
4254 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4255 "Slot #%u: Team identifier offset is out of bounds: %#x (min: %#x, max: %#x)",
4256 iSlot, offTeamId, cbSelf, cbCodeDir - 1);
4257 int rc = RTStrValidateEncodingEx((char const *)pCodeDir + offTeamId, cbCodeDir - offTeamId,
4258 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
4259 if (RT_FAILURE(rc))
4260 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4261 "Slot #%u: Malformed team identifier string: %Rrc", iSlot, rc);
4262 }
4263
4264 /* We don't support scatter. */
4265 if (cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offScatter) && pCodeDir->offScatter)
4266 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4267 "Slot #%u: Scatter not supported.", iSlot);
4268
4269 /* We don't really support the 64-bit code limit either: */
4270 if ( cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, cbCodeLimit64)
4271 && pCodeDir->cbCodeLimit64
4272 && RT_BE2H_U64(pCodeDir->cbCodeLimit64) != cbCodeLimit32)
4273 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4274 "Slot #%u: 64-bit code limit does not match 32-bit: %#RX64 vs %#RX32",
4275 iSlot, RT_BE2H_U64(pCodeDir->cbCodeLimit64), cbCodeLimit32);
4276
4277 /* Check executable segment info if present: */
4278 if ( cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, fExecSeg)
4279 && ( pThis->offSeg0ForCodeSign != RT_BE2H_U64(pCodeDir->offExecSeg)
4280 || pThis->cbSeg0ForCodeSign != RT_BE2H_U64(pCodeDir->cbExecSeg)
4281 || pThis->fSeg0ForCodeSign != RT_BE2H_U64(pCodeDir->fExecSeg)) )
4282 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4283 "Slot #%u: Segment #0 info mismatch: @%#RX64 LB %#RX64 flags=%#RX64; expected @%#RX64 LB %#RX64 flags=%#RX64",
4284 iSlot, RT_BE2H_U64(pCodeDir->offExecSeg), RT_BE2H_U64(pCodeDir->cbExecSeg),
4285 RT_BE2H_U64(pCodeDir->fExecSeg), pThis->offSeg0ForCodeSign, pThis->cbSeg0ForCodeSign,
4286 pThis->fSeg0ForCodeSign);
4287
4288 /* Check fields that must be zero (don't want anyone to use them to counter changes): */
4289 if (pCodeDir->uUnused1 != 0)
4290 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4291 "Slot #%u: Unused field #1 is non-zero: %#x", iSlot, RT_BE2H_U32(pCodeDir->uUnused1));
4292 if ( cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, uUnused2)
4293 && pCodeDir->uUnused2 != 0)
4294 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4295 "Slot #%u: Unused field #2 is non-zero: %#x", iSlot, RT_BE2H_U32(pCodeDir->uUnused2));
4296
4297 /** @todo idPlatform values. */
4298 /** @todo Check for gaps if we know the version number? Alignment? */
4299
4300 /* If first code directory, check that the code limit covers the whole image up to the signature data. */
4301 if (pSignature->cCodeDirs == 0)
4302 {
4303 /** @todo verify the that the signature data is at the very end... */
4304 if (cbCodeLimit32 != pThis->offCodeSignature)
4305 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4306 "Slot #%u: Unexpected code limit: %#x, expected %#x",
4307 iSlot, cbCodeLimit32, pThis->offCodeSignature);
4308 }
4309 /* Otherwise, check that the code limit matches the previous directories. */
4310 else
4311 for (uint32_t i = 0; i < pSignature->cCodeDirs; i++)
4312 if (pSignature->aCodeDirs[i].pCodeDir->cbCodeLimit32 != pCodeDir->cbCodeLimit32)
4313 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4314 "Slot #%u: Code limit differs from previous directory: %#x, expected %#x",
4315 iSlot, cbCodeLimit32, RT_BE2H_U32(pSignature->aCodeDirs[i].pCodeDir->cbCodeLimit32));
4316
4317 /* Commit the code dir entry: */
4318 pSignature->aCodeDirs[pSignature->cCodeDirs++].uSlot = iSlot;
4319 }
4320 }
4321
4322 /*
4323 * Check that we've got at least one code directory and one PKCS#7 signature.
4324 */
4325 if (pSignature->cCodeDirs == 0)
4326 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "No code directory slot in the code signature");
4327 if (pSignature->idxPkcs7 == UINT32_MAX)
4328 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "No PKCS#7 slot in the code signature");
4329
4330 /*
4331 * Decode the PKCS#7 signature.
4332 */
4333 RTASN1CURSORPRIMARY PrimaryCursor;
4334 RTAsn1CursorInitPrimary(&PrimaryCursor, pSignature->pbPkcs7, pSignature->cbPkcs7,
4335 pErrInfo, &g_RTAsn1DefaultAllocator, 0, "Mach-O-BLOB");
4336 int rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &pSignature->ContentInfo, "CI");
4337 if (RT_SUCCESS(rc))
4338 {
4339 if (RTCrPkcs7ContentInfo_IsSignedData(&pSignature->ContentInfo))
4340 {
4341 pSignature->pSignedData = pSignature->ContentInfo.u.pSignedData;
4342
4343 /*
4344 * Check that the signedData stuff adds up.
4345 */
4346 if (!strcmp(pSignature->pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID))
4347 {
4348 rc = RTCrPkcs7SignedData_CheckSanity(pSignature->pSignedData,
4349 RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE /** @todo consider not piggy-backing on auth-code */
4350 | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
4351 | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
4352 pErrInfo, "SD");
4353 if (RT_SUCCESS(rc))
4354 return VINF_SUCCESS;
4355 }
4356 else
4357 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID,
4358 "Unexpected pSignedData.ContentInfo.ContentType.szObjId value: %s (expected %s)",
4359 pSignature->pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID);
4360 }
4361 else
4362 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID, /** @todo error code*/
4363 "PKCS#7 is not 'signedData': %s", pSignature->ContentInfo.ContentType.szObjId);
4364 }
4365 return rc;
4366}
4367
4368/**
4369 * Destroys the decoded signature data structure.
4370 *
4371 * @param pSignature The decoded signature data. Can be NULL.
4372 */
4373static void rtldrMachO_VerifySignatureDestroy(PRTLDRMACHOSIGNATURE pSignature)
4374{
4375 if (pSignature)
4376 {
4377 RTCrPkcs7ContentInfo_Delete(&pSignature->ContentInfo);
4378 RTMemTmpFree(pSignature);
4379 }
4380}
4381
4382
4383/**
4384 * Worker for rtldrMachO_VerifySignatureValidatePkcs7Hashes that handles plists
4385 * with code directory hashes inside them.
4386 *
4387 * It is assumed that these plist files was invented to handle alternative code
4388 * directories.
4389 *
4390 * @note Putting an XML plist into the authenticated attribute list was
4391 * probably not such a great idea, given all the optional and
4392 * adjustable white-space padding. We should probably validate
4393 * everything very strictly, limiting the elements, require certain
4394 * attribute lists and even have strict expectations about the
4395 * white-space, but right now let just make sure it's xml and get the
4396 * data in the cdhashes array.
4397 *
4398 * @todo The code here is a little braindead and bulky. It should be
4399 * possible to describe the expected XML structure using a tables.
4400 */
4401static int rtldrMachO_VerifySignatureValidateCdHashesPlist(PRTLDRMACHOSIGNATURE pSignature, char *pszPlist,
4402 uint8_t *pbHash, uint32_t cbHash, PRTERRINFO pErrInfo)
4403{
4404 const char * const pszStart = pszPlist;
4405#define CHECK_ISTR_AND_SKIP_OR_RETURN(a_szLead) \
4406 do { \
4407 if (!RTStrNICmp(pszPlist, RT_STR_TUPLE(a_szLead))) \
4408 pszPlist += sizeof(a_szLead) - 1; \
4409 else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
4410 "Expected '%s' found '%.16s...' at %#zu in plist", a_szLead, pszPlist, pszPlist - pszStart); \
4411 } while (0)
4412#define CHECK_STR_AND_SKIP_OR_RETURN(a_szLead) \
4413 do { \
4414 if (!RTStrNCmp(pszPlist, RT_STR_TUPLE(a_szLead))) \
4415 pszPlist += sizeof(a_szLead) - 1; \
4416 else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
4417 "Expected '%s' found '%.16s...' at %#zu in plist", a_szLead, pszPlist, pszPlist - pszStart); \
4418 } while (0)
4419#define SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN() \
4420 do { /* currently only permitting spaces, tabs and newline, following char must be '<'. */ \
4421 char chMacro; \
4422 while ((chMacro = *pszPlist) == ' ' || chMacro == '\n' || chMacro == '\t') \
4423 pszPlist++; \
4424 if (chMacro == '<') { /* likely */ } \
4425 else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
4426 "Expected '<' found '%.16s...' at %#zu in plist", pszPlist, pszPlist - pszStart); \
4427 } while (0)
4428#define SKIP_SPACE_BEFORE_VALUE() \
4429 do { /* currently only permitting spaces, tabs and newline. */ \
4430 char chMacro; \
4431 while ((chMacro = *pszPlist) == ' ' || chMacro == '\n' || chMacro == '\t') \
4432 pszPlist++; \
4433 } while (0)
4434#define SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN() \
4435 do { /* currently only permitting a single space */ \
4436 if (pszPlist[0] == ' ' && pszPlist[1] != ' ') \
4437 pszPlist++; \
4438 else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
4439 "Expected ' ' found '%.16s...' at %#zu in plist", pszPlist, pszPlist - pszStart); \
4440 } while (0)
4441
4442 /* Example:
4443<?xml version="1.0" encoding="UTF-8"?>
4444<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4445<plist version="1.0">
4446<dict>
4447 <key>cdhashes</key>
4448 <array>
4449 <data>
4450 hul2SSkDQFRXbGlt3AmCp25MU0Y=
4451 </data>
4452 <data>
4453 N0kvxg0CJBNuZTq135PntAaRczw=
4454 </data>
4455 </array>
4456</dict>
4457</plist>
4458 */
4459
4460 /* <?xml version="1.0" encoding="UTF-8"?> */
4461 CHECK_STR_AND_SKIP_OR_RETURN("<?xml");
4462 SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
4463 CHECK_STR_AND_SKIP_OR_RETURN("version=\"1.0\"");
4464 SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
4465 CHECK_STR_AND_SKIP_OR_RETURN("encoding=\"UTF-8\"");
4466 CHECK_STR_AND_SKIP_OR_RETURN("?>");
4467 SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
4468
4469 /* <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> */
4470 CHECK_STR_AND_SKIP_OR_RETURN("<!DOCTYPE");
4471 SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
4472 CHECK_STR_AND_SKIP_OR_RETURN("plist");
4473 SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
4474 CHECK_STR_AND_SKIP_OR_RETURN("PUBLIC");
4475 SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
4476 CHECK_STR_AND_SKIP_OR_RETURN("\"-//Apple//DTD PLIST 1.0//EN\"");
4477 SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
4478 CHECK_STR_AND_SKIP_OR_RETURN("\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"");
4479 CHECK_STR_AND_SKIP_OR_RETURN(">");
4480 SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
4481
4482 /* <plist version="1.0"> */
4483 CHECK_STR_AND_SKIP_OR_RETURN("<plist");
4484 SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
4485 CHECK_STR_AND_SKIP_OR_RETURN("version=\"1.0\"");
4486 CHECK_STR_AND_SKIP_OR_RETURN(">");
4487 SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
4488
4489 /* <dict> */
4490 CHECK_STR_AND_SKIP_OR_RETURN("<dict>");
4491 SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
4492
4493 /* <key>cdhashes</key> */
4494 CHECK_STR_AND_SKIP_OR_RETURN("<key>cdhashes</key>");
4495 SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
4496
4497 /* <array> */
4498 CHECK_STR_AND_SKIP_OR_RETURN("<array>");
4499 SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
4500
4501 /*
4502 * Repeated: <data>hul2SSkDQFRXbGlt3AmCp25MU0Y=</data>
4503 */
4504 uint32_t iCodeDir = 0;
4505 for (;;)
4506 {
4507 /* Decode the binary data (base64) and skip it. */
4508 CHECK_STR_AND_SKIP_OR_RETURN("<data>");
4509 SKIP_SPACE_BEFORE_VALUE();
4510
4511 char ch;
4512 size_t cchBase64 = 0;
4513 while (RT_C_IS_ALNUM(ch = pszPlist[cchBase64]) || ch == '+' || ch == '/' || ch == '=')
4514 cchBase64++;
4515 size_t cbActualHash = cbHash;
4516 char *pszEnd = NULL;
4517 int rc = RTBase64DecodeEx(pszPlist, cchBase64, pbHash, cbHash, &cbActualHash, &pszEnd);
4518 if (RT_FAILURE(rc))
4519 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4520 "Failed to decode hash #%u in authenticated plist attribute: %Rrc (%.*s)",
4521 iCodeDir, rc, cchBase64, pszPlist);
4522 pszPlist += cchBase64;
4523 AssertReturn(pszPlist == pszEnd, VERR_INTERNAL_ERROR_2);
4524 SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
4525
4526 /* The binary hash data must be exactly the size of SHA1, larger
4527 hash like SHA-256 and SHA-384 are truncated for some reason. */
4528 if (cbActualHash != RTSHA1_HASH_SIZE)
4529 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4530 "Hash #%u in authenticated plist attribute has the wrong length: %u, exepcted %u",
4531 iCodeDir, cbActualHash, RTSHA1_HASH_SIZE);
4532
4533 /* Skip closing tag. */
4534 CHECK_STR_AND_SKIP_OR_RETURN("</data>");
4535 SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
4536
4537
4538 /* Calculate the hash and compare. */
4539 RTCRDIGEST hDigest;
4540 rc = RTCrDigestCreateByType(&hDigest, pSignature->aCodeDirs[iCodeDir].enmDigest);
4541 if (RT_SUCCESS(rc))
4542 {
4543 rc = RTCrDigestUpdate(hDigest, pSignature->aCodeDirs[iCodeDir].pCodeDir, pSignature->aCodeDirs[iCodeDir].cb);
4544 if (RT_SUCCESS(rc))
4545 {
4546 if (memcmp(pbHash, RTCrDigestGetHash(hDigest), cbActualHash) == 0)
4547 rc = VINF_SUCCESS;
4548 else
4549 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_IMAGE_HASH_MISMATCH,
4550 "Code directory #%u hash mismatch (plist):\n"
4551 "signed: %.*Rhxs\n"
4552 "our: %.*Rhxs\n",
4553 iCodeDir, cbActualHash, pbHash,
4554 RTCrDigestGetHashSize(hDigest), RTCrDigestGetHash(hDigest));
4555 }
4556 else
4557 rc = RTErrInfoSetF(pErrInfo, rc, "RTCrDigestUpdate failed: %Rrc", rc);
4558 RTCrDigestRelease(hDigest);
4559 }
4560 else
4561 rc = RTErrInfoSetF(pErrInfo, rc, "Failed to create a digest of type %u verifying code dir #%u: %Rrc",
4562 pSignature->aCodeDirs[iCodeDir].enmDigest, iCodeDir, rc);
4563 if (RT_FAILURE(rc))
4564 return rc;
4565
4566 /*
4567 * Advance.
4568 */
4569 iCodeDir++;
4570 SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
4571 if (RTStrNCmp(pszPlist, RT_STR_TUPLE("<data>")) == 0)
4572 {
4573 if (iCodeDir >= pSignature->cCodeDirs)
4574 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4575 "Authenticated plist attribute has too many code directories (%u in blob)",
4576 pSignature->cCodeDirs);
4577 }
4578 else if (iCodeDir == pSignature->cCodeDirs)
4579 break;
4580 else
4581 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4582 "Authenticated plist attribute does not include all code directors: %u out of %u",
4583 iCodeDir, pSignature->cCodeDirs);
4584 }
4585
4586 /*</array>*/
4587 CHECK_STR_AND_SKIP_OR_RETURN("</array>");
4588 SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
4589
4590 /*</dict>*/
4591 CHECK_STR_AND_SKIP_OR_RETURN("</dict>");
4592 SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
4593
4594 /*</plist>*/
4595 CHECK_STR_AND_SKIP_OR_RETURN("</plist>");
4596 SKIP_SPACE_BEFORE_VALUE();
4597
4598 if (*pszPlist == '\0')
4599 return VINF_SUCCESS;
4600 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4601 "Authenticated plist attribute has unexpected trailing content: %.32s", pszPlist);
4602}
4603
4604
4605/**
4606 * Verifies the code directory hashes embedded in the PKCS\#7 data.
4607 *
4608 * @returns IPRT status code.
4609 * @param pSignature The decoded signature data.
4610 * @param pErrInfo Where to supply extra error details. Optional.
4611 */
4612static int rtldrMachO_VerifySignatureValidatePkcs7Hashes(PRTLDRMACHOSIGNATURE pSignature, PRTERRINFO pErrInfo)
4613{
4614 /*
4615 * Look thru the authenticated attributes in the signer info array.
4616 */
4617 PRTCRPKCS7SIGNEDDATA pSignedData = pSignature->pSignedData;
4618 for (uint32_t iSignerInfo = 0; iSignerInfo < pSignedData->SignerInfos.cItems; iSignerInfo++)
4619 {
4620 PCRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[iSignerInfo];
4621 bool fMsgDigest = false;
4622 bool fPlist = false;
4623 for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->AuthenticatedAttributes.cItems; iAttrib++)
4624 {
4625 PCRTCRPKCS7ATTRIBUTE pAttrib = pSignerInfo->AuthenticatedAttributes.papItems[iAttrib];
4626 if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_MESSAGE_DIGEST_OID) == 0)
4627 {
4628 /*
4629 * Validate the message digest while we're here.
4630 */
4631 AssertReturn(pAttrib->uValues.pOctetStrings && pAttrib->uValues.pOctetStrings->cItems == 1, VERR_INTERNAL_ERROR_5);
4632
4633 RTCRDIGEST hDigest;
4634 int rc = RTCrDigestCreateByObjId(&hDigest, &pSignerInfo->DigestAlgorithm.Algorithm);
4635 if (RT_SUCCESS(rc))
4636 {
4637 rc = RTCrDigestUpdate(hDigest, pSignature->aCodeDirs[0].pCodeDir, pSignature->aCodeDirs[0].cb);
4638 if (RT_SUCCESS(rc))
4639 {
4640 if (!RTCrDigestMatch(hDigest,
4641 pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv,
4642 pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb))
4643 rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MESSAGE_DIGEST_ATTRIB_MISMATCH,
4644 "Authenticated message-digest attribute mismatch:\n"
4645 "signed: %.*Rhxs\n"
4646 "our: %.*Rhxs\n",
4647 pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb,
4648 pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv,
4649 RTCrDigestGetHashSize(hDigest), RTCrDigestGetHash(hDigest));
4650 }
4651 else
4652 rc = RTErrInfoSetF(pErrInfo, rc, "RTCrDigestUpdate failed: %Rrc", rc);
4653 RTCrDigestRelease(hDigest);
4654 }
4655 else
4656 rc = RTErrInfoSetF(pErrInfo, rc, "Failed to create a digest for OID %s: %Rrc",
4657 pSignerInfo->DigestAlgorithm.Algorithm.szObjId, rc);
4658 if (RT_FAILURE(rc))
4659 return rc;
4660 fMsgDigest = true;
4661 }
4662 else if (pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_APPLE_MULTI_CD_PLIST)
4663 {
4664 /*
4665 * An XML (better be) property list with code directory hashes in it.
4666 */
4667 if (!pAttrib->uValues.pOctetStrings || pAttrib->uValues.pOctetStrings->cItems != 1)
4668 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Bad authenticated plist attribute");
4669
4670 uint32_t cch = pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb;
4671 char const *pch = pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pch;
4672 int rc = RTStrValidateEncodingEx(pch, cch, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
4673 if (RT_FAILURE(rc))
4674 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4675 "Authenticated plist attribute is not valid UTF-8: %Rrc", rc);
4676 uint32_t const cchMin = sizeof("<?xml?><plist><dict><key>cdhashes</key><array><data>hul2SSkDQFRXbGlt3AmCp25MU0Y=</data></array></dict></plist>") - 1;
4677 if (cch < cchMin)
4678 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4679 "Authenticated plist attribute is too short: %#x, min: %#x", cch, cchMin);
4680 if (cch > _64K)
4681 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
4682 "Authenticated plist attribute is too long: %#x, max: 64KB", cch);
4683
4684 /* Copy the plist into a buffer and zero terminate it. Also allocate room for decoding a hash. */
4685 const uint32_t cbMaxHash = 128;
4686 char *pszTmp = (char *)RTMemTmpAlloc(cbMaxHash + cch + 3);
4687 if (!pszTmp)
4688 return VERR_NO_TMP_MEMORY;
4689 pszTmp[cbMaxHash + cch] = '\0';
4690 pszTmp[cbMaxHash + cch + 1] = '\0';
4691 pszTmp[cbMaxHash + cch + 2] = '\0';
4692 rc = rtldrMachO_VerifySignatureValidateCdHashesPlist(pSignature, (char *)memcpy(pszTmp + cbMaxHash, pch, cch),
4693 (uint8_t *)pszTmp, cbMaxHash, pErrInfo);
4694 RTMemTmpFree(pszTmp);
4695 if (RT_FAILURE(rc))
4696 return rc;
4697 fPlist = true;
4698 }
4699 }
4700 if (!fMsgDigest && pSignature->cCodeDirs > 1)
4701 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Missing authenticated message-digest attribute");
4702 if (!fPlist && pSignature->cCodeDirs > 1)
4703 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Missing authenticated code directory hash plist attribute");
4704 }
4705 if (pSignedData->SignerInfos.cItems < 1)
4706 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "PKCS#7 signed data contains no signatures");
4707
4708 return VINF_SUCCESS;
4709}
4710
4711
4712/**
4713 * Verifies the page hashes of the given code directory.
4714 *
4715 * @returns IPRT status code.
4716 * @param pThis The Mach-O module instance.
4717 * @param pEntry The data entry for the code directory to validate.
4718 * @param pbBuf Read buffer.
4719 * @param cbBuf Buffer size.
4720 * @param pErrInfo Where to supply extra error details. Optional.
4721 */
4722static int rtldrMachO_VerifySignatureValidateCodeDir(PRTLDRMODMACHO pThis, PRTLDRMACHCODEDIR pEntry,
4723 uint8_t *pbBuf, uint32_t cbBuf, PRTERRINFO pErrInfo)
4724{
4725 RTCRDIGEST hDigest;
4726 int rc = RTCrDigestCreateByType(&hDigest, pEntry->enmDigest);
4727 if (RT_SUCCESS(rc))
4728 {
4729 PCRTCRAPLCSCODEDIRECTORY pCodeDir = pEntry->pCodeDir;
4730 PRTLDRREADER const pRdr = pThis->Core.pReader;
4731 uint32_t cbCodeLimit = RT_BE2H_U32(pCodeDir->cbCodeLimit32);
4732 uint32_t const cbPage = RT_BIT_32(pCodeDir->cPageShift);
4733 uint32_t const cHashes = RT_BE2H_U32(pCodeDir->cCodeSlots);
4734 uint8_t const cbHash = pCodeDir->cbHash;
4735 uint8_t const *pbHash = (uint8_t const *)pCodeDir + RT_BE2H_U32(pCodeDir->offHashSlots);
4736 RTFOFF offFile = pThis->offImage;
4737 if ( RT_BE2H_U32(pCodeDir->uVersion) < RTCRAPLCS_VER_SUPPORTS_SCATTER
4738 || pCodeDir->offScatter == 0)
4739 {
4740 /*
4741 * Work the image in linear fashion.
4742 */
4743 for (uint32_t iHash = 0; iHash < cHashes; iHash++, pbHash += cbHash, cbCodeLimit -= cbPage)
4744 {
4745 RTFOFF const offPage = offFile;
4746
4747 /*
4748 * Read and digest the data for the current hash page.
4749 */
4750 rc = RTCrDigestReset(hDigest);
4751 AssertRCBreak(rc);
4752 Assert(cbCodeLimit > cbPage || iHash + 1 == cHashes);
4753 uint32_t cbLeft = iHash + 1 < cHashes ? cbPage : cbCodeLimit;
4754 while (cbLeft > 0)
4755 {
4756 uint32_t const cbToRead = RT_MIN(cbBuf, cbLeft);
4757 rc = pRdr->pfnRead(pRdr, pbBuf, cbToRead, offFile);
4758 AssertRCBreak(rc);
4759
4760 rc = RTCrDigestUpdate(hDigest, pbBuf, cbToRead);
4761 AssertRCBreak(rc);
4762
4763 offFile += cbToRead;
4764 cbLeft -= cbToRead;
4765 }
4766 AssertRCBreak(rc);
4767 rc = RTCrDigestFinal(hDigest, NULL, 0);
4768 AssertRCBreak(rc);
4769
4770 /*
4771 * Compare it.
4772 * Note! Don't use RTCrDigestMatch here as there is a truncated SHA-256 variant.
4773 */
4774 if (memcmp(pbHash, RTCrDigestGetHash(hDigest), cbHash) != 0)
4775 {
4776 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_MISMATCH,
4777 "Hash #%u (@%RX64 LB %#x) mismatch in code dir #%u: %.*Rhxs, expected %.*Rhxs",
4778 iHash, offPage, cbPage, pEntry->uSlot, (int)cbHash, pbHash,
4779 (int)cbHash, RTCrDigestGetHash(hDigest));
4780 break;
4781 }
4782
4783 }
4784 }
4785 /*
4786 * Work the image in scattered fashion.
4787 */
4788 else
4789 rc = VERR_INTERNAL_ERROR_4;
4790
4791 RTCrDigestRelease(hDigest);
4792 }
4793 return rc;
4794}
4795
4796
4797/**
4798 * Verifies the page hashes of all the code directories
4799 *
4800 * @returns IPRT status code.
4801 * @param pThis The Mach-O module instance.
4802 * @param pSignature The decoded signature data.
4803 * @param pErrInfo Where to supply extra error details. Optional.
4804 */
4805static int rtldrMachO_VerifySignatureValidateCodeDirs(PRTLDRMODMACHO pThis, PRTLDRMACHOSIGNATURE pSignature, PRTERRINFO pErrInfo)
4806{
4807 void *pvBuf = RTMemTmpAllocZ(_4K);
4808 if (pvBuf)
4809 {
4810 int rc = VERR_INTERNAL_ERROR_3;
4811 for (uint32_t i = 0; i < pSignature->cCodeDirs; i++)
4812 {
4813 rc = rtldrMachO_VerifySignatureValidateCodeDir(pThis, &pSignature->aCodeDirs[i], (uint8_t *)pvBuf, _4K, pErrInfo);
4814 if (RT_FAILURE(rc))
4815 break;
4816 }
4817 RTMemTmpFree(pvBuf);
4818 return rc;
4819 }
4820 return VERR_NO_TMP_MEMORY;
4821}
4822
4823#endif /* !IPRT_WITHOUT_LDR_VERIFY*/
4824
4825/**
4826 * @interface_method_impl{RTLDROPS,pfnVerifySignature}
4827 */
4828static DECLCALLBACK(int)
4829rtldrMachO_VerifySignature(PRTLDRMODINTERNAL pMod, PFNRTLDRVALIDATESIGNEDDATA pfnCallback, void *pvUser, PRTERRINFO pErrInfo)
4830{
4831#ifndef IPRT_WITHOUT_LDR_VERIFY
4832 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
4833 int rc = rtldrMachO_LoadSignatureBlob(pThis);
4834 if (RT_SUCCESS(rc))
4835 {
4836 PRTLDRMACHOSIGNATURE pSignature = NULL;
4837 rc = rtldrMachO_VerifySignatureDecode(pThis, &pSignature, pErrInfo);
4838 if (RT_SUCCESS(rc))
4839 {
4840 rc = rtldrMachO_VerifySignatureValidatePkcs7Hashes(pSignature, pErrInfo);
4841 if (RT_SUCCESS(rc))
4842 {
4843 rc = rtldrMachO_VerifySignatureValidateCodeDirs(pThis, pSignature, pErrInfo);
4844 if (RT_SUCCESS(rc))
4845 {
4846 /*
4847 * Finally, let the caller verify the certificate chain for the PKCS#7 bit.
4848 */
4849 rc = pfnCallback(&pThis->Core, RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA,
4850 &pSignature->ContentInfo, sizeof(pSignature->ContentInfo),
4851 pErrInfo, pvUser);
4852 }
4853 }
4854 }
4855 rtldrMachO_VerifySignatureDestroy(pSignature);
4856 }
4857 return rc;
4858#else
4859 RT_NOREF_PV(pMod); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(pvUser); RT_NOREF_PV(pErrInfo);
4860 return VERR_NOT_SUPPORTED;
4861#endif
4862}
4863
4864
4865/**
4866 * Operations for a Mach-O module interpreter.
4867 */
4868static const RTLDROPS s_rtldrMachOOps=
4869{
4870 "mach-o",
4871 rtldrMachO_Close,
4872 NULL,
4873 NULL /*pfnDone*/,
4874 rtldrMachO_EnumSymbols,
4875 /* ext */
4876 rtldrMachO_GetImageSize,
4877 rtldrMachO_GetBits,
4878 rtldrMachO_RelocateBits,
4879 rtldrMachO_GetSymbolEx,
4880 NULL /*pfnQueryForwarderInfo*/,
4881 rtldrMachO_EnumDbgInfo,
4882 rtldrMachO_EnumSegments,
4883 rtldrMachO_LinkAddressToSegOffset,
4884 rtldrMachO_LinkAddressToRva,
4885 rtldrMachO_SegOffsetToRva,
4886 rtldrMachO_RvaToSegOffset,
4887 rtldrMachO_ReadDbgInfo,
4888 rtldrMachO_QueryProp,
4889 rtldrMachO_VerifySignature,
4890 NULL /*pfnHashImage*/,
4891 NULL /*pfnUnwindFrame*/,
4892 42
4893};
4894
4895
4896/**
4897 * Handles opening Mach-O images (non-fat).
4898 */
4899DECLHIDDEN(int) rtldrMachOOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offImage,
4900 PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
4901{
4902
4903 /*
4904 * Create the instance data and do a minimal header validation.
4905 */
4906 PRTLDRMODMACHO pThis = NULL;
4907 int rc = kldrModMachODoCreate(pReader, offImage, fFlags, &pThis, pErrInfo);
4908 if (RT_SUCCESS(rc))
4909 {
4910 /*
4911 * Match up against the requested CPU architecture.
4912 */
4913 if ( enmArch == RTLDRARCH_WHATEVER
4914 || pThis->Core.enmArch == enmArch)
4915 {
4916 pThis->Core.pOps = &s_rtldrMachOOps;
4917 pThis->Core.u32Magic = RTLDRMOD_MAGIC;
4918 *phLdrMod = &pThis->Core;
4919 return VINF_SUCCESS;
4920 }
4921 rc = VERR_LDR_ARCH_MISMATCH;
4922 }
4923 if (pThis)
4924 {
4925 RTMemFree(pThis->pbLoadCommands);
4926 RTMemFree(pThis);
4927 }
4928 return rc;
4929
4930}
4931
4932
4933/**
4934 * Handles opening FAT Mach-O image.
4935 */
4936DECLHIDDEN(int) rtldrFatOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
4937{
4938 fat_header_t FatHdr;
4939 int rc = pReader->pfnRead(pReader, &FatHdr, sizeof(FatHdr), 0);
4940 if (RT_FAILURE(rc))
4941 return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
4942
4943 if (FatHdr.magic == IMAGE_FAT_SIGNATURE)
4944 { /* likely */ }
4945 else if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
4946 FatHdr.nfat_arch = RT_BSWAP_U32(FatHdr.nfat_arch);
4947 else
4948 return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "magic=%#x", FatHdr.magic);
4949 if (FatHdr.nfat_arch < 64)
4950 return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "Bad nfat_arch value: %#x", FatHdr.nfat_arch);
4951
4952 uint32_t offEntry = sizeof(FatHdr);
4953 for (uint32_t i = 0; i < FatHdr.nfat_arch; i++, offEntry += sizeof(fat_arch_t))
4954 {
4955 fat_arch_t FatEntry;
4956 rc = pReader->pfnRead(pReader, &FatEntry, sizeof(FatEntry), offEntry);
4957 if (RT_FAILURE(rc))
4958 return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
4959 if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
4960 {
4961 FatEntry.cputype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cputype);
4962 //FatEntry.cpusubtype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cpusubtype);
4963 FatEntry.offset = RT_BSWAP_U32(FatEntry.offset);
4964 //FatEntry.size = RT_BSWAP_U32(FatEntry.size);
4965 //FatEntry.align = RT_BSWAP_U32(FatEntry.align);
4966 }
4967
4968 /*
4969 * Match enmArch.
4970 */
4971 bool fMatch = false;
4972 switch (enmArch)
4973 {
4974 case RTLDRARCH_WHATEVER:
4975 fMatch = true;
4976 break;
4977
4978 case RTLDRARCH_X86_32:
4979 fMatch = FatEntry.cputype == CPU_TYPE_X86;
4980 break;
4981
4982 case RTLDRARCH_AMD64:
4983 fMatch = FatEntry.cputype == CPU_TYPE_X86_64;
4984 break;
4985
4986 case RTLDRARCH_ARM32:
4987 case RTLDRARCH_ARM64:
4988 case RTLDRARCH_X86_16:
4989 fMatch = false;
4990 break;
4991
4992 case RTLDRARCH_INVALID:
4993 case RTLDRARCH_HOST:
4994 case RTLDRARCH_END:
4995 case RTLDRARCH_32BIT_HACK:
4996 AssertFailedReturn(VERR_INVALID_PARAMETER);
4997 }
4998 if (fMatch)
4999 return rtldrMachOOpen(pReader, fFlags, enmArch, FatEntry.offset, phLdrMod, pErrInfo);
5000 }
5001
5002 return VERR_LDR_ARCH_MISMATCH;
5003
5004}
5005
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