VirtualBox

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

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

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

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

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