VirtualBox

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

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

IPRT: Two more license header cleanups.

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