VirtualBox

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

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

Make scm happy. bugref:9232

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