VirtualBox

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

Last change on this file since 74726 was 74726, checked in by vboxsync, 7 years ago

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

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

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