VirtualBox

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

Last change on this file was 109183, checked in by vboxsync, 8 days ago

IPRT/ldrMachO: Deal with LC_DYLD_EXPORTS_TRIE, LC_DYLD_CHAINED_FIXUPS and S_INIT_FUNC_OFFSETS for debug loading (assertions) and similar, just so the build works on 14.7 with clang v16. jiraref:VBP-1653

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

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