VirtualBox

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

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

scm --update-copyright-year

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