VirtualBox

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

Last change on this file was 103005, checked in by vboxsync, 4 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use