VirtualBox

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

Last change on this file since 95894 was 94869, checked in by vboxsync, 3 years ago

IPRT/ldr: Minimal MachO/ARM64 read support to make RTSignTool work. bugref:9898

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