VirtualBox

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

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

IPRT/ldr: Working on parsing Mach-O code signing structures... bugref:9232

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 157.3 KB
Line 
1/* $Id: ldrMachO.cpp 74656 2018-10-07 18:28:21Z vboxsync $ */
2/** @file
3 * kLdr - The Module Interpreter for the MACH-O format.
4 */
5
6/*
7 * Copyright (C) 2018 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 *
18 * This code is based on:
19 *
20 * Copyright (c) 2006-2013 Knut St. Osmundsen <[email protected]>
21 *
22 * Permission is hereby granted, free of charge, to any person
23 * obtaining a copy of this software and associated documentation
24 * files (the "Software"), to deal in the Software without
25 * restriction, including without limitation the rights to use,
26 * copy, modify, merge, publish, distribute, sublicense, and/or sell
27 * copies of the Software, and to permit persons to whom the
28 * Software is furnished to do so, subject to the following
29 * conditions:
30 *
31 * The above copyright notice and this permission notice shall be
32 * included in all copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
35 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
36 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
37 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
38 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
39 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
40 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
41 * OTHER DEALINGS IN THE SOFTWARE.
42 */
43
44
45/*********************************************************************************************************************************
46* Header Files *
47*********************************************************************************************************************************/
48#define LOG_GROUP RTLOGGROUP_LDR
49#include <iprt/ldr.h>
50#include "internal/iprt.h"
51
52#include <iprt/asm.h>
53#include <iprt/assert.h>
54#include <iprt/err.h>
55#include <iprt/log.h>
56#include <iprt/mem.h>
57#include <iprt/string.h>
58
59#include <iprt/formats/mach-o.h>
60#include <iprt/crypto/applecodesign.h>
61#include "internal/ldr.h"
62
63
64/*********************************************************************************************************************************
65* Defined Constants And Macros *
66*********************************************************************************************************************************/
67/** @def RTLDRMODMACHO_STRICT
68 * Define RTLDRMODMACHO_STRICT to enabled strict checks in RTLDRMODMACHO. */
69#define RTLDRMODMACHO_STRICT 1
70
71/** @def RTLDRMODMACHO_ASSERT
72 * Assert that an expression is true when KLDR_STRICT is defined.
73 */
74#ifdef RTLDRMODMACHO_STRICT
75# define RTLDRMODMACHO_ASSERT(expr) Assert(expr)
76#else
77# define RTLDRMODMACHO_ASSERT(expr) do {} while (0)
78#endif
79
80/** @def RTLDRMODMACHO_CHECK_RETURN
81 * Checks that an expression is true and return if it isn't.
82 * This is a debug aid.
83 */
84#ifdef RTLDRMODMACHO_STRICT2
85# define RTLDRMODMACHO_CHECK_RETURN(expr, rc) AssertReturn(expr, rc)
86#else
87# define RTLDRMODMACHO_CHECK_RETURN(expr, rc) do { if (!(expr)) { return (rc); } } while (0)
88#endif
89
90/** @def RTLDRMODMACHO_CHECK_RETURN
91 * Checks that an expression is true and return if it isn't.
92 * This is a debug aid.
93 */
94#ifdef RTLDRMODMACHO_STRICT2
95# define RTLDRMODMACHO_FAILED_RETURN(rc) AssertFailedReturn(rc)
96#else
97# define RTLDRMODMACHO_FAILED_RETURN(rc) return (rc)
98#endif
99
100
101/*********************************************************************************************************************************
102* Structures and Typedefs *
103*********************************************************************************************************************************/
104/**
105 * Mach-O section details.
106 */
107typedef struct RTLDRMODMACHOSECT
108{
109 /** The size of the section (in bytes). */
110 RTLDRADDR cb;
111 /** The link address of this section. */
112 RTLDRADDR LinkAddress;
113 /** The RVA of this section. */
114 RTLDRADDR RVA;
115 /** The file offset of this section.
116 * This is -1 if the section doesn't have a file backing. */
117 RTFOFF offFile;
118 /** The number of fixups. */
119 uint32_t cFixups;
120 /** The array of fixups. (lazy loaded) */
121 macho_relocation_info_t *paFixups;
122 /** The file offset of the fixups for this section.
123 * This is -1 if the section doesn't have any fixups. */
124 RTFOFF offFixups;
125 /** Mach-O section flags. */
126 uint32_t fFlags;
127 /** kLdr segment index. */
128 uint32_t iSegment;
129 /** Pointer to the Mach-O section structure. */
130 void *pvMachoSection;
131} RTLDRMODMACHOSECT, *PRTLDRMODMACHOSECT;
132
133/**
134 * Extra per-segment info.
135 *
136 * This is corresponds to a kLdr segment, not a Mach-O segment!
137 */
138typedef struct RTLDRMODMACHOSEG
139{
140 /** Common segment info. */
141 RTLDRSEG SegInfo;
142
143 /** The orignal segment number (in case we had to resort it). */
144 uint32_t iOrgSegNo;
145 /** The number of sections in the segment. */
146 uint32_t cSections;
147 /** Pointer to the sections belonging to this segment.
148 * The array resides in the big memory chunk allocated for
149 * the module handle, so it doesn't need freeing. */
150 PRTLDRMODMACHOSECT paSections;
151
152} RTLDRMODMACHOSEG, *PRTLDRMODMACHOSEG;
153
154/**
155 * Instance data for the Mach-O MH_OBJECT module interpreter.
156 * @todo interpret the other MH_* formats.
157 */
158typedef struct RTLDRMODMACHO
159{
160 /** Core module structure. */
161 RTLDRMODINTERNAL Core;
162
163 /** The minium cpu this module was built for.
164 * This might not be accurate, so use kLdrModCanExecuteOn() to check. */
165 RTLDRCPU enmCpu;
166 /** The number of segments in the module. */
167 uint32_t cSegments;
168
169 /** Pointer to the RDR file mapping of the raw file bits. NULL if not mapped. */
170 const void *pvBits;
171 /** Pointer to the user mapping. */
172 void *pvMapping;
173 /** The module open flags. */
174 uint32_t fOpenFlags;
175
176 /** The offset of the image. (FAT fun.) */
177 RTFOFF offImage;
178 /** The link address. */
179 RTLDRADDR LinkAddress;
180 /** The size of the mapped image. */
181 RTLDRADDR cbImage;
182 /** Whether we're capable of loading the image. */
183 bool fCanLoad;
184 /** Whether we're creating a global offset table segment.
185 * This dependes on the cputype and image type. */
186 bool fMakeGot;
187 /** The size of a indirect GOT jump stub entry.
188 * This is 0 if not needed. */
189 uint32_t cbJmpStub;
190 /** Effective file type. If the original was a MH_OBJECT file, the
191 * corresponding MH_DSYM needs the segment translation of a MH_OBJECT too.
192 * The MH_DSYM normally has a separate __DWARF segment, but this is
193 * automatically skipped during the transation. */
194 uint32_t uEffFileType;
195 /** Pointer to the load commands. (endian converted) */
196 uint8_t *pbLoadCommands;
197 /** The Mach-O header. (endian converted)
198 * @remark The reserved field is only valid for real 64-bit headers. */
199 mach_header_64_t Hdr;
200
201 /** The offset of the symbol table. */
202 RTFOFF offSymbols;
203 /** The number of symbols. */
204 uint32_t cSymbols;
205 /** The pointer to the loaded symbol table. */
206 void *pvaSymbols;
207 /** The offset of the string table. */
208 RTFOFF offStrings;
209 /** The size of the of the string table. */
210 uint32_t cchStrings;
211 /** Pointer to the loaded string table. */
212 char *pchStrings;
213
214 /** The image UUID, all zeros if not found. */
215 uint8_t abImageUuid[16];
216
217 /** The code signature offset. */
218 uint32_t offCodeSignature;
219 /** The code signature size (0 if not signed). */
220 uint32_t cbCodeSignature;
221 /** Pointer to the code signature blob if loaded. */
222 union
223 {
224 uint8_t *pb;
225 PCRTCRAPLCSSUPERBLOB pSuper;
226 } PtrCodeSignature;
227
228 /** The RVA of the Global Offset Table. */
229 RTLDRADDR GotRVA;
230 /** The RVA of the indirect GOT jump stubs. */
231 RTLDRADDR JmpStubsRVA;
232
233 /** The number of sections. */
234 uint32_t cSections;
235 /** Pointer to the section array running in parallel to the Mach-O one. */
236 PRTLDRMODMACHOSECT paSections;
237
238 /** Array of segments parallel to the one in KLDRMOD. */
239 RTLDRMODMACHOSEG aSegments[1];
240} RTLDRMODMACHO, *PRTLDRMODMACHO;
241
242
243
244
245/*********************************************************************************************************************************
246* Internal Functions *
247*********************************************************************************************************************************/
248#if 0
249static int32_t kldrModMachONumberOfImports(PRTLDRMODINTERNAL pMod, const void *pvBits);
250#endif
251static DECLCALLBACK(int) rtldrMachO_RelocateBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
252 RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser);
253
254
255static int kldrModMachOPreParseLoadCommands(uint8_t *pbLoadCommands, const mach_header_32_t *pHdr, PRTLDRREADER pRdr, RTFOFF offImage,
256 uint32_t fOpenFlags, uint32_t *pcSegments, uint32_t *pcSections, uint32_t *pcbStringPool,
257 bool *pfCanLoad, PRTLDRADDR pLinkAddress, uint8_t *puEffFileType, PRTERRINFO pErrInfo);
258static int kldrModMachOParseLoadCommands(PRTLDRMODMACHO pThis, char *pbStringPool, uint32_t cbStringPool);
259
260static int kldrModMachOLoadObjSymTab(PRTLDRMODMACHO pThis);
261static int kldrModMachOLoadFixups(PRTLDRMODMACHO pThis, RTFOFF offFixups, uint32_t cFixups, macho_relocation_info_t **ppaFixups);
262static int kldrModMachOMapVirginBits(PRTLDRMODMACHO pThis);
263
264static int kldrModMachODoQuerySymbol32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms, const char *pchStrings,
265 uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol, const char *pchSymbol,
266 uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind);
267static int kldrModMachODoQuerySymbol64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms, const char *pchStrings,
268 uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol, const char *pchSymbol,
269 uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind);
270static int kldrModMachODoEnumSymbols32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
271 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
272 uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser);
273static int kldrModMachODoEnumSymbols64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
274 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
275 uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser);
276static int kldrModMachOObjDoImports(PRTLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser);
277static int kldrModMachOObjDoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress);
278static int kldrModMachOFixupSectionGeneric32Bit(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, PRTLDRMODMACHOSECT pFixupSect,
279 macho_nlist_32_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress);
280static int kldrModMachOFixupSectionAMD64(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, PRTLDRMODMACHOSECT pFixupSect,
281 macho_nlist_64_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress);
282
283static int kldrModMachOMakeGOT(PRTLDRMODMACHO pThis, void *pvBits, RTLDRADDR NewBaseAddress);
284
285/*static int kldrModMachODoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress, RTLDRADDR OldBaseAddress);
286static int kldrModMachODoImports(PRTLDRMODMACHO pThis, void *pvMapping, PFNRTLDRIMPORT pfnGetImport, void *pvUser);*/
287
288
289
290/**
291 * Separate function for reading creating the Mach-O module instance to
292 * simplify cleanup on failure.
293 */
294static int kldrModMachODoCreate(PRTLDRREADER pRdr, RTFOFF offImage, uint32_t fOpenFlags,
295 PRTLDRMODMACHO *ppModMachO, PRTERRINFO pErrInfo)
296{
297 *ppModMachO = NULL;
298
299 /*
300 * Read the Mach-O header.
301 */
302 union
303 {
304 mach_header_32_t Hdr32;
305 mach_header_64_t Hdr64;
306 } s;
307 Assert(&s.Hdr32.magic == &s.Hdr64.magic);
308 Assert(&s.Hdr32.flags == &s.Hdr64.flags);
309 int rc = pRdr->pfnRead(pRdr, &s, sizeof(s), offImage);
310 if (rc)
311 return RTErrInfoSetF(pErrInfo, rc, "Error reading Mach-O header at %RTfoff: %Rrc", offImage, rc);
312 if ( s.Hdr32.magic != IMAGE_MACHO32_SIGNATURE
313 && s.Hdr32.magic != IMAGE_MACHO64_SIGNATURE)
314 {
315 if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE
316 || s.Hdr32.magic == IMAGE_MACHO64_SIGNATURE_OE)
317 return VERR_LDRMACHO_OTHER_ENDIAN_NOT_SUPPORTED;
318 return VERR_INVALID_EXE_SIGNATURE;
319 }
320
321 /* sanity checks. */
322 if ( s.Hdr32.sizeofcmds > pRdr->pfnSize(pRdr) - sizeof(mach_header_32_t)
323 || s.Hdr32.sizeofcmds < sizeof(load_command_t) * s.Hdr32.ncmds
324 || (s.Hdr32.flags & ~MH_VALID_FLAGS))
325 return VERR_LDRMACHO_BAD_HEADER;
326
327 bool fMakeGot;
328 uint8_t cbJmpStub;
329 switch (s.Hdr32.cputype)
330 {
331 case CPU_TYPE_X86:
332 fMakeGot = false;
333 cbJmpStub = 0;
334 break;
335 case CPU_TYPE_X86_64:
336 fMakeGot = s.Hdr32.filetype == MH_OBJECT;
337 cbJmpStub = fMakeGot ? 8 : 0;
338 break;
339 default:
340 return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
341 }
342
343 if ( s.Hdr32.filetype != MH_OBJECT
344 && s.Hdr32.filetype != MH_EXECUTE
345 && s.Hdr32.filetype != MH_DYLIB
346 && s.Hdr32.filetype != MH_BUNDLE
347 && s.Hdr32.filetype != MH_DSYM
348 && s.Hdr32.filetype != MH_KEXT_BUNDLE)
349 return VERR_LDRMACHO_UNSUPPORTED_FILE_TYPE;
350
351 /*
352 * Read and pre-parse the load commands to figure out how many segments we'll be needing.
353 */
354 uint8_t *pbLoadCommands = (uint8_t *)RTMemAlloc(s.Hdr32.sizeofcmds);
355 if (!pbLoadCommands)
356 return VERR_NO_MEMORY;
357 rc = pRdr->pfnRead(pRdr, pbLoadCommands, s.Hdr32.sizeofcmds,
358 s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE
359 || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE
360 ? sizeof(mach_header_32_t) + offImage
361 : sizeof(mach_header_64_t) + offImage);
362
363 uint32_t cSegments = 0;
364 uint32_t cSections = 0;
365 uint32_t cbStringPool = 0;
366 bool fCanLoad = true;
367 RTLDRADDR LinkAddress = NIL_RTLDRADDR;
368 uint8_t uEffFileType = 0;
369 if (RT_SUCCESS(rc))
370 rc = kldrModMachOPreParseLoadCommands(pbLoadCommands, &s.Hdr32, pRdr, offImage, fOpenFlags,
371 &cSegments, &cSections, &cbStringPool, &fCanLoad, &LinkAddress, &uEffFileType,
372 pErrInfo);
373 if (RT_FAILURE(rc))
374 {
375 RTMemFree(pbLoadCommands);
376 return rc;
377 }
378 cSegments += fMakeGot;
379
380
381 /*
382 * Calc the instance size, allocate and initialize it.
383 */
384 size_t const cbModAndSegs = RT_ALIGN_Z(RT_UOFFSETOF_DYN(RTLDRMODMACHO, aSegments[cSegments])
385 + sizeof(RTLDRMODMACHOSECT) * cSections, 16);
386 PRTLDRMODMACHO pThis = (PRTLDRMODMACHO)RTMemAlloc(cbModAndSegs + cbStringPool);
387 if (!pThis)
388 return VERR_NO_MEMORY;
389 *ppModMachO = pThis;
390 pThis->pbLoadCommands = pbLoadCommands;
391 pThis->offImage = offImage;
392
393 /* Core & CPU.*/
394 pThis->Core.u32Magic = 0; /* set by caller */
395 pThis->Core.eState = LDR_STATE_OPENED;
396 pThis->Core.pOps = NULL; /* set by caller. */
397 pThis->Core.pReader = pRdr;
398 switch (s.Hdr32.cputype)
399 {
400 case CPU_TYPE_X86:
401 pThis->Core.enmArch = RTLDRARCH_X86_32;
402 pThis->Core.enmEndian = RTLDRENDIAN_LITTLE;
403 switch (s.Hdr32.cpusubtype)
404 {
405 case CPU_SUBTYPE_I386_ALL: /* == CPU_SUBTYPE_386 */
406 pThis->enmCpu = RTLDRCPU_X86_32_BLEND;
407 break;
408 case CPU_SUBTYPE_486:
409 pThis->enmCpu = RTLDRCPU_I486;
410 break;
411 case CPU_SUBTYPE_486SX:
412 pThis->enmCpu = RTLDRCPU_I486SX;
413 break;
414 case CPU_SUBTYPE_PENT: /* == CPU_SUBTYPE_586 */
415 pThis->enmCpu = RTLDRCPU_I586;
416 break;
417 case CPU_SUBTYPE_PENTPRO:
418 case CPU_SUBTYPE_PENTII_M3:
419 case CPU_SUBTYPE_PENTII_M5:
420 case CPU_SUBTYPE_CELERON:
421 case CPU_SUBTYPE_CELERON_MOBILE:
422 case CPU_SUBTYPE_PENTIUM_3:
423 case CPU_SUBTYPE_PENTIUM_3_M:
424 case CPU_SUBTYPE_PENTIUM_3_XEON:
425 pThis->enmCpu = RTLDRCPU_I686;
426 break;
427 case CPU_SUBTYPE_PENTIUM_M:
428 case CPU_SUBTYPE_PENTIUM_4:
429 case CPU_SUBTYPE_PENTIUM_4_M:
430 case CPU_SUBTYPE_XEON:
431 case CPU_SUBTYPE_XEON_MP:
432 pThis->enmCpu = RTLDRCPU_P4;
433 break;
434
435 default:
436 /* Hack for kextutil output. */
437 if ( s.Hdr32.cpusubtype == 0
438 && s.Hdr32.filetype == MH_OBJECT)
439 break;
440 return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
441 }
442 break;
443
444 case CPU_TYPE_X86_64:
445 pThis->Core.enmArch = RTLDRARCH_AMD64;
446 pThis->Core.enmEndian = RTLDRENDIAN_LITTLE;
447 switch (s.Hdr32.cpusubtype & ~CPU_SUBTYPE_MASK)
448 {
449 case CPU_SUBTYPE_X86_64_ALL: pThis->enmCpu = RTLDRCPU_AMD64_BLEND; break;
450 default:
451 return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
452 }
453 break;
454
455 default:
456 return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
457 }
458
459 pThis->Core.enmFormat = RTLDRFMT_MACHO;
460 switch (s.Hdr32.filetype)
461 {
462 case MH_OBJECT: pThis->Core.enmType = RTLDRTYPE_OBJECT; break;
463 case MH_EXECUTE: pThis->Core.enmType = RTLDRTYPE_EXECUTABLE_FIXED; break;
464 case MH_DYLIB: pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
465 case MH_BUNDLE: pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
466 case MH_KEXT_BUNDLE:pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
467 case MH_DSYM: pThis->Core.enmType = RTLDRTYPE_DEBUG_INFO; break;
468 default:
469 return VERR_LDRMACHO_UNSUPPORTED_FILE_TYPE;
470 }
471
472 /* RTLDRMODMACHO */
473 pThis->cSegments = cSegments;
474 pThis->pvBits = NULL;
475 pThis->pvMapping = NULL;
476 pThis->fOpenFlags = fOpenFlags;
477 pThis->Hdr = s.Hdr64;
478 if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE
479 || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE)
480 pThis->Hdr.reserved = 0;
481 pThis->LinkAddress = LinkAddress;
482 pThis->cbImage = 0;
483 pThis->fCanLoad = fCanLoad;
484 pThis->fMakeGot = fMakeGot;
485 pThis->cbJmpStub = cbJmpStub;
486 pThis->uEffFileType = uEffFileType;
487 pThis->offSymbols = 0;
488 pThis->cSymbols = 0;
489 pThis->pvaSymbols = NULL;
490 pThis->offStrings = 0;
491 pThis->cchStrings = 0;
492 pThis->pchStrings = NULL;
493 memset(pThis->abImageUuid, 0, sizeof(pThis->abImageUuid));
494 pThis->offCodeSignature = 0;
495 pThis->cbCodeSignature = 0;
496 pThis->PtrCodeSignature.pb = NULL;
497 pThis->GotRVA = NIL_RTLDRADDR;
498 pThis->JmpStubsRVA = NIL_RTLDRADDR;
499 pThis->cSections = cSections;
500 pThis->paSections = (PRTLDRMODMACHOSECT)&pThis->aSegments[pThis->cSegments];
501
502 /*
503 * Setup the KLDRMOD segment array.
504 */
505 rc = kldrModMachOParseLoadCommands(pThis, (char *)pThis + cbModAndSegs, cbStringPool);
506
507 /*
508 * We're done.
509 */
510 return rc;
511}
512
513
514/**
515 * Converts, validates and preparses the load commands before we carve
516 * out the module instance.
517 *
518 * The conversion that's preformed is format endian to host endian. The
519 * preparsing has to do with segment counting, section counting and string pool
520 * sizing.
521 *
522 * Segment are created in two different ways, depending on the file type.
523 *
524 * For object files there is only one segment command without a given segment
525 * name. The sections inside that segment have different segment names and are
526 * not sorted by their segname attribute. We create one segment for each
527 * section, with the segment name being 'segname.sectname' in order to hopefully
528 * keep the names unique. Debug sections does not get segments.
529 *
530 * For non-object files, one kLdr segment is created for each Mach-O segment.
531 * Debug segments is not exposed by kLdr via the kLdr segment table, but via the
532 * debug enumeration callback API.
533 *
534 * @returns IPRT status code.
535 * @param pbLoadCommands The load commands to parse.
536 * @param pHdr The header.
537 * @param pRdr The file reader.
538 * @param offImage The image header (FAT fun).
539 * @param fOpenFlags RTLDR_O_XXX.
540 * @param pcSegments Where to store the segment count.
541 * @param pcSections Where to store the section count.
542 * @param pcbStringPool Where to store the string pool size.
543 * @param pfCanLoad Where to store the can-load-image indicator.
544 * @param pLinkAddress Where to store the image link address (i.e. the
545 * lowest segment address).
546 * @param puEffFileType Where to store the effective file type.
547 * @param pErrInfo Where to return additional error info. Optional.
548 */
549static int kldrModMachOPreParseLoadCommands(uint8_t *pbLoadCommands, const mach_header_32_t *pHdr, PRTLDRREADER pRdr,
550 RTFOFF offImage, uint32_t fOpenFlags, uint32_t *pcSegments, uint32_t *pcSections,
551 uint32_t *pcbStringPool, bool *pfCanLoad, PRTLDRADDR pLinkAddress,
552 uint8_t *puEffFileType, PRTERRINFO pErrInfo)
553{
554 union
555 {
556 uint8_t *pb;
557 load_command_t *pLoadCmd;
558 segment_command_32_t *pSeg32;
559 segment_command_64_t *pSeg64;
560 thread_command_t *pThread;
561 symtab_command_t *pSymTab;
562 uuid_command_t *pUuid;
563 } u;
564 const uint64_t cbFile = pRdr->pfnSize(pRdr) - offImage;
565 uint32_t cSegments = 0;
566 uint32_t cSections = 0;
567 size_t cbStringPool = 0;
568 uint32_t cLeft = pHdr->ncmds;
569 uint32_t cbLeft = pHdr->sizeofcmds;
570 uint8_t *pb = pbLoadCommands;
571 int cSegmentCommands = 0;
572 int cSymbolTabs = 0;
573 int fConvertEndian = pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
574 || pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE;
575 uint8_t uEffFileType = *puEffFileType = pHdr->filetype;
576
577 *pcSegments = 0;
578 *pcSections = 0;
579 *pcbStringPool = 0;
580 *pfCanLoad = true;
581 *pLinkAddress = ~(RTLDRADDR)0;
582
583 while (cLeft-- > 0)
584 {
585 u.pb = pb;
586
587 /*
588 * Convert and validate command header.
589 */
590 RTLDRMODMACHO_CHECK_RETURN(cbLeft >= sizeof(load_command_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
591 if (fConvertEndian)
592 {
593 u.pLoadCmd->cmd = RT_BSWAP_U32(u.pLoadCmd->cmd);
594 u.pLoadCmd->cmdsize = RT_BSWAP_U32(u.pLoadCmd->cmdsize);
595 }
596 RTLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize <= cbLeft, VERR_LDRMACHO_BAD_LOAD_COMMAND);
597 cbLeft -= u.pLoadCmd->cmdsize;
598 pb += u.pLoadCmd->cmdsize;
599
600 /*
601 * Convert endian if needed, parse and validate the command.
602 */
603 switch (u.pLoadCmd->cmd)
604 {
605 case LC_SEGMENT_32:
606 {
607 segment_command_32_t *pSrcSeg = (segment_command_32_t *)u.pLoadCmd;
608 section_32_t *pFirstSect = (section_32_t *)(pSrcSeg + 1);
609 section_32_t *pSect = pFirstSect;
610 uint32_t cSectionsLeft = pSrcSeg->nsects;
611 uint64_t offSect = 0;
612
613 /* Convert and verify the segment. */
614 RTLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_32_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
615 RTLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
616 || pHdr->magic == IMAGE_MACHO32_SIGNATURE, VERR_LDRMACHO_BIT_MIX);
617 if (fConvertEndian)
618 {
619 pSrcSeg->vmaddr = RT_BSWAP_U32(pSrcSeg->vmaddr);
620 pSrcSeg->vmsize = RT_BSWAP_U32(pSrcSeg->vmsize);
621 pSrcSeg->fileoff = RT_BSWAP_U32(pSrcSeg->fileoff);
622 pSrcSeg->filesize = RT_BSWAP_U32(pSrcSeg->filesize);
623 pSrcSeg->maxprot = RT_BSWAP_U32(pSrcSeg->maxprot);
624 pSrcSeg->initprot = RT_BSWAP_U32(pSrcSeg->initprot);
625 pSrcSeg->nsects = RT_BSWAP_U32(pSrcSeg->nsects);
626 pSrcSeg->flags = RT_BSWAP_U32(pSrcSeg->flags);
627 }
628
629 /* Validation code shared with the 64-bit variant. */
630#define VALIDATE_AND_ADD_SEGMENT(a_cBits) \
631 do { \
632 bool fSkipSeg = !strcmp(pSrcSeg->segname, "__DWARF") /* Note: Not for non-object files. */ \
633 || ( !strcmp(pSrcSeg->segname, "__CTF") /* Their CTF tool did/does weird things, */ \
634 && pSrcSeg->vmsize == 0) /* overlapping vmaddr and zero vmsize. */ \
635 || (cSectionsLeft > 0 && (pFirstSect->flags & S_ATTR_DEBUG)); \
636 \
637 /* MH_DSYM files for MH_OBJECT files must have MH_OBJECT segment translation. */ \
638 if ( uEffFileType == MH_DSYM \
639 && cSegmentCommands == 0 \
640 && pSrcSeg->segname[0] == '\0') \
641 *puEffFileType = uEffFileType = MH_OBJECT; \
642 \
643 RTLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize == 0 \
644 || ( pSrcSeg->fileoff <= cbFile \
645 && (uint64_t)pSrcSeg->fileoff + pSrcSeg->filesize <= cbFile), \
646 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
647 RTLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize <= pSrcSeg->vmsize \
648 || (fSkipSeg && !strcmp(pSrcSeg->segname, "__CTF") /* see above */), \
649 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
650 RTLDRMODMACHO_CHECK_RETURN(!(~pSrcSeg->maxprot & pSrcSeg->initprot), \
651 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
652 RTLDRMODMACHO_CHECK_RETURN(!(pSrcSeg->flags & ~(SG_HIGHVM | SG_FVMLIB | SG_NORELOC | SG_PROTECTED_VERSION_1)), \
653 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
654 RTLDRMODMACHO_CHECK_RETURN( pSrcSeg->nsects * sizeof(section_##a_cBits##_t) \
655 <= u.pLoadCmd->cmdsize - sizeof(segment_command_##a_cBits##_t), \
656 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
657 RTLDRMODMACHO_CHECK_RETURN( uEffFileType != MH_OBJECT \
658 || cSegmentCommands == 0 \
659 || ( cSegmentCommands == 1 \
660 && uEffFileType == MH_OBJECT \
661 && pHdr->filetype == MH_DSYM \
662 && fSkipSeg), \
663 VERR_LDRMACHO_BAD_OBJECT_FILE); \
664 cSegmentCommands++; \
665 \
666 /* Add the segment, if not object file. */ \
667 if (!fSkipSeg && uEffFileType != MH_OBJECT) \
668 { \
669 cbStringPool += RTStrNLen(&pSrcSeg->segname[0], sizeof(pSrcSeg->segname)) + 1; \
670 cSegments++; \
671 if (cSegments == 1) /* The link address is set by the first segment. */ \
672 *pLinkAddress = pSrcSeg->vmaddr; \
673 } \
674 } while (0)
675
676 VALIDATE_AND_ADD_SEGMENT(32);
677
678 /*
679 * Convert, validate and parse the sections.
680 */
681 cSectionsLeft = pSrcSeg->nsects;
682 pFirstSect = pSect = (section_32_t *)(pSrcSeg + 1);
683 while (cSectionsLeft-- > 0)
684 {
685 if (fConvertEndian)
686 {
687 pSect->addr = RT_BSWAP_U32(pSect->addr);
688 pSect->size = RT_BSWAP_U32(pSect->size);
689 pSect->offset = RT_BSWAP_U32(pSect->offset);
690 pSect->align = RT_BSWAP_U32(pSect->align);
691 pSect->reloff = RT_BSWAP_U32(pSect->reloff);
692 pSect->nreloc = RT_BSWAP_U32(pSect->nreloc);
693 pSect->flags = RT_BSWAP_U32(pSect->flags);
694 pSect->reserved1 = RT_BSWAP_U32(pSect->reserved1);
695 pSect->reserved2 = RT_BSWAP_U32(pSect->reserved2);
696 }
697
698 /* Validation code shared with the 64-bit variant. */
699 #define VALIDATE_AND_ADD_SECTION(a_cBits) \
700 do { \
701 int fFileBits; \
702 \
703 /* validate */ \
704 if (uEffFileType != MH_OBJECT) \
705 RTLDRMODMACHO_CHECK_RETURN(!strcmp(pSect->segname, pSrcSeg->segname),\
706 VERR_LDRMACHO_BAD_SECTION); \
707 \
708 switch (pSect->flags & SECTION_TYPE) \
709 { \
710 case S_ZEROFILL: \
711 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
712 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
713 fFileBits = 0; \
714 break; \
715 case S_REGULAR: \
716 case S_CSTRING_LITERALS: \
717 case S_COALESCED: \
718 case S_4BYTE_LITERALS: \
719 case S_8BYTE_LITERALS: \
720 case S_16BYTE_LITERALS: \
721 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
722 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
723 fFileBits = 1; \
724 break; \
725 \
726 case S_SYMBOL_STUBS: \
727 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
728 /* reserved2 == stub size. 0 has been seen (corecrypto.kext) */ \
729 RTLDRMODMACHO_CHECK_RETURN(pSect->reserved2 < 64, VERR_LDRMACHO_BAD_SECTION); \
730 fFileBits = 1; \
731 break; \
732 \
733 case S_NON_LAZY_SYMBOL_POINTERS: \
734 case S_LAZY_SYMBOL_POINTERS: \
735 case S_LAZY_DYLIB_SYMBOL_POINTERS: \
736 /* (reserved 1 = is indirect symbol table index) */ \
737 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
738 *pfCanLoad = false; \
739 fFileBits = -1; /* __DATA.__got in the 64-bit mach_kernel has bits, any things without bits? */ \
740 break; \
741 \
742 case S_MOD_INIT_FUNC_POINTERS: \
743 /** @todo this requires a query API or flag... (e.g. C++ constructors) */ \
744 RTLDRMODMACHO_CHECK_RETURN(fOpenFlags & RTLDR_O_FOR_DEBUG, \
745 VERR_LDRMACHO_UNSUPPORTED_INIT_SECTION); \
746 /* Falls through. */ \
747 case S_MOD_TERM_FUNC_POINTERS: \
748 /** @todo this requires a query API or flag... (e.g. C++ destructors) */ \
749 RTLDRMODMACHO_CHECK_RETURN(fOpenFlags & RTLDR_O_FOR_DEBUG, \
750 VERR_LDRMACHO_UNSUPPORTED_TERM_SECTION); \
751 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
752 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
753 fFileBits = 1; \
754 break; /* ignored */ \
755 \
756 case S_LITERAL_POINTERS: \
757 case S_DTRACE_DOF: \
758 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
759 RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
760 fFileBits = 1; \
761 break; \
762 \
763 case S_INTERPOSING: \
764 case S_GB_ZEROFILL: \
765 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNSUPPORTED_SECTION); \
766 \
767 default: \
768 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNKNOWN_SECTION); \
769 } \
770 RTLDRMODMACHO_CHECK_RETURN(!(pSect->flags & ~( S_ATTR_PURE_INSTRUCTIONS | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS \
771 | S_ATTR_NO_DEAD_STRIP | S_ATTR_LIVE_SUPPORT | S_ATTR_SELF_MODIFYING_CODE \
772 | S_ATTR_DEBUG | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_EXT_RELOC \
773 | S_ATTR_LOC_RELOC | SECTION_TYPE)), \
774 VERR_LDRMACHO_BAD_SECTION); \
775 RTLDRMODMACHO_CHECK_RETURN((pSect->flags & S_ATTR_DEBUG) == (pFirstSect->flags & S_ATTR_DEBUG), \
776 VERR_LDRMACHO_MIXED_DEBUG_SECTION_FLAGS); \
777 \
778 RTLDRMODMACHO_CHECK_RETURN(pSect->addr - pSrcSeg->vmaddr <= pSrcSeg->vmsize, \
779 VERR_LDRMACHO_BAD_SECTION); \
780 RTLDRMODMACHO_CHECK_RETURN( pSect->addr - pSrcSeg->vmaddr + pSect->size <= pSrcSeg->vmsize \
781 || !strcmp(pSrcSeg->segname, "__CTF") /* see above */, \
782 VERR_LDRMACHO_BAD_SECTION); \
783 RTLDRMODMACHO_CHECK_RETURN(pSect->align < 31, \
784 VERR_LDRMACHO_BAD_SECTION); \
785 /* Workaround for buggy ld64 (or as, llvm, ++) that produces a misaligned __TEXT.__unwind_info. */ \
786 /* Seen: pSect->align = 4, pSect->addr = 0x5ebe14. Just adjust the alignment down. */ \
787 if ( ((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSect->addr) \
788 && pSect->align == 4 \
789 && strcmp(pSect->sectname, "__unwind_info") == 0) \
790 pSect->align = 2; \
791 RTLDRMODMACHO_CHECK_RETURN(!((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSect->addr), \
792 VERR_LDRMACHO_BAD_SECTION); \
793 RTLDRMODMACHO_CHECK_RETURN(!((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSrcSeg->vmaddr), \
794 VERR_LDRMACHO_BAD_SECTION); \
795 \
796 /* Adjust the section offset before we check file offset. */ \
797 offSect = (offSect + RT_BIT_64(pSect->align) - UINT64_C(1)) & ~(RT_BIT_64(pSect->align) - UINT64_C(1)); \
798 if (pSect->addr) \
799 { \
800 RTLDRMODMACHO_CHECK_RETURN(offSect <= pSect->addr - pSrcSeg->vmaddr, VERR_LDRMACHO_BAD_SECTION); \
801 if (offSect < pSect->addr - pSrcSeg->vmaddr) \
802 offSect = pSect->addr - pSrcSeg->vmaddr; \
803 } \
804 \
805 if (fFileBits && pSect->offset == 0 && pSrcSeg->fileoff == 0 && pHdr->filetype == MH_DSYM) \
806 fFileBits = 0; \
807 if (fFileBits) \
808 { \
809 if (uEffFileType != MH_OBJECT) \
810 { \
811 RTLDRMODMACHO_CHECK_RETURN(pSect->offset == pSrcSeg->fileoff + offSect, \
812 VERR_LDRMACHO_NON_CONT_SEG_BITS); \
813 RTLDRMODMACHO_CHECK_RETURN(pSect->offset - pSrcSeg->fileoff <= pSrcSeg->filesize, \
814 VERR_LDRMACHO_BAD_SECTION); \
815 } \
816 RTLDRMODMACHO_CHECK_RETURN(pSect->offset <= cbFile, \
817 VERR_LDRMACHO_BAD_SECTION); \
818 RTLDRMODMACHO_CHECK_RETURN((uint64_t)pSect->offset + pSect->size <= cbFile, \
819 VERR_LDRMACHO_BAD_SECTION); \
820 } \
821 else \
822 RTLDRMODMACHO_CHECK_RETURN(pSect->offset == 0, VERR_LDRMACHO_BAD_SECTION); \
823 \
824 if (!pSect->nreloc) \
825 RTLDRMODMACHO_CHECK_RETURN(!pSect->reloff, \
826 VERR_LDRMACHO_BAD_SECTION); \
827 else \
828 { \
829 RTLDRMODMACHO_CHECK_RETURN(pSect->reloff <= cbFile, \
830 VERR_LDRMACHO_BAD_SECTION); \
831 RTLDRMODMACHO_CHECK_RETURN( (uint64_t)pSect->reloff \
832 + (RTFOFF)pSect->nreloc * sizeof(macho_relocation_info_t) \
833 <= cbFile, \
834 VERR_LDRMACHO_BAD_SECTION); \
835 } \
836 \
837 /* Validate against file type (pointless?) and count the section, for object files add segment. */ \
838 switch (uEffFileType) \
839 { \
840 case MH_OBJECT: \
841 if ( !(pSect->flags & S_ATTR_DEBUG) \
842 && strcmp(pSect->segname, "__DWARF")) \
843 { \
844 cbStringPool += RTStrNLen(&pSect->segname[0], sizeof(pSect->segname)) + 1; \
845 cbStringPool += RTStrNLen(&pSect->sectname[0], sizeof(pSect->sectname)) + 1; \
846 cSegments++; \
847 if (cSegments == 1) /* The link address is set by the first segment. */ \
848 *pLinkAddress = pSect->addr; \
849 } \
850 /* Falls through. */ \
851 case MH_EXECUTE: \
852 case MH_DYLIB: \
853 case MH_BUNDLE: \
854 case MH_DSYM: \
855 case MH_KEXT_BUNDLE: \
856 cSections++; \
857 break; \
858 default: \
859 RTLDRMODMACHO_FAILED_RETURN(VERR_INVALID_PARAMETER); \
860 } \
861 \
862 /* Advance the section offset, since we're also aligning it. */ \
863 offSect += pSect->size; \
864 } while (0) /* VALIDATE_AND_ADD_SECTION */
865
866 VALIDATE_AND_ADD_SECTION(32);
867
868 /* next */
869 pSect++;
870 }
871 break;
872 }
873
874 case LC_SEGMENT_64:
875 {
876 segment_command_64_t *pSrcSeg = (segment_command_64_t *)u.pLoadCmd;
877 section_64_t *pFirstSect = (section_64_t *)(pSrcSeg + 1);
878 section_64_t *pSect = pFirstSect;
879 uint32_t cSectionsLeft = pSrcSeg->nsects;
880 uint64_t offSect = 0;
881
882 /* Convert and verify the segment. */
883 RTLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_64_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
884 RTLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE
885 || pHdr->magic == IMAGE_MACHO64_SIGNATURE, VERR_LDRMACHO_BIT_MIX);
886 if (fConvertEndian)
887 {
888 pSrcSeg->vmaddr = RT_BSWAP_U64(pSrcSeg->vmaddr);
889 pSrcSeg->vmsize = RT_BSWAP_U64(pSrcSeg->vmsize);
890 pSrcSeg->fileoff = RT_BSWAP_U64(pSrcSeg->fileoff);
891 pSrcSeg->filesize = RT_BSWAP_U64(pSrcSeg->filesize);
892 pSrcSeg->maxprot = RT_BSWAP_U32(pSrcSeg->maxprot);
893 pSrcSeg->initprot = RT_BSWAP_U32(pSrcSeg->initprot);
894 pSrcSeg->nsects = RT_BSWAP_U32(pSrcSeg->nsects);
895 pSrcSeg->flags = RT_BSWAP_U32(pSrcSeg->flags);
896 }
897
898 VALIDATE_AND_ADD_SEGMENT(64);
899
900 /*
901 * Convert, validate and parse the sections.
902 */
903 while (cSectionsLeft-- > 0)
904 {
905 if (fConvertEndian)
906 {
907 pSect->addr = RT_BSWAP_U64(pSect->addr);
908 pSect->size = RT_BSWAP_U64(pSect->size);
909 pSect->offset = RT_BSWAP_U32(pSect->offset);
910 pSect->align = RT_BSWAP_U32(pSect->align);
911 pSect->reloff = RT_BSWAP_U32(pSect->reloff);
912 pSect->nreloc = RT_BSWAP_U32(pSect->nreloc);
913 pSect->flags = RT_BSWAP_U32(pSect->flags);
914 pSect->reserved1 = RT_BSWAP_U32(pSect->reserved1);
915 pSect->reserved2 = RT_BSWAP_U32(pSect->reserved2);
916 }
917
918 VALIDATE_AND_ADD_SECTION(64);
919
920 /* next */
921 pSect++;
922 }
923 break;
924 } /* LC_SEGMENT_64 */
925
926
927 case LC_SYMTAB:
928 {
929 size_t cbSym;
930 if (fConvertEndian)
931 {
932 u.pSymTab->symoff = RT_BSWAP_U32(u.pSymTab->symoff);
933 u.pSymTab->nsyms = RT_BSWAP_U32(u.pSymTab->nsyms);
934 u.pSymTab->stroff = RT_BSWAP_U32(u.pSymTab->stroff);
935 u.pSymTab->strsize = RT_BSWAP_U32(u.pSymTab->strsize);
936 }
937
938 /* verify */
939 cbSym = pHdr->magic == IMAGE_MACHO32_SIGNATURE
940 || pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
941 ? sizeof(macho_nlist_32_t)
942 : sizeof(macho_nlist_64_t);
943 if ( u.pSymTab->symoff >= cbFile
944 || (uint64_t)u.pSymTab->symoff + u.pSymTab->nsyms * cbSym > cbFile)
945 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
946 if ( u.pSymTab->stroff >= cbFile
947 || (uint64_t)u.pSymTab->stroff + u.pSymTab->strsize > cbFile)
948 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
949
950 /* only one string in objects, please. */
951 cSymbolTabs++;
952 if ( uEffFileType == MH_OBJECT
953 && cSymbolTabs != 1)
954 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_OBJECT_FILE);
955 break;
956 }
957
958 case LC_DYSYMTAB:
959 /** @todo deal with this! */
960 break;
961
962 case LC_THREAD:
963 case LC_UNIXTHREAD:
964 {
965 uint32_t *pu32 = (uint32_t *)(u.pb + sizeof(load_command_t));
966 uint32_t cItemsLeft = (u.pThread->cmdsize - sizeof(load_command_t)) / sizeof(uint32_t);
967 while (cItemsLeft)
968 {
969 /* convert & verify header items ([0] == flavor, [1] == uint32_t count). */
970 if (cItemsLeft < 2)
971 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
972 if (fConvertEndian)
973 {
974 pu32[0] = RT_BSWAP_U32(pu32[0]);
975 pu32[1] = RT_BSWAP_U32(pu32[1]);
976 }
977 if (pu32[1] + 2 > cItemsLeft)
978 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
979
980 /* convert & verify according to flavor. */
981 switch (pu32[0])
982 {
983 /** @todo */
984 default:
985 break;
986 }
987
988 /* next */
989 cItemsLeft -= pu32[1] + 2;
990 pu32 += pu32[1] + 2;
991 }
992 break;
993 }
994
995 case LC_UUID:
996 if (u.pUuid->cmdsize != sizeof(uuid_command_t))
997 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
998 /** @todo Check anything here need converting? */
999 break;
1000
1001 case LC_CODE_SIGNATURE:
1002 if (u.pUuid->cmdsize != sizeof(linkedit_data_command_t))
1003 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
1004 break;
1005
1006 case LC_VERSION_MIN_MACOSX:
1007 case LC_VERSION_MIN_IPHONEOS:
1008 if (u.pUuid->cmdsize != sizeof(version_min_command_t))
1009 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
1010 break;
1011
1012 case LC_SOURCE_VERSION: /* Harmless. It just gives a clue regarding the source code revision/version. */
1013 case LC_DATA_IN_CODE: /* Ignore */
1014 case LC_DYLIB_CODE_SIGN_DRS:/* Ignore */
1015 /** @todo valid command size. */
1016 break;
1017
1018 case LC_FUNCTION_STARTS: /** @todo dylib++ */
1019 /* Ignore for now. */
1020 break;
1021 case LC_ID_DYLIB: /** @todo dylib */
1022 case LC_LOAD_DYLIB: /** @todo dylib */
1023 case LC_LOAD_DYLINKER: /** @todo dylib */
1024 case LC_TWOLEVEL_HINTS: /** @todo dylib */
1025 case LC_LOAD_WEAK_DYLIB: /** @todo dylib */
1026 case LC_ID_DYLINKER: /** @todo dylib */
1027 case LC_RPATH: /** @todo dylib */
1028 case LC_SEGMENT_SPLIT_INFO: /** @todo dylib++ */
1029 case LC_REEXPORT_DYLIB: /** @todo dylib */
1030 case LC_DYLD_INFO: /** @todo dylib */
1031 case LC_DYLD_INFO_ONLY: /** @todo dylib */
1032 case LC_LOAD_UPWARD_DYLIB: /** @todo dylib */
1033 case LC_DYLD_ENVIRONMENT: /** @todo dylib */
1034 case LC_MAIN: /** @todo parse this and find and entry point or smth. */
1035 /** @todo valid command size. */
1036 if (!(fOpenFlags & RTLDR_O_FOR_DEBUG))
1037 RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_UNSUPPORTED_LOAD_COMMAND,
1038 "cmd=%#x", u.pLoadCmd->cmd));
1039 *pfCanLoad = false;
1040 break;
1041
1042 case LC_LOADFVMLIB:
1043 case LC_IDFVMLIB:
1044 case LC_IDENT:
1045 case LC_FVMFILE:
1046 case LC_PREPAGE:
1047 case LC_PREBOUND_DYLIB:
1048 case LC_ROUTINES:
1049 case LC_ROUTINES_64:
1050 case LC_SUB_FRAMEWORK:
1051 case LC_SUB_UMBRELLA:
1052 case LC_SUB_CLIENT:
1053 case LC_SUB_LIBRARY:
1054 case LC_PREBIND_CKSUM:
1055 case LC_SYMSEG:
1056 RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_UNSUPPORTED_LOAD_COMMAND,
1057 "cmd=%#x", u.pLoadCmd->cmd));
1058
1059 default:
1060 RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_UNKNOWN_LOAD_COMMAND,
1061 "cmd=%#x", u.pLoadCmd->cmd));
1062 }
1063 }
1064
1065 /* be strict. */
1066 if (cbLeft)
1067 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
1068
1069 switch (uEffFileType)
1070 {
1071 case MH_OBJECT:
1072 case MH_EXECUTE:
1073 case MH_DYLIB:
1074 case MH_BUNDLE:
1075 case MH_DSYM:
1076 case MH_KEXT_BUNDLE:
1077 if (!cSegments)
1078 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_OBJECT_FILE);
1079 break;
1080 }
1081
1082 *pcSegments = cSegments;
1083 *pcSections = cSections;
1084 *pcbStringPool = (uint32_t)cbStringPool;
1085
1086 return VINF_SUCCESS;
1087}
1088
1089
1090/**
1091 * Parses the load commands after we've carved out the module instance.
1092 *
1093 * This fills in the segment table and perhaps some other properties.
1094 *
1095 * @returns IPRT status code.
1096 * @param pThis The module.
1097 * @param pbStringPool The string pool
1098 * @param cbStringPool The size of the string pool.
1099 */
1100static int kldrModMachOParseLoadCommands(PRTLDRMODMACHO pThis, char *pbStringPool, uint32_t cbStringPool)
1101{
1102 union
1103 {
1104 const uint8_t *pb;
1105 const load_command_t *pLoadCmd;
1106 const segment_command_32_t *pSeg32;
1107 const segment_command_64_t *pSeg64;
1108 const symtab_command_t *pSymTab;
1109 const uuid_command_t *pUuid;
1110 const linkedit_data_command_t *pData;
1111 } u;
1112 uint32_t cLeft = pThis->Hdr.ncmds;
1113 uint32_t cbLeft = pThis->Hdr.sizeofcmds;
1114 const uint8_t *pb = pThis->pbLoadCommands;
1115 PRTLDRMODMACHOSEG pDstSeg = &pThis->aSegments[0];
1116 PRTLDRMODMACHOSECT pSectExtra = pThis->paSections;
1117 const uint32_t cSegments = pThis->cSegments;
1118 PRTLDRMODMACHOSEG pSegItr;
1119 RT_NOREF(cbStringPool);
1120
1121 while (cLeft-- > 0)
1122 {
1123 u.pb = pb;
1124 cbLeft -= u.pLoadCmd->cmdsize;
1125 pb += u.pLoadCmd->cmdsize;
1126
1127 /*
1128 * Convert endian if needed, parse and validate the command.
1129 */
1130 switch (u.pLoadCmd->cmd)
1131 {
1132 case LC_SEGMENT_32:
1133 {
1134 const segment_command_32_t *pSrcSeg = (const segment_command_32_t *)u.pLoadCmd;
1135 section_32_t *pFirstSect = (section_32_t *)(pSrcSeg + 1);
1136 section_32_t *pSect = pFirstSect;
1137 uint32_t cSectionsLeft = pSrcSeg->nsects;
1138
1139 /* Adds a segment, used by the macro below and thus shared with the 64-bit segment variant. */
1140#define NEW_SEGMENT(a_cBits, a_achName1, a_fObjFile, a_achName2, a_SegAddr, a_cbSeg, a_fFileBits, a_offFile, a_cbFile) \
1141 do { \
1142 pDstSeg->SegInfo.pszName = pbStringPool; \
1143 pDstSeg->SegInfo.cchName = (uint32_t)RTStrNLen(a_achName1, sizeof(a_achName1)); \
1144 memcpy(pbStringPool, a_achName1, pDstSeg->SegInfo.cchName); \
1145 pbStringPool += pDstSeg->SegInfo.cchName; \
1146 if (a_fObjFile) \
1147 { /* MH_OBJECT: Add '.sectname' - sections aren't sorted by segments. */ \
1148 size_t cchName2 = RTStrNLen(a_achName2, sizeof(a_achName2)); \
1149 *pbStringPool++ = '.'; \
1150 memcpy(pbStringPool, a_achName2, cchName2); \
1151 pbStringPool += cchName2; \
1152 pDstSeg->SegInfo.cchName += (uint32_t)cchName2; \
1153 } \
1154 *pbStringPool++ = '\0'; \
1155 pDstSeg->SegInfo.SelFlat = 0; \
1156 pDstSeg->SegInfo.Sel16bit = 0; \
1157 pDstSeg->SegInfo.fFlags = 0; \
1158 pDstSeg->SegInfo.fProt = RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC; /** @todo fixme! */ \
1159 pDstSeg->SegInfo.cb = (a_cbSeg); \
1160 pDstSeg->SegInfo.Alignment = 1; /* updated while parsing sections. */ \
1161 pDstSeg->SegInfo.LinkAddress = (a_SegAddr); \
1162 if (a_fFileBits) \
1163 { \
1164 pDstSeg->SegInfo.offFile = (RTFOFF)((a_offFile) + pThis->offImage); \
1165 pDstSeg->SegInfo.cbFile = (RTFOFF)(a_cbFile); \
1166 } \
1167 else \
1168 { \
1169 pDstSeg->SegInfo.offFile = -1; \
1170 pDstSeg->SegInfo.cbFile = -1; \
1171 } \
1172 pDstSeg->SegInfo.RVA = (a_SegAddr) - pThis->LinkAddress; \
1173 pDstSeg->SegInfo.cbMapped = 0; \
1174 \
1175 pDstSeg->iOrgSegNo = (uint32_t)(pDstSeg - &pThis->aSegments[0]); \
1176 pDstSeg->cSections = 0; \
1177 pDstSeg->paSections = pSectExtra; \
1178 } while (0)
1179
1180 /* Closes the new segment - part of NEW_SEGMENT. */
1181#define CLOSE_SEGMENT() \
1182 do { \
1183 pDstSeg->cSections = (uint32_t)(pSectExtra - pDstSeg->paSections); \
1184 pDstSeg++; \
1185 } while (0)
1186
1187
1188 /* Shared with the 64-bit variant. */
1189#define ADD_SEGMENT_AND_ITS_SECTIONS(a_cBits) \
1190 do { \
1191 bool fAddSegOuter = false; \
1192 \
1193 /* \
1194 * Check that the segment name is unique. We couldn't do that \
1195 * in the preparsing stage. \
1196 */ \
1197 if (pThis->uEffFileType != MH_OBJECT) \
1198 for (pSegItr = &pThis->aSegments[0]; pSegItr != pDstSeg; pSegItr++) \
1199 if (!strncmp(pSegItr->SegInfo.pszName, pSrcSeg->segname, sizeof(pSrcSeg->segname))) \
1200 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_DUPLICATE_SEGMENT_NAME); \
1201 \
1202 /* \
1203 * Create a new segment, unless we're supposed to skip this one. \
1204 */ \
1205 if ( pThis->uEffFileType != MH_OBJECT \
1206 && (cSectionsLeft == 0 || !(pFirstSect->flags & S_ATTR_DEBUG)) \
1207 && strcmp(pSrcSeg->segname, "__DWARF") \
1208 && strcmp(pSrcSeg->segname, "__CTF") ) \
1209 { \
1210 NEW_SEGMENT(a_cBits, pSrcSeg->segname, false /*a_fObjFile*/, "" /*a_achName2*/, \
1211 pSrcSeg->vmaddr, pSrcSeg->vmsize, \
1212 pSrcSeg->filesize != 0, pSrcSeg->fileoff, pSrcSeg->filesize); \
1213 fAddSegOuter = true; \
1214 } \
1215 \
1216 /* \
1217 * Convert and parse the sections. \
1218 */ \
1219 while (cSectionsLeft-- > 0) \
1220 { \
1221 /* New segment if object file. */ \
1222 bool fAddSegInner = false; \
1223 if ( pThis->uEffFileType == MH_OBJECT \
1224 && !(pSect->flags & S_ATTR_DEBUG) \
1225 && strcmp(pSrcSeg->segname, "__DWARF") \
1226 && strcmp(pSrcSeg->segname, "__CTF") ) \
1227 { \
1228 Assert(!fAddSegOuter); \
1229 NEW_SEGMENT(a_cBits, pSect->segname, true /*a_fObjFile*/, pSect->sectname, \
1230 pSect->addr, pSect->size, \
1231 pSect->offset != 0, pSect->offset, pSect->size); \
1232 fAddSegInner = true; \
1233 } \
1234 \
1235 /* Section data extract. */ \
1236 pSectExtra->cb = pSect->size; \
1237 pSectExtra->RVA = pSect->addr - pDstSeg->SegInfo.LinkAddress; \
1238 pSectExtra->LinkAddress = pSect->addr; \
1239 if (pSect->offset) \
1240 pSectExtra->offFile = pSect->offset + pThis->offImage; \
1241 else \
1242 pSectExtra->offFile = -1; \
1243 pSectExtra->cFixups = pSect->nreloc; \
1244 pSectExtra->paFixups = NULL; \
1245 if (pSect->nreloc) \
1246 pSectExtra->offFixups = pSect->reloff + pThis->offImage; \
1247 else \
1248 pSectExtra->offFixups = -1; \
1249 pSectExtra->fFlags = pSect->flags; \
1250 pSectExtra->iSegment = (uint32_t)(pDstSeg - &pThis->aSegments[0]); \
1251 pSectExtra->pvMachoSection = pSect; \
1252 \
1253 /* Update the segment alignment, if we're not skipping it. */ \
1254 if ( (fAddSegOuter || fAddSegInner) \
1255 && pDstSeg->SegInfo.Alignment < ((RTLDRADDR)1 << pSect->align)) \
1256 pDstSeg->SegInfo.Alignment = (RTLDRADDR)1 << pSect->align; \
1257 \
1258 /* Next section, and if object file next segment. */ \
1259 pSectExtra++; \
1260 pSect++; \
1261 if (fAddSegInner) \
1262 CLOSE_SEGMENT(); \
1263 } \
1264 \
1265 /* Close the segment and advance. */ \
1266 if (fAddSegOuter) \
1267 CLOSE_SEGMENT(); \
1268 } while (0) /* ADD_SEGMENT_AND_ITS_SECTIONS */
1269
1270 ADD_SEGMENT_AND_ITS_SECTIONS(32);
1271 break;
1272 }
1273
1274 case LC_SEGMENT_64:
1275 {
1276 const segment_command_64_t *pSrcSeg = (const segment_command_64_t *)u.pLoadCmd;
1277 section_64_t *pFirstSect = (section_64_t *)(pSrcSeg + 1);
1278 section_64_t *pSect = pFirstSect;
1279 uint32_t cSectionsLeft = pSrcSeg->nsects;
1280
1281 ADD_SEGMENT_AND_ITS_SECTIONS(64);
1282 break;
1283 }
1284
1285 case LC_SYMTAB:
1286 switch (pThis->uEffFileType)
1287 {
1288 case MH_OBJECT:
1289 case MH_EXECUTE:
1290 case MH_DYLIB: /** @todo ??? */
1291 case MH_BUNDLE: /** @todo ??? */
1292 case MH_DSYM:
1293 case MH_KEXT_BUNDLE:
1294 pThis->offSymbols = u.pSymTab->symoff + pThis->offImage;
1295 pThis->cSymbols = u.pSymTab->nsyms;
1296 pThis->offStrings = u.pSymTab->stroff + pThis->offImage;
1297 pThis->cchStrings = u.pSymTab->strsize;
1298 break;
1299 }
1300 break;
1301
1302 case LC_UUID:
1303 memcpy(pThis->abImageUuid, u.pUuid->uuid, sizeof(pThis->abImageUuid));
1304 break;
1305
1306 case LC_CODE_SIGNATURE:
1307 pThis->offCodeSignature = u.pData->dataoff;
1308 pThis->cbCodeSignature = u.pData->datasize;
1309 break;
1310
1311 default:
1312 break;
1313 } /* command switch */
1314 } /* while more commands */
1315
1316 Assert(pDstSeg == &pThis->aSegments[cSegments - pThis->fMakeGot]);
1317
1318 /*
1319 * Adjust mapping addresses calculating the image size.
1320 */
1321 {
1322 bool fLoadLinkEdit = false;
1323 PRTLDRMODMACHOSECT pSectExtraItr;
1324 RTLDRADDR uNextRVA = 0;
1325 RTLDRADDR cb;
1326 uint32_t cSegmentsToAdjust = cSegments - pThis->fMakeGot;
1327 uint32_t c;
1328
1329 for (;;)
1330 {
1331 /* Check if there is __DWARF segment at the end and make sure it's left
1332 out of the RVA negotiations and image loading. */
1333 if ( cSegmentsToAdjust > 0
1334 && !strcmp(pThis->aSegments[cSegmentsToAdjust - 1].SegInfo.pszName, "__DWARF"))
1335 {
1336 cSegmentsToAdjust--;
1337 pThis->aSegments[cSegmentsToAdjust].SegInfo.RVA = NIL_RTLDRADDR;
1338 pThis->aSegments[cSegmentsToAdjust].SegInfo.cbMapped = 0;
1339 continue;
1340 }
1341
1342 /* If we're skipping the __LINKEDIT segment, check for it and adjust
1343 the number of segments we'll be messing with here. ASSUMES it's
1344 last (by now anyway). */
1345 if ( !fLoadLinkEdit
1346 && cSegmentsToAdjust > 0
1347 && !strcmp(pThis->aSegments[cSegmentsToAdjust - 1].SegInfo.pszName, "__LINKEDIT"))
1348 {
1349 cSegmentsToAdjust--;
1350 pThis->aSegments[cSegmentsToAdjust].SegInfo.RVA = NIL_RTLDRADDR;
1351 pThis->aSegments[cSegmentsToAdjust].SegInfo.cbMapped = 0;
1352 continue;
1353 }
1354 break;
1355 }
1356
1357 /* Adjust RVAs. */
1358 c = cSegmentsToAdjust;
1359 for (pDstSeg = &pThis->aSegments[0]; c-- > 0; pDstSeg++)
1360 {
1361 uNextRVA = RTLDR_ALIGN_ADDR(uNextRVA, pDstSeg->SegInfo.Alignment);
1362 cb = pDstSeg->SegInfo.RVA - uNextRVA;
1363 if (cb >= 0x00100000) /* 1MB */
1364 {
1365 pDstSeg->SegInfo.RVA = uNextRVA;
1366 //pThis->pMod->fFlags |= KLDRMOD_FLAGS_NON_CONTIGUOUS_LINK_ADDRS;
1367 }
1368 uNextRVA = pDstSeg->SegInfo.RVA + pDstSeg->SegInfo.cb;
1369 }
1370
1371 /* Calculate the cbMapping members. */
1372 c = cSegmentsToAdjust;
1373 for (pDstSeg = &pThis->aSegments[0]; c-- > 1; pDstSeg++)
1374 {
1375
1376 cb = pDstSeg[1].SegInfo.RVA - pDstSeg->SegInfo.RVA;
1377 pDstSeg->SegInfo.cbMapped = (size_t)cb == cb ? (size_t)cb : ~(size_t)0;
1378 }
1379
1380 cb = RTLDR_ALIGN_ADDR(pDstSeg->SegInfo.cb, pDstSeg->SegInfo.Alignment);
1381 pDstSeg->SegInfo.cbMapped = (size_t)cb == cb ? (size_t)cb : ~(size_t)0;
1382
1383 /* Set the image size. */
1384 pThis->cbImage = pDstSeg->SegInfo.RVA + cb;
1385
1386 /* Fixup the section RVAs (internal). */
1387 c = cSegmentsToAdjust;
1388 uNextRVA = pThis->cbImage;
1389 pDstSeg = &pThis->aSegments[0];
1390 for (pSectExtraItr = pThis->paSections; pSectExtraItr != pSectExtra; pSectExtraItr++)
1391 {
1392 if (pSectExtraItr->iSegment < c)
1393 pSectExtraItr->RVA += pDstSeg[pSectExtraItr->iSegment].SegInfo.RVA;
1394 else
1395 {
1396 pSectExtraItr->RVA = uNextRVA;
1397 uNextRVA += RTLDR_ALIGN_ADDR(pSectExtraItr->cb, 64);
1398 }
1399 }
1400 }
1401
1402 /*
1403 * Make the GOT segment if necessary.
1404 */
1405 if (pThis->fMakeGot)
1406 {
1407 uint32_t cbPtr = ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1408 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1409 ? sizeof(uint32_t)
1410 : sizeof(uint64_t);
1411 uint32_t cbGot = pThis->cSymbols * cbPtr;
1412 uint32_t cbJmpStubs;
1413
1414 pThis->GotRVA = pThis->cbImage;
1415
1416 if (pThis->cbJmpStub)
1417 {
1418 cbGot = RT_ALIGN_Z(cbGot, 64);
1419 pThis->JmpStubsRVA = pThis->GotRVA + cbGot;
1420 cbJmpStubs = pThis->cbJmpStub * pThis->cSymbols;
1421 }
1422 else
1423 {
1424 pThis->JmpStubsRVA = NIL_RTLDRADDR;
1425 cbJmpStubs = 0;
1426 }
1427
1428 pDstSeg = &pThis->aSegments[cSegments - 1];
1429 pDstSeg->SegInfo.pszName = "GOT";
1430 pDstSeg->SegInfo.cchName = 3;
1431 pDstSeg->SegInfo.SelFlat = 0;
1432 pDstSeg->SegInfo.Sel16bit = 0;
1433 pDstSeg->SegInfo.fFlags = 0;
1434 pDstSeg->SegInfo.fProt = RTMEM_PROT_READ;
1435 pDstSeg->SegInfo.cb = cbGot + cbJmpStubs;
1436 pDstSeg->SegInfo.Alignment = 64;
1437 pDstSeg->SegInfo.LinkAddress = pThis->LinkAddress + pThis->GotRVA;
1438 pDstSeg->SegInfo.offFile = -1;
1439 pDstSeg->SegInfo.cbFile = -1;
1440 pDstSeg->SegInfo.RVA = pThis->GotRVA;
1441 pDstSeg->SegInfo.cbMapped = (size_t)RTLDR_ALIGN_ADDR(cbGot + cbJmpStubs, pDstSeg->SegInfo.Alignment);
1442
1443 pDstSeg->iOrgSegNo = UINT32_MAX;
1444 pDstSeg->cSections = 0;
1445 pDstSeg->paSections = NULL;
1446
1447 pThis->cbImage += pDstSeg->SegInfo.cbMapped;
1448 }
1449
1450 return VINF_SUCCESS;
1451}
1452
1453
1454/**
1455 * @interface_method_impl{RTLDROPS,pfnClose}
1456 */
1457static DECLCALLBACK(int) rtldrMachO_Close(PRTLDRMODINTERNAL pMod)
1458{
1459 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
1460 RTLDRMODMACHO_ASSERT(!pThis->pvMapping);
1461
1462 uint32_t i = pThis->cSegments;
1463 while (i-- > 0)
1464 {
1465 uint32_t j = pThis->aSegments[i].cSections;
1466 while (j-- > 0)
1467 {
1468 RTMemFree(pThis->aSegments[i].paSections[j].paFixups);
1469 pThis->aSegments[i].paSections[j].paFixups = NULL;
1470 }
1471 }
1472
1473 RTMemFree(pThis->pbLoadCommands);
1474 pThis->pbLoadCommands = NULL;
1475 RTMemFree(pThis->pchStrings);
1476 pThis->pchStrings = NULL;
1477 RTMemFree(pThis->pvaSymbols);
1478 pThis->pvaSymbols = NULL;
1479 RTMemFree(pThis->PtrCodeSignature.pb);
1480 pThis->PtrCodeSignature.pb = NULL;
1481
1482 return VINF_SUCCESS;
1483}
1484
1485
1486/**
1487 * Gets the right base address.
1488 *
1489 * @param pThis The interpreter module instance
1490 * @param pBaseAddress The base address, IN & OUT. Optional.
1491 */
1492static void kldrModMachOAdjustBaseAddress(PRTLDRMODMACHO pThis, PRTLDRADDR pBaseAddress)
1493{
1494 /*
1495 * Adjust the base address.
1496 */
1497 if (*pBaseAddress == RTLDR_BASEADDRESS_LINK)
1498 *pBaseAddress = pThis->LinkAddress;
1499}
1500
1501
1502/**
1503 * Resolves a linker generated symbol.
1504 *
1505 * The Apple linker generates symbols indicating the start and end of sections
1506 * and segments. This function checks for these and returns the right value.
1507 *
1508 * @returns VINF_SUCCESS or VERR_SYMBOL_NOT_FOUND.
1509 * @param pThis The interpreter module instance.
1510 * @param pchSymbol The symbol.
1511 * @param cchSymbol The length of the symbol.
1512 * @param BaseAddress The base address to apply when calculating the
1513 * value.
1514 * @param puValue Where to return the symbol value.
1515 */
1516static int kldrModMachOQueryLinkerSymbol(PRTLDRMODMACHO pThis, const char *pchSymbol, size_t cchSymbol,
1517 RTLDRADDR BaseAddress, PRTLDRADDR puValue)
1518{
1519 /*
1520 * Match possible name prefixes.
1521 */
1522 static const struct
1523 {
1524 const char *pszPrefix;
1525 uint32_t cchPrefix;
1526 bool fSection;
1527 bool fStart;
1528 } s_aPrefixes[] =
1529 {
1530 { "section$start$", (uint8_t)sizeof("section$start$") - 1, true, true },
1531 { "section$end$", (uint8_t)sizeof("section$end$") - 1, true, false},
1532 { "segment$start$", (uint8_t)sizeof("segment$start$") - 1, false, true },
1533 { "segment$end$", (uint8_t)sizeof("segment$end$") - 1, false, false},
1534 };
1535 size_t cchSectName = 0;
1536 const char *pchSectName = "";
1537 size_t cchSegName = 0;
1538 const char *pchSegName = NULL;
1539 uint32_t iPrefix = RT_ELEMENTS(s_aPrefixes) - 1;
1540 uint32_t iSeg;
1541 RTLDRADDR uValue;
1542
1543 for (;;)
1544 {
1545 uint8_t const cchPrefix = s_aPrefixes[iPrefix].cchPrefix;
1546 if ( cchSymbol > cchPrefix
1547 && strncmp(pchSymbol, s_aPrefixes[iPrefix].pszPrefix, cchPrefix) == 0)
1548 {
1549 pchSegName = pchSymbol + cchPrefix;
1550 cchSegName = cchSymbol - cchPrefix;
1551 break;
1552 }
1553
1554 /* next */
1555 if (!iPrefix)
1556 return VERR_SYMBOL_NOT_FOUND;
1557 iPrefix--;
1558 }
1559
1560 /*
1561 * Split the remainder into segment and section name, if necessary.
1562 */
1563 if (s_aPrefixes[iPrefix].fSection)
1564 {
1565 pchSectName = (const char *)memchr(pchSegName, '$', cchSegName);
1566 if (!pchSectName)
1567 return VERR_SYMBOL_NOT_FOUND;
1568 cchSegName = pchSectName - pchSegName;
1569 pchSectName++;
1570 cchSectName = cchSymbol - (pchSectName - pchSymbol);
1571 }
1572
1573 /*
1574 * Locate the segment.
1575 */
1576 if (!pThis->cSegments)
1577 return VERR_SYMBOL_NOT_FOUND;
1578 for (iSeg = 0; iSeg < pThis->cSegments; iSeg++)
1579 {
1580 if ( pThis->aSegments[iSeg].SegInfo.cchName >= cchSegName
1581 && memcmp(pThis->aSegments[iSeg].SegInfo.pszName, pchSegName, cchSegName) == 0)
1582 {
1583 section_32_t const *pSect;
1584 if ( pThis->aSegments[iSeg].SegInfo.cchName == cchSegName
1585 && pThis->Hdr.filetype != MH_OBJECT /* Good enough for __DWARF segs in MH_DHSYM, I hope. */)
1586 break;
1587
1588 pSect = (section_32_t *)pThis->aSegments[iSeg].paSections[0].pvMachoSection;
1589 if ( pThis->uEffFileType == MH_OBJECT
1590 && pThis->aSegments[iSeg].SegInfo.cchName > cchSegName + 1
1591 && pThis->aSegments[iSeg].SegInfo.pszName[cchSegName] == '.'
1592 && strncmp(&pThis->aSegments[iSeg].SegInfo.pszName[cchSegName + 1], pSect->sectname, sizeof(pSect->sectname)) == 0
1593 && pThis->aSegments[iSeg].SegInfo.cchName - cchSegName - 1 <= sizeof(pSect->sectname) )
1594 break;
1595 }
1596 }
1597 if (iSeg >= pThis->cSegments)
1598 return VERR_SYMBOL_NOT_FOUND;
1599
1600 if (!s_aPrefixes[iPrefix].fSection)
1601 {
1602 /*
1603 * Calculate the segment start/end address.
1604 */
1605 uValue = pThis->aSegments[iSeg].SegInfo.RVA;
1606 if (!s_aPrefixes[iPrefix].fStart)
1607 uValue += pThis->aSegments[iSeg].SegInfo.cb;
1608 }
1609 else
1610 {
1611 /*
1612 * Locate the section.
1613 */
1614 uint32_t iSect = pThis->aSegments[iSeg].cSections;
1615 if (!iSect)
1616 return VERR_SYMBOL_NOT_FOUND;
1617 for (;;)
1618 {
1619 section_32_t *pSect = (section_32_t *)pThis->aSegments[iSeg].paSections[iSect].pvMachoSection;
1620 if ( cchSectName <= sizeof(pSect->sectname)
1621 && memcmp(pSect->sectname, pchSectName, cchSectName) == 0
1622 && ( cchSectName == sizeof(pSect->sectname)
1623 || pSect->sectname[cchSectName] == '\0') )
1624 break;
1625 /* next */
1626 if (!iSect)
1627 return VERR_SYMBOL_NOT_FOUND;
1628 iSect--;
1629 }
1630
1631 uValue = pThis->aSegments[iSeg].paSections[iSect].RVA;
1632 if (!s_aPrefixes[iPrefix].fStart)
1633 uValue += pThis->aSegments[iSeg].paSections[iSect].cb;
1634 }
1635
1636 /*
1637 * Convert from RVA to load address.
1638 */
1639 uValue += BaseAddress;
1640 if (puValue)
1641 *puValue = uValue;
1642
1643 return VINF_SUCCESS;
1644}
1645
1646
1647/**
1648 * @interface_method_impl{RTLDROPS,pfnGetSymbolEx}
1649 */
1650static DECLCALLBACK(int) rtldrMachO_GetSymbolEx(PRTLDRMODINTERNAL pMod, const void *pvBits, RTUINTPTR BaseAddress,
1651 uint32_t iOrdinal, const char *pszSymbol, RTUINTPTR *pValue)
1652{
1653 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
1654 RT_NOREF(pvBits);
1655 //RT_NOREF(pszVersion);
1656 //RT_NOREF(pfnGetForwarder);
1657 //RT_NOREF(pvUser);
1658 uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
1659 uint32_t *pfKind = &fKind;
1660 size_t cchSymbol = pszSymbol ? strlen(pszSymbol) : 0;
1661
1662 /*
1663 * Resolve defaults.
1664 */
1665 kldrModMachOAdjustBaseAddress(pThis, &BaseAddress);
1666
1667 /*
1668 * Refuse segmented requests for now.
1669 */
1670 RTLDRMODMACHO_CHECK_RETURN( !pfKind
1671 || (*pfKind & RTLDRSYMKIND_REQ_TYPE_MASK) == RTLDRSYMKIND_REQ_FLAT,
1672 VERR_LDRMACHO_TODO);
1673
1674 /*
1675 * Take action according to file type.
1676 */
1677 int rc;
1678 if ( pThis->Hdr.filetype == MH_OBJECT
1679 || pThis->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
1680 || pThis->Hdr.filetype == MH_DYLIB
1681 || pThis->Hdr.filetype == MH_BUNDLE
1682 || pThis->Hdr.filetype == MH_DSYM
1683 || pThis->Hdr.filetype == MH_KEXT_BUNDLE)
1684 {
1685 rc = kldrModMachOLoadObjSymTab(pThis);
1686 if (RT_SUCCESS(rc))
1687 {
1688 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1689 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1690 rc = kldrModMachODoQuerySymbol32Bit(pThis, (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols,
1691 pThis->pchStrings, pThis->cchStrings, BaseAddress, iOrdinal, pszSymbol,
1692 (uint32_t)cchSymbol, pValue, pfKind);
1693 else
1694 rc = kldrModMachODoQuerySymbol64Bit(pThis, (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols,
1695 pThis->pchStrings, pThis->cchStrings, BaseAddress, iOrdinal, pszSymbol,
1696 (uint32_t)cchSymbol, pValue, pfKind);
1697 }
1698
1699 /*
1700 * Check for link-editor generated symbols and supply what we can.
1701 *
1702 * As small service to clients that insists on adding a '_' prefix
1703 * before querying symbols, we will ignore the prefix.
1704 */
1705 if ( rc == VERR_SYMBOL_NOT_FOUND
1706 && cchSymbol > sizeof("section$end$") - 1
1707 && ( pszSymbol[0] == 's'
1708 || (pszSymbol[1] == 's' && pszSymbol[0] == '_') )
1709 && memchr(pszSymbol, '$', cchSymbol) )
1710 {
1711 if (pszSymbol[0] == '_')
1712 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol + 1, cchSymbol - 1, BaseAddress, pValue);
1713 else
1714 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, pValue);
1715 }
1716 }
1717 else
1718 rc = VERR_LDRMACHO_TODO;
1719
1720 return rc;
1721}
1722
1723
1724/**
1725 * Lookup a symbol in a 32-bit symbol table.
1726 *
1727 * @returns IPRT status code.
1728 * @param pThis
1729 * @param paSyms Pointer to the symbol table.
1730 * @param cSyms Number of symbols in the table.
1731 * @param pchStrings Pointer to the string table.
1732 * @param cchStrings Size of the string table.
1733 * @param BaseAddress Adjusted base address, see kLdrModQuerySymbol.
1734 * @param iSymbol See kLdrModQuerySymbol.
1735 * @param pchSymbol See kLdrModQuerySymbol.
1736 * @param cchSymbol See kLdrModQuerySymbol.
1737 * @param puValue See kLdrModQuerySymbol.
1738 * @param pfKind See kLdrModQuerySymbol.
1739 */
1740static int kldrModMachODoQuerySymbol32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
1741 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol,
1742 const char *pchSymbol, uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind)
1743{
1744 /*
1745 * Find a valid symbol matching the search criteria.
1746 */
1747 if (iSymbol == UINT32_MAX)
1748 {
1749 /* simplify validation. */
1750 if (cchStrings <= cchSymbol)
1751 return VERR_SYMBOL_NOT_FOUND;
1752 cchStrings -= cchSymbol;
1753
1754 /* external symbols are usually at the end, so search the other way. */
1755 for (iSymbol = cSyms - 1; iSymbol != UINT32_MAX; iSymbol--)
1756 {
1757 const char *psz;
1758
1759 /* Skip irrellevant and non-public symbols. */
1760 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1761 continue;
1762 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1763 continue;
1764 if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
1765 continue;
1766 if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
1767 continue;
1768
1769 /* get name */
1770 if (!paSyms[iSymbol].n_un.n_strx)
1771 continue;
1772 if ((uint32_t)paSyms[iSymbol].n_un.n_strx >= cchStrings)
1773 continue;
1774 psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
1775 if (psz[cchSymbol])
1776 continue;
1777 if (memcmp(psz, pchSymbol, cchSymbol))
1778 continue;
1779
1780 /* match! */
1781 break;
1782 }
1783 if (iSymbol == UINT32_MAX)
1784 return VERR_SYMBOL_NOT_FOUND;
1785 }
1786 else
1787 {
1788 if (iSymbol >= cSyms)
1789 return VERR_SYMBOL_NOT_FOUND;
1790 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1791 return VERR_SYMBOL_NOT_FOUND;
1792 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1793 return VERR_SYMBOL_NOT_FOUND;
1794 }
1795
1796 /*
1797 * Calc the return values.
1798 */
1799 if (pfKind)
1800 {
1801 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1802 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1803 *pfKind = RTLDRSYMKIND_32BIT | RTLDRSYMKIND_NO_TYPE;
1804 else
1805 *pfKind = RTLDRSYMKIND_64BIT | RTLDRSYMKIND_NO_TYPE;
1806 if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
1807 *pfKind |= RTLDRSYMKIND_WEAK;
1808 }
1809
1810 switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
1811 {
1812 case MACHO_N_SECT:
1813 {
1814 PRTLDRMODMACHOSECT pSect;
1815 RTLDRADDR offSect;
1816 RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSymbol].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
1817 pSect = &pThis->paSections[paSyms[iSymbol].n_sect - 1];
1818
1819 offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
1820 RTLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
1821 || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
1822 && offSect == 0U - pSect->RVA
1823 && pThis->uEffFileType != MH_OBJECT),
1824 VERR_LDRMACHO_BAD_SYMBOL);
1825 if (puValue)
1826 *puValue = BaseAddress + pSect->RVA + offSect;
1827
1828 if ( pfKind
1829 && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
1830 *pfKind = (*pfKind & ~RTLDRSYMKIND_TYPE_MASK) | RTLDRSYMKIND_CODE;
1831 break;
1832 }
1833
1834 case MACHO_N_ABS:
1835 if (puValue)
1836 *puValue = paSyms[iSymbol].n_value;
1837 /*if (pfKind)
1838 pfKind |= RTLDRSYMKIND_ABS;*/
1839 break;
1840
1841 case MACHO_N_PBUD:
1842 case MACHO_N_INDR:
1843 /** @todo implement indirect and prebound symbols. */
1844 default:
1845 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
1846 }
1847
1848 return VINF_SUCCESS;
1849}
1850
1851
1852/**
1853 * Lookup a symbol in a 64-bit symbol table.
1854 *
1855 * @returns IPRT status code.
1856 * @param pThis
1857 * @param paSyms Pointer to the symbol table.
1858 * @param cSyms Number of symbols in the table.
1859 * @param pchStrings Pointer to the string table.
1860 * @param cchStrings Size of the string table.
1861 * @param BaseAddress Adjusted base address, see kLdrModQuerySymbol.
1862 * @param iSymbol See kLdrModQuerySymbol.
1863 * @param pchSymbol See kLdrModQuerySymbol.
1864 * @param cchSymbol See kLdrModQuerySymbol.
1865 * @param puValue See kLdrModQuerySymbol.
1866 * @param pfKind See kLdrModQuerySymbol.
1867 */
1868static int kldrModMachODoQuerySymbol64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
1869 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol,
1870 const char *pchSymbol, uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind)
1871{
1872 /*
1873 * Find a valid symbol matching the search criteria.
1874 */
1875 if (iSymbol == UINT32_MAX)
1876 {
1877 /* simplify validation. */
1878 if (cchStrings <= cchSymbol)
1879 return VERR_SYMBOL_NOT_FOUND;
1880 cchStrings -= cchSymbol;
1881
1882 /* external symbols are usually at the end, so search the other way. */
1883 for (iSymbol = cSyms - 1; iSymbol != UINT32_MAX; iSymbol--)
1884 {
1885 const char *psz;
1886
1887 /* Skip irrellevant and non-public symbols. */
1888 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1889 continue;
1890 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1891 continue;
1892 if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
1893 continue;
1894 if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
1895 continue;
1896
1897 /* get name */
1898 if (!paSyms[iSymbol].n_un.n_strx)
1899 continue;
1900 if ((uint32_t)paSyms[iSymbol].n_un.n_strx >= cchStrings)
1901 continue;
1902 psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
1903 if (psz[cchSymbol])
1904 continue;
1905 if (memcmp(psz, pchSymbol, cchSymbol))
1906 continue;
1907
1908 /* match! */
1909 break;
1910 }
1911 if (iSymbol == UINT32_MAX)
1912 return VERR_SYMBOL_NOT_FOUND;
1913 }
1914 else
1915 {
1916 if (iSymbol >= cSyms)
1917 return VERR_SYMBOL_NOT_FOUND;
1918 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1919 return VERR_SYMBOL_NOT_FOUND;
1920 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1921 return VERR_SYMBOL_NOT_FOUND;
1922 }
1923
1924 /*
1925 * Calc the return values.
1926 */
1927 if (pfKind)
1928 {
1929 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1930 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1931 *pfKind = RTLDRSYMKIND_32BIT | RTLDRSYMKIND_NO_TYPE;
1932 else
1933 *pfKind = RTLDRSYMKIND_64BIT | RTLDRSYMKIND_NO_TYPE;
1934 if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
1935 *pfKind |= RTLDRSYMKIND_WEAK;
1936 }
1937
1938 switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
1939 {
1940 case MACHO_N_SECT:
1941 {
1942 PRTLDRMODMACHOSECT pSect;
1943 RTLDRADDR offSect;
1944 RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSymbol].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
1945 pSect = &pThis->paSections[paSyms[iSymbol].n_sect - 1];
1946
1947 offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
1948 RTLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
1949 || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
1950 && offSect == 0U - pSect->RVA
1951 && pThis->uEffFileType != MH_OBJECT),
1952 VERR_LDRMACHO_BAD_SYMBOL);
1953 if (puValue)
1954 *puValue = BaseAddress + pSect->RVA + offSect;
1955
1956 if ( pfKind
1957 && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
1958 *pfKind = (*pfKind & ~RTLDRSYMKIND_TYPE_MASK) | RTLDRSYMKIND_CODE;
1959 break;
1960 }
1961
1962 case MACHO_N_ABS:
1963 if (puValue)
1964 *puValue = paSyms[iSymbol].n_value;
1965 /*if (pfKind)
1966 pfKind |= RTLDRSYMKIND_ABS;*/
1967 break;
1968
1969 case MACHO_N_PBUD:
1970 case MACHO_N_INDR:
1971 /** @todo implement indirect and prebound symbols. */
1972 default:
1973 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
1974 }
1975
1976 return VINF_SUCCESS;
1977}
1978
1979
1980/**
1981 * @interface_method_impl{RTLDROPS,pfnEnumSymbols}
1982 */
1983static DECLCALLBACK(int) rtldrMachO_EnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFlags, const void *pvBits,
1984 RTUINTPTR BaseAddress, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
1985{
1986 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
1987 RT_NOREF(pvBits);
1988
1989 /*
1990 * Resolve defaults.
1991 */
1992 kldrModMachOAdjustBaseAddress(pThis, &BaseAddress);
1993
1994 /*
1995 * Take action according to file type.
1996 */
1997 int rc;
1998 if ( pThis->Hdr.filetype == MH_OBJECT
1999 || pThis->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
2000 || pThis->Hdr.filetype == MH_DYLIB
2001 || pThis->Hdr.filetype == MH_BUNDLE
2002 || pThis->Hdr.filetype == MH_DSYM
2003 || pThis->Hdr.filetype == MH_KEXT_BUNDLE)
2004 {
2005 rc = kldrModMachOLoadObjSymTab(pThis);
2006 if (RT_SUCCESS(rc))
2007 {
2008 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
2009 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
2010 rc = kldrModMachODoEnumSymbols32Bit(pThis, (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols,
2011 pThis->pchStrings, pThis->cchStrings, BaseAddress,
2012 fFlags, pfnCallback, pvUser);
2013 else
2014 rc = kldrModMachODoEnumSymbols64Bit(pThis, (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols,
2015 pThis->pchStrings, pThis->cchStrings, BaseAddress,
2016 fFlags, pfnCallback, pvUser);
2017 }
2018 }
2019 else
2020 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2021
2022 return rc;
2023}
2024
2025
2026/**
2027 * Enum a 32-bit symbol table.
2028 *
2029 * @returns IPRT status code.
2030 * @param pThis
2031 * @param paSyms Pointer to the symbol table.
2032 * @param cSyms Number of symbols in the table.
2033 * @param pchStrings Pointer to the string table.
2034 * @param cchStrings Size of the string table.
2035 * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols.
2036 * @param fFlags See kLdrModEnumSymbols.
2037 * @param pfnCallback See kLdrModEnumSymbols.
2038 * @param pvUser See kLdrModEnumSymbols.
2039 */
2040static int kldrModMachODoEnumSymbols32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
2041 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
2042 uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
2043{
2044 const uint32_t fKindBase = pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
2045 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
2046 ? RTLDRSYMKIND_32BIT : RTLDRSYMKIND_64BIT;
2047 uint32_t iSym;
2048 int rc;
2049
2050 /*
2051 * Iterate the symbol table.
2052 */
2053 for (iSym = 0; iSym < cSyms; iSym++)
2054 {
2055 uint32_t fKind;
2056 RTLDRADDR uValue;
2057 const char *psz;
2058 size_t cch;
2059
2060 /* Skip debug symbols and undefined symbols. */
2061 if (paSyms[iSym].n_type & MACHO_N_STAB)
2062 continue;
2063 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2064 continue;
2065
2066 /* Skip non-public symbols unless they are requested explicitly. */
2067 if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_ALL))
2068 {
2069 if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
2070 continue;
2071 if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
2072 continue;
2073 if (!paSyms[iSym].n_un.n_strx)
2074 continue;
2075 }
2076
2077 /*
2078 * Gather symbol info
2079 */
2080
2081 /* name */
2082 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2083 psz = &pchStrings[paSyms[iSym].n_un.n_strx];
2084 cch = strlen(psz);
2085 if (!cch)
2086 psz = NULL;
2087
2088 /* kind & value */
2089 fKind = fKindBase;
2090 if (paSyms[iSym].n_desc & N_WEAK_DEF)
2091 fKind |= RTLDRSYMKIND_WEAK;
2092 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
2093 {
2094 case MACHO_N_SECT:
2095 {
2096 PRTLDRMODMACHOSECT pSect;
2097 RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSym].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2098 pSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
2099
2100 uValue = paSyms[iSym].n_value - pSect->LinkAddress;
2101 RTLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
2102 || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
2103 && uValue == 0U - pSect->RVA
2104 && pThis->uEffFileType != MH_OBJECT),
2105 VERR_LDRMACHO_BAD_SYMBOL);
2106 uValue += BaseAddress + pSect->RVA;
2107
2108 if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
2109 fKind |= RTLDRSYMKIND_CODE;
2110 else
2111 fKind |= RTLDRSYMKIND_NO_TYPE;
2112 break;
2113 }
2114
2115 case MACHO_N_ABS:
2116 uValue = paSyms[iSym].n_value;
2117 fKind |= RTLDRSYMKIND_NO_TYPE /*RTLDRSYMKIND_ABS*/;
2118 break;
2119
2120 case MACHO_N_PBUD:
2121 case MACHO_N_INDR:
2122 /** @todo implement indirect and prebound symbols. */
2123 default:
2124 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2125 }
2126
2127 /*
2128 * Do callback.
2129 */
2130 rc = pfnCallback(&pThis->Core, psz, iSym, uValue/*, fKind*/, pvUser);
2131 if (rc != VINF_SUCCESS)
2132 return rc;
2133 }
2134 return VINF_SUCCESS;
2135}
2136
2137
2138/**
2139 * Enum a 64-bit symbol table.
2140 *
2141 * @returns IPRT status code.
2142 * @param pThis
2143 * @param paSyms Pointer to the symbol table.
2144 * @param cSyms Number of symbols in the table.
2145 * @param pchStrings Pointer to the string table.
2146 * @param cchStrings Size of the string table.
2147 * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols.
2148 * @param fFlags See kLdrModEnumSymbols.
2149 * @param pfnCallback See kLdrModEnumSymbols.
2150 * @param pvUser See kLdrModEnumSymbols.
2151 */
2152static int kldrModMachODoEnumSymbols64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
2153 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
2154 uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
2155{
2156 const uint32_t fKindBase = pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
2157 || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE
2158 ? RTLDRSYMKIND_64BIT : RTLDRSYMKIND_32BIT;
2159 uint32_t iSym;
2160 int rc;
2161
2162 /*
2163 * Iterate the symbol table.
2164 */
2165 for (iSym = 0; iSym < cSyms; iSym++)
2166 {
2167 uint32_t fKind;
2168 RTLDRADDR uValue;
2169 const char *psz;
2170 size_t cch;
2171
2172 /* Skip debug symbols and undefined symbols. */
2173 if (paSyms[iSym].n_type & MACHO_N_STAB)
2174 continue;
2175 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2176 continue;
2177
2178 /* Skip non-public symbols unless they are requested explicitly. */
2179 if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_ALL))
2180 {
2181 if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
2182 continue;
2183 if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
2184 continue;
2185 if (!paSyms[iSym].n_un.n_strx)
2186 continue;
2187 }
2188
2189 /*
2190 * Gather symbol info
2191 */
2192
2193 /* name */
2194 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2195 psz = &pchStrings[paSyms[iSym].n_un.n_strx];
2196 cch = strlen(psz);
2197 if (!cch)
2198 psz = NULL;
2199
2200 /* kind & value */
2201 fKind = fKindBase;
2202 if (paSyms[iSym].n_desc & N_WEAK_DEF)
2203 fKind |= RTLDRSYMKIND_WEAK;
2204 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
2205 {
2206 case MACHO_N_SECT:
2207 {
2208 PRTLDRMODMACHOSECT pSect;
2209 RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSym].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2210 pSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
2211
2212 uValue = paSyms[iSym].n_value - pSect->LinkAddress;
2213 RTLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
2214 || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
2215 && uValue == 0U - pSect->RVA
2216 && pThis->uEffFileType != MH_OBJECT),
2217 VERR_LDRMACHO_BAD_SYMBOL);
2218 uValue += BaseAddress + pSect->RVA;
2219
2220 if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
2221 fKind |= RTLDRSYMKIND_CODE;
2222 else
2223 fKind |= RTLDRSYMKIND_NO_TYPE;
2224 break;
2225 }
2226
2227 case MACHO_N_ABS:
2228 uValue = paSyms[iSym].n_value;
2229 fKind |= RTLDRSYMKIND_NO_TYPE /*RTLDRSYMKIND_ABS*/;
2230 break;
2231
2232 case MACHO_N_PBUD:
2233 case MACHO_N_INDR:
2234 /** @todo implement indirect and prebound symbols. */
2235 default:
2236 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2237 }
2238
2239 /*
2240 * Do callback.
2241 */
2242 rc = pfnCallback(&pThis->Core, psz, iSym, uValue/*, fKind*/, pvUser);
2243 if (rc != VINF_SUCCESS)
2244 return rc;
2245 }
2246 return VINF_SUCCESS;
2247}
2248
2249#if 0
2250
2251/** @copydoc kLdrModGetImport */
2252static int kldrModMachOGetImport(PRTLDRMODINTERNAL pMod, const void *pvBits, uint32_t iImport, char *pszName, size_t cchName)
2253{
2254 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2255 RT_NOREF(pvBits);
2256 RT_NOREF(iImport);
2257 RT_NOREF(pszName);
2258 RT_NOREF(cchName);
2259
2260 if (pThis->Hdr.filetype == MH_OBJECT)
2261 return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
2262
2263 /* later */
2264 return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
2265}
2266
2267
2268
2269/** @copydoc kLdrModNumberOfImports */
2270static int32_t kldrModMachONumberOfImports(PRTLDRMODINTERNAL pMod, const void *pvBits)
2271{
2272 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2273 RT_NOREF(pvBits);
2274
2275 if (pThis->Hdr.filetype == MH_OBJECT)
2276 return VINF_SUCCESS;
2277
2278 /* later */
2279 return VINF_SUCCESS;
2280}
2281
2282
2283/** @copydoc kLdrModGetStackInfo */
2284static int kldrModMachOGetStackInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
2285{
2286 /*PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);*/
2287 RT_NOREF(pMod);
2288 RT_NOREF(pvBits);
2289 RT_NOREF(BaseAddress);
2290
2291 pStackInfo->Address = NIL_RTLDRADDR;
2292 pStackInfo->LinkAddress = NIL_RTLDRADDR;
2293 pStackInfo->cbStack = pStackInfo->cbStackThread = 0;
2294 /* later */
2295
2296 return VINF_SUCCESS;
2297}
2298
2299
2300/** @copydoc kLdrModQueryMainEntrypoint */
2301static int kldrModMachOQueryMainEntrypoint(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PRTLDRADDR pMainEPAddress)
2302{
2303#if 0
2304 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2305 int rc;
2306
2307 /*
2308 * Resolve base address alias if any.
2309 */
2310 rc = kldrModMachOBitsAndBaseAddress(pThis, NULL, &BaseAddress);
2311 if (RT_FAILURE(rc))
2312 return rc;
2313
2314 /*
2315 * Convert the address from the header.
2316 */
2317 *pMainEPAddress = pThis->Hdrs.OptionalHeader.AddressOfEntryPoint
2318 ? BaseAddress + pThis->Hdrs.OptionalHeader.AddressOfEntryPoint
2319 : NIL_RTLDRADDR;
2320#else
2321 *pMainEPAddress = NIL_RTLDRADDR;
2322 RT_NOREF(pvBits);
2323 RT_NOREF(BaseAddress);
2324 RT_NOREF(pMod);
2325#endif
2326 return VINF_SUCCESS;
2327}
2328
2329#endif
2330
2331
2332/**
2333 * @interface_method_impl{RTLDROPS,pfnEnumDbgInfo}
2334 */
2335static DECLCALLBACK(int) rtldrMachO_EnumDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, PFNRTLDRENUMDBG pfnCallback, void *pvUser)
2336{
2337 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2338 int rc = VINF_SUCCESS;
2339 uint32_t iSect;
2340 RT_NOREF(pvBits);
2341
2342 for (iSect = 0; iSect < pThis->cSections; iSect++)
2343 {
2344 /* (32-bit & 64-bit starts the same way) */
2345 section_32_t *pMachOSect = (section_32_t *)pThis->paSections[iSect].pvMachoSection;
2346 char szTmp[sizeof(pMachOSect->sectname) + 1];
2347
2348 if (strcmp(pMachOSect->segname, "__DWARF"))
2349 continue;
2350
2351 memcpy(szTmp, pMachOSect->sectname, sizeof(pMachOSect->sectname));
2352 szTmp[sizeof(pMachOSect->sectname)] = '\0';
2353
2354 RTLDRDBGINFO DbgInfo;
2355 DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF;
2356 DbgInfo.iDbgInfo = iSect;
2357 DbgInfo.LinkAddress = pThis->paSections[iSect].LinkAddress;
2358 DbgInfo.cb = pThis->paSections[iSect].cb;
2359 DbgInfo.pszExtFile = NULL;
2360 DbgInfo.u.Dwarf.pszSection = szTmp;
2361 rc = pfnCallback(&pThis->Core, &DbgInfo, pvUser);
2362 if (rc != VINF_SUCCESS)
2363 break;
2364 }
2365
2366 return rc;
2367}
2368
2369#if 0
2370
2371/** @copydoc kLdrModHasDbgInfo */
2372static int kldrModMachOHasDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits)
2373{
2374 /*PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);*/
2375
2376#if 0
2377 /*
2378 * Base this entirely on the presence of a debug directory.
2379 */
2380 if ( pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size
2381 < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */
2382 || !pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)
2383 return KLDR_ERR_NO_DEBUG_INFO;
2384 return VINF_SUCCESS;
2385#else
2386 RT_NOREF(pMod);
2387 RT_NOREF(pvBits);
2388 return VERR_LDR_NO_DEBUG_INFO;
2389#endif
2390}
2391
2392
2393/** @copydoc kLdrModMap */
2394static int kldrModMachOMap(PRTLDRMODINTERNAL pMod)
2395{
2396 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2397 unsigned fFixed;
2398 uint32_t i;
2399 void *pvBase;
2400 int rc;
2401
2402 if (!pThis->fCanLoad)
2403 return VERR_LDRMACHO_TODO;
2404
2405 /*
2406 * Already mapped?
2407 */
2408 if (pThis->pvMapping)
2409 return KLDR_ERR_ALREADY_MAPPED;
2410
2411 /*
2412 * Map it.
2413 */
2414 /* fixed image? */
2415 fFixed = pMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2416 || pMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2417 if (!fFixed)
2418 pvBase = NULL;
2419 else
2420 {
2421 pvBase = (void *)(uintptr_t)pMod->aSegments[0].LinkAddress;
2422 if ((uintptr_t)pvBase != pMod->aSegments[0].LinkAddress)
2423 return VERR_LDR_ADDRESS_OVERFLOW;
2424 }
2425
2426 /* try do the prepare */
2427 rc = kRdrMap(pMod->pRdr, &pvBase, pMod->cSegments, pMod->aSegments, fFixed);
2428 if (RT_FAILURE(rc))
2429 return rc;
2430
2431 /*
2432 * Update the segments with their map addresses.
2433 */
2434 for (i = 0; i < pMod->cSegments; i++)
2435 {
2436 if (pMod->aSegments[i].RVA != NIL_RTLDRADDR)
2437 pMod->aSegments[i].MapAddress = (uintptr_t)pvBase + (uintptr_t)pMod->aSegments[i].RVA;
2438 }
2439 pThis->pvMapping = pvBase;
2440
2441 return VINF_SUCCESS;
2442}
2443
2444
2445/** @copydoc kLdrModUnmap */
2446static int kldrModMachOUnmap(PRTLDRMODINTERNAL pMod)
2447{
2448 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2449 uint32_t i;
2450 int rc;
2451
2452 /*
2453 * Mapped?
2454 */
2455 if (!pThis->pvMapping)
2456 return KLDR_ERR_NOT_MAPPED;
2457
2458 /*
2459 * Try unmap the image.
2460 */
2461 rc = kRdrUnmap(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
2462 if (RT_FAILURE(rc))
2463 return rc;
2464
2465 /*
2466 * Update the segments to reflect that they aren't mapped any longer.
2467 */
2468 pThis->pvMapping = NULL;
2469 for (i = 0; i < pMod->cSegments; i++)
2470 pMod->aSegments[i].MapAddress = 0;
2471
2472 return VINF_SUCCESS;
2473}
2474
2475
2476/** @copydoc kLdrModAllocTLS */
2477static int kldrModMachOAllocTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
2478{
2479 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2480
2481 /*
2482 * Mapped?
2483 */
2484 if ( pvMapping == KLDRMOD_INT_MAP
2485 && !pThis->pvMapping )
2486 return KLDR_ERR_NOT_MAPPED;
2487 return VINF_SUCCESS;
2488}
2489
2490
2491/** @copydoc kLdrModFreeTLS */
2492static void kldrModMachOFreeTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
2493{
2494 RT_NOREF(pMod);
2495 RT_NOREF(pvMapping);
2496}
2497
2498
2499
2500/** @copydoc kLdrModReload */
2501static int kldrModMachOReload(PRTLDRMODINTERNAL pMod)
2502{
2503 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2504
2505 /*
2506 * Mapped?
2507 */
2508 if (!pThis->pvMapping)
2509 return KLDR_ERR_NOT_MAPPED;
2510
2511 /* the file provider does it all */
2512 return kRdrRefresh(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
2513}
2514
2515
2516/** @copydoc kLdrModFixupMapping */
2517static int kldrModMachOFixupMapping(PRTLDRMODINTERNAL pMod, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
2518{
2519 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2520 int rc, rc2;
2521
2522 /*
2523 * Mapped?
2524 */
2525 if (!pThis->pvMapping)
2526 return KLDR_ERR_NOT_MAPPED;
2527
2528 /*
2529 * Before doing anything we'll have to make all pages writable.
2530 */
2531 rc = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */);
2532 if (RT_FAILURE(rc))
2533 return rc;
2534
2535 /*
2536 * Resolve imports and apply base relocations.
2537 */
2538 rc = rtldrMachO_RelocateBits(pMod, pThis->pvMapping, (uintptr_t)pThis->pvMapping, pThis->LinkAddress,
2539 pfnGetImport, pvUser);
2540
2541 /*
2542 * Restore protection.
2543 */
2544 rc2 = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */);
2545 if (RT_SUCCESS(rc) && RT_FAILURE(rc2)
2546 rc = rc2;
2547 return rc;
2548}
2549
2550#endif
2551
2552/**
2553 * MH_OBJECT: Resolves undefined symbols (imports).
2554 *
2555 * @returns IPRT status code.
2556 * @param pThis The Mach-O module interpreter instance.
2557 * @param BaseAddress The module base address.
2558 * @param pfnGetImport The callback for resolving an imported symbol.
2559 * @param pvUser User argument to the callback.
2560 */
2561static int kldrModMachOObjDoImports(PRTLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
2562{
2563 const uint32_t cSyms = pThis->cSymbols;
2564 uint32_t iSym;
2565
2566 /*
2567 * Ensure that we've got the symbol table and section fixups handy.
2568 */
2569 int rc = kldrModMachOLoadObjSymTab(pThis);
2570 if (RT_FAILURE(rc))
2571 return rc;
2572
2573 /*
2574 * Iterate the symbol table and resolve undefined symbols.
2575 * We currently ignore REFERENCE_TYPE.
2576 */
2577 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
2578 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
2579 {
2580 macho_nlist_32_t *paSyms = (macho_nlist_32_t *)pThis->pvaSymbols;
2581 for (iSym = 0; iSym < cSyms; iSym++)
2582 {
2583 /* skip stabs */
2584 if (paSyms[iSym].n_type & MACHO_N_STAB)
2585 continue;
2586
2587 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2588 {
2589 const char *pszSymbol;
2590 size_t cchSymbol;
2591 //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
2592 RTLDRADDR Value = NIL_RTLDRADDR;
2593
2594 /** @todo Implement N_REF_TO_WEAK. */
2595 RTLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
2596
2597 /* Get the symbol name. */
2598 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2599 pszSymbol = &pThis->pchStrings[paSyms[iSym].n_un.n_strx];
2600 cchSymbol = strlen(pszSymbol);
2601
2602 /* Check for linker defined symbols relating to sections and segments. */
2603 if ( cchSymbol > sizeof("section$end$") - 1
2604 && *pszSymbol == 's'
2605 && memchr(pszSymbol, '$', cchSymbol))
2606 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
2607 else
2608 rc = VERR_SYMBOL_NOT_FOUND;
2609
2610 /* Ask the user for an address to the symbol. */
2611 if (RT_FAILURE_NP(rc))
2612 rc = pfnGetImport(&pThis->Core, NULL /*pszModule*/, pszSymbol, iSym, &Value/*, &fKind*/, pvUser);
2613 if (RT_FAILURE(rc))
2614 {
2615 /* weak reference? */
2616 if (!(paSyms[iSym].n_desc & N_WEAK_REF))
2617 break;
2618 Value = 0;
2619 }
2620
2621 /* Update the symbol. */
2622 paSyms[iSym].n_value = (uint32_t)Value;
2623 if (paSyms[iSym].n_value != Value)
2624 {
2625 rc = VERR_LDR_ADDRESS_OVERFLOW;
2626 break;
2627 }
2628 }
2629 else if (paSyms[iSym].n_desc & N_WEAK_DEF)
2630 {
2631 /** @todo implement weak symbols. */
2632 /*return VERR_LDRMACHO_TODO; - ignored for now. */
2633 }
2634 }
2635 }
2636 else
2637 {
2638 /* (Identical to the 32-bit code, just different paSym type. (and n_strx is unsigned)) */
2639 macho_nlist_64_t *paSyms = (macho_nlist_64_t *)pThis->pvaSymbols;
2640 for (iSym = 0; iSym < cSyms; iSym++)
2641 {
2642 /* skip stabs */
2643 if (paSyms[iSym].n_type & MACHO_N_STAB)
2644 continue;
2645
2646 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2647 {
2648 const char *pszSymbol;
2649 size_t cchSymbol;
2650 //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
2651 RTLDRADDR Value = NIL_RTLDRADDR;
2652
2653 /** @todo Implement N_REF_TO_WEAK. */
2654 RTLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
2655
2656 /* Get the symbol name. */
2657 RTLDRMODMACHO_CHECK_RETURN(paSyms[iSym].n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2658 pszSymbol = &pThis->pchStrings[paSyms[iSym].n_un.n_strx];
2659 cchSymbol = strlen(pszSymbol);
2660
2661 /* Check for linker defined symbols relating to sections and segments. */
2662 if ( cchSymbol > sizeof("section$end$") - 1
2663 && *pszSymbol == 's'
2664 && memchr(pszSymbol, '$', cchSymbol))
2665 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
2666 else
2667 rc = VERR_SYMBOL_NOT_FOUND;
2668
2669 /* Ask the user for an address to the symbol. */
2670 if (RT_FAILURE_NP(rc))
2671 rc = pfnGetImport(&pThis->Core, NULL, pszSymbol, iSym, &Value, /*&fKind,*/ pvUser);
2672 if (RT_FAILURE(rc))
2673 {
2674 /* weak reference? */
2675 if (!(paSyms[iSym].n_desc & N_WEAK_REF))
2676 break;
2677 Value = 0;
2678 }
2679
2680 /* Update the symbol. */
2681 paSyms[iSym].n_value = Value;
2682 if (paSyms[iSym].n_value != Value)
2683 {
2684 rc = VERR_LDR_ADDRESS_OVERFLOW;
2685 break;
2686 }
2687 }
2688 else if (paSyms[iSym].n_desc & N_WEAK_DEF)
2689 {
2690 /** @todo implement weak symbols. */
2691 /*return VERR_LDRMACHO_TODO; - ignored for now. */
2692 }
2693 }
2694 }
2695
2696 return rc;
2697}
2698
2699
2700/**
2701 * MH_OBJECT: Applies base relocations to a (unprotected) image mapping.
2702 *
2703 * @returns IPRT status code.
2704 * @param pThis The Mach-O module interpreter instance.
2705 * @param pvMapping The mapping to fixup.
2706 * @param NewBaseAddress The address to fixup the mapping to.
2707 */
2708static int kldrModMachOObjDoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress)
2709{
2710 /*
2711 * Ensure that we've got the symbol table and section fixups handy.
2712 */
2713 int rc = kldrModMachOLoadObjSymTab(pThis);
2714 if (RT_FAILURE(rc))
2715 return rc;
2716
2717 /*
2718 * Iterate over the segments and their sections and apply fixups.
2719 */
2720 rc = VINF_SUCCESS;
2721 for (uint32_t iSeg = 0; RT_SUCCESS(rc) && iSeg < pThis->cSegments; iSeg++)
2722 {
2723 PRTLDRMODMACHOSEG pSeg = &pThis->aSegments[iSeg];
2724 for (uint32_t iSect = 0; iSect < pSeg->cSections; iSect++)
2725 {
2726 PRTLDRMODMACHOSECT pSect = &pSeg->paSections[iSect];
2727
2728 /* skip sections without fixups. */
2729 if (!pSect->cFixups)
2730 continue;
2731
2732 /* lazy load (and endian convert) the fixups. */
2733 if (!pSect->paFixups)
2734 {
2735 rc = kldrModMachOLoadFixups(pThis, pSect->offFixups, pSect->cFixups, &pSect->paFixups);
2736 if (RT_FAILURE(rc))
2737 break;
2738 }
2739
2740 /*
2741 * Apply the fixups.
2742 */
2743 uint8_t *pbSectBits = (uint8_t *)pvMapping + (uintptr_t)pSect->RVA;
2744 if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE) /** @todo this aint right. */
2745 rc = kldrModMachOFixupSectionGeneric32Bit(pThis, pbSectBits, pSect,
2746 (macho_nlist_32_t *)pThis->pvaSymbols,
2747 pThis->cSymbols, NewBaseAddress);
2748 else if ( pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
2749 && pThis->Hdr.cputype == CPU_TYPE_X86_64)
2750 rc = kldrModMachOFixupSectionAMD64(pThis, pbSectBits, pSect,
2751 (macho_nlist_64_t *)pThis->pvaSymbols,
2752 pThis->cSymbols, NewBaseAddress);
2753 else
2754 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2755 if (RT_FAILURE(rc))
2756 break;
2757 }
2758 }
2759
2760 return rc;
2761}
2762
2763
2764/**
2765 * Applies generic fixups to a section in an image of the same endian-ness
2766 * as the host CPU.
2767 *
2768 * @returns IPRT status code.
2769 * @param pThis The Mach-O module interpreter instance.
2770 * @param pbSectBits Pointer to the section bits.
2771 * @param pFixupSect The section being fixed up.
2772 * @param paSyms Pointer to the symbol table.
2773 * @param cSyms Number of symbols.
2774 * @param NewBaseAddress The new base image address.
2775 */
2776static int kldrModMachOFixupSectionGeneric32Bit(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, PRTLDRMODMACHOSECT pFixupSect,
2777 macho_nlist_32_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
2778{
2779 const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
2780 const uint32_t cFixups = pFixupSect->cFixups;
2781 size_t cbSectBits = (size_t)pFixupSect->cb;
2782 const uint8_t *pbSectVirginBits;
2783 uint32_t iFixup;
2784 RTLDRADDR SymAddr = ~(RTLDRADDR)0;
2785 int rc;
2786
2787 /*
2788 * Find the virgin bits.
2789 */
2790 if (pFixupSect->offFile != -1)
2791 {
2792 rc = kldrModMachOMapVirginBits(pThis);
2793 if (RT_FAILURE(rc))
2794 return rc;
2795 pbSectVirginBits = (const uint8_t *)pThis->pvBits + pFixupSect->offFile;
2796 }
2797 else
2798 pbSectVirginBits = NULL;
2799
2800 /*
2801 * Iterate the fixups and apply them.
2802 */
2803 for (iFixup = 0; iFixup < cFixups; iFixup++)
2804 {
2805 RTPTRUNION uFix;
2806 RTPTRUNION uFixVirgin;
2807 union
2808 {
2809 macho_relocation_info_t r;
2810 scattered_relocation_info_t s;
2811 } Fixup;
2812 Fixup.r = paFixups[iFixup];
2813
2814 if (!(Fixup.r.r_address & R_SCATTERED))
2815 {
2816 /* sanity */
2817 if ((uint32_t)Fixup.r.r_address >= cbSectBits)
2818 return VERR_LDR_BAD_FIXUP;
2819
2820 /* calc fixup addresses. */
2821 uFix.pv = pbSectBits + Fixup.r.r_address;
2822 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.r.r_address : 0;
2823
2824 /*
2825 * Calc the symbol value.
2826 */
2827 /* Calc the linked symbol address / addend. */
2828 switch (Fixup.r.r_length)
2829 {
2830 /** @todo Deal with unaligned accesses on non x86 platforms. */
2831 case 0: SymAddr = *uFixVirgin.pi8; break;
2832 case 1: SymAddr = *uFixVirgin.pi16; break;
2833 case 2: SymAddr = *uFixVirgin.pi32; break;
2834 case 3: SymAddr = *uFixVirgin.pi64; break;
2835 }
2836 if (Fixup.r.r_pcrel)
2837 SymAddr += Fixup.r.r_address + pFixupSect->LinkAddress;
2838
2839 /* Add symbol / section address. */
2840 if (Fixup.r.r_extern)
2841 {
2842 const macho_nlist_32_t *pSym;
2843 if (Fixup.r.r_symbolnum >= cSyms)
2844 return VERR_LDR_BAD_FIXUP;
2845 pSym = &paSyms[Fixup.r.r_symbolnum];
2846
2847 if (pSym->n_type & MACHO_N_STAB)
2848 return VERR_LDR_BAD_FIXUP;
2849
2850 switch (pSym->n_type & MACHO_N_TYPE)
2851 {
2852 case MACHO_N_SECT:
2853 {
2854 PRTLDRMODMACHOSECT pSymSect;
2855 RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2856 pSymSect = &pThis->paSections[pSym->n_sect - 1];
2857
2858 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
2859 break;
2860 }
2861
2862 case MACHO_N_UNDF:
2863 case MACHO_N_ABS:
2864 SymAddr += pSym->n_value;
2865 break;
2866
2867 case MACHO_N_INDR:
2868 case MACHO_N_PBUD:
2869 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2870 default:
2871 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
2872 }
2873 }
2874 else if (Fixup.r.r_symbolnum != R_ABS)
2875 {
2876 PRTLDRMODMACHOSECT pSymSect;
2877 if (Fixup.r.r_symbolnum > pThis->cSections)
2878 return VERR_LDR_BAD_FIXUP;
2879 pSymSect = &pThis->paSections[Fixup.r.r_symbolnum - 1];
2880
2881 SymAddr -= pSymSect->LinkAddress;
2882 SymAddr += pSymSect->RVA + NewBaseAddress;
2883 }
2884
2885 /* adjust for PC relative */
2886 if (Fixup.r.r_pcrel)
2887 SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
2888 }
2889 else
2890 {
2891 PRTLDRMODMACHOSECT pSymSect;
2892 uint32_t iSymSect;
2893 RTLDRADDR Value;
2894
2895 /* sanity */
2896 RTLDRMODMACHO_ASSERT(Fixup.s.r_scattered);
2897 if ((uint32_t)Fixup.s.r_address >= cbSectBits)
2898 return VERR_LDR_BAD_FIXUP;
2899
2900 /* calc fixup addresses. */
2901 uFix.pv = pbSectBits + Fixup.s.r_address;
2902 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.s.r_address : 0;
2903
2904 /*
2905 * Calc the symbol value.
2906 */
2907 /* The addend is stored in the code. */
2908 switch (Fixup.s.r_length)
2909 {
2910 case 0: SymAddr = *uFixVirgin.pi8; break;
2911 case 1: SymAddr = *uFixVirgin.pi16; break;
2912 case 2: SymAddr = *uFixVirgin.pi32; break;
2913 case 3: SymAddr = *uFixVirgin.pi64; break;
2914 }
2915 if (Fixup.s.r_pcrel)
2916 SymAddr += Fixup.s.r_address;
2917 Value = Fixup.s.r_value;
2918 SymAddr -= Value; /* (-> addend only) */
2919
2920 /* Find the section number from the r_value. */
2921 pSymSect = NULL;
2922 for (iSymSect = 0; iSymSect < pThis->cSections; iSymSect++)
2923 {
2924 RTLDRADDR off = Value - pThis->paSections[iSymSect].LinkAddress;
2925 if (off < pThis->paSections[iSymSect].cb)
2926 {
2927 pSymSect = &pThis->paSections[iSymSect];
2928 break;
2929 }
2930 else if (off == pThis->paSections[iSymSect].cb) /* edge case */
2931 pSymSect = &pThis->paSections[iSymSect];
2932 }
2933 if (!pSymSect)
2934 return VERR_LDR_BAD_FIXUP;
2935
2936 /* Calc the symbol address. */
2937 SymAddr += Value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
2938 if (Fixup.s.r_pcrel)
2939 SymAddr -= Fixup.s.r_address + pFixupSect->RVA + NewBaseAddress;
2940
2941 Fixup.r.r_length = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_length;
2942 Fixup.r.r_type = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_type;
2943 }
2944
2945 /*
2946 * Write back the fixed up value.
2947 */
2948 if (Fixup.r.r_type == GENERIC_RELOC_VANILLA)
2949 {
2950 switch (Fixup.r.r_length)
2951 {
2952 case 0: *uFix.pu8 = (uint8_t)SymAddr; break;
2953 case 1: *uFix.pu16 = (uint16_t)SymAddr; break;
2954 case 2: *uFix.pu32 = (uint32_t)SymAddr; break;
2955 case 3: *uFix.pu64 = (uint64_t)SymAddr; break;
2956 }
2957 }
2958 else if (Fixup.r.r_type <= GENERIC_RELOC_LOCAL_SECTDIFF)
2959 return VERR_LDRMACHO_UNSUPPORTED_FIXUP_TYPE;
2960 else
2961 return VERR_LDR_BAD_FIXUP;
2962 }
2963
2964 return VINF_SUCCESS;
2965}
2966
2967
2968/**
2969 * Applies AMD64 fixups to a section.
2970 *
2971 * @returns IPRT status code.
2972 * @param pThis The Mach-O module interpreter instance.
2973 * @param pbSectBits Pointer to the section bits.
2974 * @param pFixupSect The section being fixed up.
2975 * @param paSyms Pointer to the symbol table.
2976 * @param cSyms Number of symbols.
2977 * @param NewBaseAddress The new base image address.
2978 */
2979static int kldrModMachOFixupSectionAMD64(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, PRTLDRMODMACHOSECT pFixupSect,
2980 macho_nlist_64_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
2981{
2982 const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
2983 const uint32_t cFixups = pFixupSect->cFixups;
2984 size_t cbSectBits = (size_t)pFixupSect->cb;
2985 const uint8_t *pbSectVirginBits;
2986 uint32_t iFixup;
2987 RTLDRADDR SymAddr;
2988 int rc;
2989
2990 /*
2991 * Find the virgin bits.
2992 */
2993 if (pFixupSect->offFile != -1)
2994 {
2995 rc = kldrModMachOMapVirginBits(pThis);
2996 if (RT_FAILURE(rc))
2997 return rc;
2998 pbSectVirginBits = (const uint8_t *)pThis->pvBits + pFixupSect->offFile;
2999 }
3000 else
3001 pbSectVirginBits = NULL;
3002
3003 /*
3004 * Iterate the fixups and apply them.
3005 */
3006 for (iFixup = 0; iFixup < cFixups; iFixup++)
3007 {
3008 union
3009 {
3010 macho_relocation_info_t r;
3011 scattered_relocation_info_t s;
3012 } Fixup;
3013 Fixup.r = paFixups[iFixup];
3014
3015 /* AMD64 doesn't use scattered fixups. */
3016 RTLDRMODMACHO_CHECK_RETURN(!(Fixup.r.r_address & R_SCATTERED), VERR_LDR_BAD_FIXUP);
3017
3018 /* sanity */
3019 RTLDRMODMACHO_CHECK_RETURN((uint32_t)Fixup.r.r_address < cbSectBits, VERR_LDR_BAD_FIXUP);
3020
3021 /* calc fixup addresses. */
3022 RTPTRUNION uFix;
3023 uFix.pv = pbSectBits + Fixup.r.r_address;
3024 RTPTRUNION uFixVirgin;
3025 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.r.r_address : 0;
3026
3027 /*
3028 * Calc the symbol value.
3029 */
3030 /* Calc the linked symbol address / addend. */
3031 switch (Fixup.r.r_length)
3032 {
3033 /** @todo Deal with unaligned accesses on non x86 platforms. */
3034 case 2: SymAddr = *uFixVirgin.pi32; break;
3035 case 3: SymAddr = *uFixVirgin.pi64; break;
3036 default:
3037 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3038 }
3039
3040 /* Add symbol / section address. */
3041 if (Fixup.r.r_extern)
3042 {
3043 const macho_nlist_64_t *pSym;
3044
3045 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
3046 pSym = &paSyms[Fixup.r.r_symbolnum];
3047 RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_BAD_FIXUP);
3048
3049 switch (Fixup.r.r_type)
3050 {
3051 /* GOT references just needs to have their symbol verified.
3052 Later, we'll optimize GOT building here using a parallel sym->got array. */
3053 case X86_64_RELOC_GOT_LOAD:
3054 case X86_64_RELOC_GOT:
3055 switch (pSym->n_type & MACHO_N_TYPE)
3056 {
3057 case MACHO_N_SECT:
3058 case MACHO_N_UNDF:
3059 case MACHO_N_ABS:
3060 break;
3061 case MACHO_N_INDR:
3062 case MACHO_N_PBUD:
3063 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3064 default:
3065 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3066 }
3067 SymAddr = sizeof(uint64_t) * Fixup.r.r_symbolnum + pThis->GotRVA + NewBaseAddress;
3068 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_BAD_FIXUP);
3069 SymAddr -= 4;
3070 break;
3071
3072 /* Verify the r_pcrel field for signed fixups on the way into the default case. */
3073 case X86_64_RELOC_BRANCH:
3074 case X86_64_RELOC_SIGNED:
3075 case X86_64_RELOC_SIGNED_1:
3076 case X86_64_RELOC_SIGNED_2:
3077 case X86_64_RELOC_SIGNED_4:
3078 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3079 /* Falls through. */
3080 default:
3081 {
3082 /* Adjust with fixup specific addend and vierfy unsigned/r_pcrel. */
3083 switch (Fixup.r.r_type)
3084 {
3085 case X86_64_RELOC_UNSIGNED:
3086 RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3087 break;
3088 case X86_64_RELOC_BRANCH:
3089 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_BAD_FIXUP);
3090 SymAddr -= 4;
3091 break;
3092 case X86_64_RELOC_SIGNED:
3093 case X86_64_RELOC_SIGNED_1:
3094 case X86_64_RELOC_SIGNED_2:
3095 case X86_64_RELOC_SIGNED_4:
3096 SymAddr -= 4;
3097 break;
3098 default:
3099 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3100 }
3101
3102 switch (pSym->n_type & MACHO_N_TYPE)
3103 {
3104 case MACHO_N_SECT:
3105 {
3106 PRTLDRMODMACHOSECT pSymSect;
3107 RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3108 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3109 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3110 break;
3111 }
3112
3113 case MACHO_N_UNDF:
3114 /* branch to an external symbol may have to take a short detour. */
3115 if ( Fixup.r.r_type == X86_64_RELOC_BRANCH
3116 && SymAddr + Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress
3117 - pSym->n_value
3118 + UINT64_C(0x80000000)
3119 >= UINT64_C(0xffffff20))
3120 SymAddr += pThis->cbJmpStub * Fixup.r.r_symbolnum + pThis->JmpStubsRVA + NewBaseAddress;
3121 else
3122 SymAddr += pSym->n_value;
3123 break;
3124
3125 case MACHO_N_ABS:
3126 SymAddr += pSym->n_value;
3127 break;
3128
3129 case MACHO_N_INDR:
3130 case MACHO_N_PBUD:
3131 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3132 default:
3133 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3134 }
3135 break;
3136 }
3137
3138 /*
3139 * This is a weird customer, it will always be follows by an UNSIGNED fixup.
3140 */
3141 case X86_64_RELOC_SUBTRACTOR:
3142 {
3143 macho_relocation_info_t Fixup2;
3144
3145 /* Deal with the SUBTRACT symbol first, by subtracting it from SymAddr. */
3146 switch (pSym->n_type & MACHO_N_TYPE)
3147 {
3148 case MACHO_N_SECT:
3149 {
3150 PRTLDRMODMACHOSECT pSymSect;
3151 RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3152 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3153 SymAddr -= pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3154 break;
3155 }
3156
3157 case MACHO_N_UNDF:
3158 case MACHO_N_ABS:
3159 SymAddr -= pSym->n_value;
3160 break;
3161
3162 case MACHO_N_INDR:
3163 case MACHO_N_PBUD:
3164 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3165 default:
3166 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3167 }
3168
3169 /* Load the 2nd fixup, check sanity. */
3170 iFixup++;
3171 RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel && iFixup < cFixups, VERR_LDR_BAD_FIXUP);
3172 Fixup2 = paFixups[iFixup];
3173 RTLDRMODMACHO_CHECK_RETURN( Fixup2.r_address == Fixup.r.r_address
3174 && Fixup2.r_length == Fixup.r.r_length
3175 && Fixup2.r_type == X86_64_RELOC_UNSIGNED
3176 && !Fixup2.r_pcrel
3177 && Fixup2.r_symbolnum < cSyms,
3178 VERR_LDR_BAD_FIXUP);
3179
3180 if (Fixup2.r_extern)
3181 {
3182 RTLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
3183 pSym = &paSyms[Fixup2.r_symbolnum];
3184 RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_BAD_FIXUP);
3185
3186 /* Add it's value to SymAddr. */
3187 switch (pSym->n_type & MACHO_N_TYPE)
3188 {
3189 case MACHO_N_SECT:
3190 {
3191 PRTLDRMODMACHOSECT pSymSect;
3192 RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3193 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3194 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3195 break;
3196 }
3197
3198 case MACHO_N_UNDF:
3199 case MACHO_N_ABS:
3200 SymAddr += pSym->n_value;
3201 break;
3202
3203 case MACHO_N_INDR:
3204 case MACHO_N_PBUD:
3205 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3206 default:
3207 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3208 }
3209 }
3210 else if (Fixup2.r_symbolnum != R_ABS)
3211 {
3212 PRTLDRMODMACHOSECT pSymSect;
3213 RTLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
3214 pSymSect = &pThis->paSections[Fixup2.r_symbolnum - 1];
3215 SymAddr += pSymSect->RVA + NewBaseAddress;
3216 }
3217 else
3218 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3219 }
3220 break;
3221 }
3222 }
3223 else
3224 {
3225 /* verify against fixup type and make adjustments */
3226 switch (Fixup.r.r_type)
3227 {
3228 case X86_64_RELOC_UNSIGNED:
3229 RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3230 break;
3231 case X86_64_RELOC_BRANCH:
3232 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3233 SymAddr += 4; /* dunno what the assmbler/linker really is doing here... */
3234 break;
3235 case X86_64_RELOC_SIGNED:
3236 case X86_64_RELOC_SIGNED_1:
3237 case X86_64_RELOC_SIGNED_2:
3238 case X86_64_RELOC_SIGNED_4:
3239 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3240 break;
3241 /*case X86_64_RELOC_GOT_LOAD:*/
3242 /*case X86_64_RELOC_GOT: */
3243 /*case X86_64_RELOC_SUBTRACTOR: - must be r_extern=1 says as. */
3244 default:
3245 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3246 }
3247 if (Fixup.r.r_symbolnum != R_ABS)
3248 {
3249 PRTLDRMODMACHOSECT pSymSect;
3250 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
3251 pSymSect = &pThis->paSections[Fixup.r.r_symbolnum - 1];
3252
3253 SymAddr -= pSymSect->LinkAddress;
3254 SymAddr += pSymSect->RVA + NewBaseAddress;
3255 if (Fixup.r.r_pcrel)
3256 SymAddr += Fixup.r.r_address;
3257 }
3258 }
3259
3260 /* adjust for PC relative */
3261 if (Fixup.r.r_pcrel)
3262 SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
3263
3264 /*
3265 * Write back the fixed up value.
3266 */
3267 switch (Fixup.r.r_length)
3268 {
3269 case 3:
3270 *uFix.pu64 = (uint64_t)SymAddr;
3271 break;
3272 case 2:
3273 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel || Fixup.r.r_type == X86_64_RELOC_SUBTRACTOR, VERR_LDR_BAD_FIXUP);
3274 RTLDRMODMACHO_CHECK_RETURN((int32_t)SymAddr == (int64_t)SymAddr, VERR_LDR_ADDRESS_OVERFLOW);
3275 *uFix.pu32 = (uint32_t)SymAddr;
3276 break;
3277 default:
3278 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3279 }
3280 }
3281
3282 return VINF_SUCCESS;
3283}
3284
3285
3286/**
3287 * Loads the symbol table for a MH_OBJECT file.
3288 *
3289 * The symbol table is pointed to by RTLDRMODMACHO::pvaSymbols.
3290 *
3291 * @returns IPRT status code.
3292 * @param pThis The Mach-O module interpreter instance.
3293 */
3294static int kldrModMachOLoadObjSymTab(PRTLDRMODMACHO pThis)
3295{
3296 int rc = VINF_SUCCESS;
3297
3298 if ( !pThis->pvaSymbols
3299 && pThis->cSymbols)
3300 {
3301 size_t cbSyms;
3302 size_t cbSym;
3303 void *pvSyms;
3304 void *pvStrings;
3305
3306 /* sanity */
3307 RTLDRMODMACHO_CHECK_RETURN( pThis->offSymbols
3308 && (!pThis->cchStrings || pThis->offStrings),
3309 VERR_LDRMACHO_BAD_OBJECT_FILE);
3310
3311 /* allocate */
3312 cbSym = pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
3313 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
3314 ? sizeof(macho_nlist_32_t)
3315 : sizeof(macho_nlist_64_t);
3316 cbSyms = pThis->cSymbols * cbSym;
3317 RTLDRMODMACHO_CHECK_RETURN(cbSyms / cbSym == pThis->cSymbols, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
3318 rc = VERR_NO_MEMORY;
3319 pvSyms = RTMemAlloc(cbSyms);
3320 if (pvSyms)
3321 {
3322 if (pThis->cchStrings)
3323 pvStrings = RTMemAlloc(pThis->cchStrings);
3324 else
3325 pvStrings = RTMemAllocZ(4);
3326 if (pvStrings)
3327 {
3328 /* read */
3329 rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvSyms, cbSyms, pThis->offSymbols);
3330 if (RT_SUCCESS(rc) && pThis->cchStrings)
3331 rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvStrings,
3332 pThis->cchStrings, pThis->offStrings);
3333 if (RT_SUCCESS(rc))
3334 {
3335 pThis->pvaSymbols = pvSyms;
3336 pThis->pchStrings = (char *)pvStrings;
3337
3338 /* perform endian conversion? */
3339 if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
3340 {
3341 uint32_t cLeft = pThis->cSymbols;
3342 macho_nlist_32_t *pSym = (macho_nlist_32_t *)pvSyms;
3343 while (cLeft-- > 0)
3344 {
3345 pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
3346 pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
3347 pSym->n_value = RT_BSWAP_U32(pSym->n_value);
3348 pSym++;
3349 }
3350 }
3351 else if (pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
3352 {
3353 uint32_t cLeft = pThis->cSymbols;
3354 macho_nlist_64_t *pSym = (macho_nlist_64_t *)pvSyms;
3355 while (cLeft-- > 0)
3356 {
3357 pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
3358 pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
3359 pSym->n_value = RT_BSWAP_U64(pSym->n_value);
3360 pSym++;
3361 }
3362 }
3363
3364 return VINF_SUCCESS;
3365 }
3366 RTMemFree(pvStrings);
3367 }
3368 RTMemFree(pvSyms);
3369 }
3370 }
3371 else
3372 RTLDRMODMACHO_ASSERT(pThis->pchStrings || pThis->Hdr.filetype == MH_DSYM);
3373
3374 return rc;
3375}
3376
3377
3378/**
3379 * Loads the fixups at the given address and performs endian
3380 * conversion if necessary.
3381 *
3382 * @returns IPRT status code.
3383 * @param pThis The Mach-O module interpreter instance.
3384 * @param offFixups The file offset of the fixups.
3385 * @param cFixups The number of fixups to load.
3386 * @param ppaFixups Where to put the pointer to the allocated fixup array.
3387 */
3388static int kldrModMachOLoadFixups(PRTLDRMODMACHO pThis, RTFOFF offFixups, uint32_t cFixups, macho_relocation_info_t **ppaFixups)
3389{
3390 macho_relocation_info_t *paFixups;
3391 size_t cbFixups;
3392
3393 /* allocate the memory. */
3394 cbFixups = cFixups * sizeof(*paFixups);
3395 RTLDRMODMACHO_CHECK_RETURN(cbFixups / sizeof(*paFixups) == cFixups, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
3396 paFixups = (macho_relocation_info_t *)RTMemAlloc(cbFixups);
3397 if (!paFixups)
3398 return VERR_NO_MEMORY;
3399
3400 /* read the fixups. */
3401 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, paFixups, cbFixups, offFixups);
3402 if (RT_SUCCESS(rc))
3403 {
3404 *ppaFixups = paFixups;
3405
3406 /* do endian conversion if necessary. */
3407 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
3408 || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
3409 {
3410 uint32_t iFixup;
3411 for (iFixup = 0; iFixup < cFixups; iFixup++)
3412 {
3413 uint32_t *pu32 = (uint32_t *)&paFixups[iFixup];
3414 pu32[0] = RT_BSWAP_U32(pu32[0]);
3415 pu32[1] = RT_BSWAP_U32(pu32[1]);
3416 }
3417 }
3418 }
3419 else
3420 RTMemFree(paFixups);
3421 return rc;
3422}
3423
3424
3425/**
3426 * Maps the virgin file bits into memory if not already done.
3427 *
3428 * @returns IPRT status code.
3429 * @param pThis The Mach-O module interpreter instance.
3430 */
3431static int kldrModMachOMapVirginBits(PRTLDRMODMACHO pThis)
3432{
3433 int rc = VINF_SUCCESS;
3434 if (!pThis->pvBits)
3435 rc = pThis->Core.pReader->pfnMap(pThis->Core.pReader, &pThis->pvBits);
3436 return rc;
3437}
3438
3439#if 0
3440
3441/** @copydoc kLdrModCallInit */
3442static int kldrModMachOCallInit(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
3443{
3444 /* later */
3445 RT_NOREF(pMod);
3446 RT_NOREF(pvMapping);
3447 RT_NOREF(uHandle);
3448 return VINF_SUCCESS;
3449}
3450
3451
3452/** @copydoc kLdrModCallTerm */
3453static int kldrModMachOCallTerm(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
3454{
3455 /* later */
3456 RT_NOREF(pMod);
3457 RT_NOREF(pvMapping);
3458 RT_NOREF(uHandle);
3459 return VINF_SUCCESS;
3460}
3461
3462
3463/** @copydoc kLdrModCallThread */
3464static int kldrModMachOCallThread(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle, unsigned fAttachingOrDetaching)
3465{
3466 /* Relevant for Mach-O? */
3467 RT_NOREF(pMod);
3468 RT_NOREF(pvMapping);
3469 RT_NOREF(uHandle);
3470 RT_NOREF(fAttachingOrDetaching);
3471 return VINF_SUCCESS;
3472}
3473
3474#endif
3475
3476
3477/**
3478 * @interface_method_impl{RTLDROPS,pfnGetImageSize}
3479 */
3480static DECLCALLBACK(size_t) rtldrMachO_GetImageSize(PRTLDRMODINTERNAL pMod)
3481{
3482 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3483 return pThis->cbImage;
3484}
3485
3486
3487/**
3488 * @interface_method_impl{RTLDROPS,pfnGetBits}
3489 */
3490static DECLCALLBACK(int) rtldrMachO_GetBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR BaseAddress,
3491 PFNRTLDRIMPORT pfnGetImport, void *pvUser)
3492{
3493 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3494
3495 if (!pThis->fCanLoad)
3496 return VERR_LDRMACHO_TODO;
3497
3498 /*
3499 * Zero the entire buffer first to simplify things.
3500 */
3501 memset(pvBits, 0, (size_t)pThis->cbImage);
3502
3503 /*
3504 * When possible use the segment table to load the data.
3505 */
3506 for (uint32_t i = 0; i < pThis->cSegments; i++)
3507 {
3508 /* skip it? */
3509 if ( pThis->aSegments[i].SegInfo.cbFile == -1
3510 || pThis->aSegments[i].SegInfo.offFile == -1
3511 || pThis->aSegments[i].SegInfo.LinkAddress == NIL_RTLDRADDR
3512 || !pThis->aSegments[i].SegInfo.Alignment)
3513 continue;
3514 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader,
3515 (uint8_t *)pvBits + pThis->aSegments[i].SegInfo.RVA,
3516 pThis->aSegments[i].SegInfo.cbFile,
3517 pThis->aSegments[i].SegInfo.offFile);
3518 if (RT_FAILURE(rc))
3519 return rc;
3520 }
3521
3522 /*
3523 * Perform relocations.
3524 */
3525 return rtldrMachO_RelocateBits(pMod, pvBits, BaseAddress, pThis->LinkAddress, pfnGetImport, pvUser);
3526}
3527
3528
3529/**
3530 * @interface_method_impl{RTLDROPS,pfnRelocate}
3531 */
3532static DECLCALLBACK(int) rtldrMachO_RelocateBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
3533 RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
3534{
3535 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3536 int rc;
3537 RT_NOREF(OldBaseAddress);
3538
3539 /*
3540 * Call workers to do the jobs.
3541 */
3542 if (pThis->Hdr.filetype == MH_OBJECT)
3543 {
3544 rc = kldrModMachOObjDoImports(pThis, NewBaseAddress, pfnGetImport, pvUser);
3545 if (RT_SUCCESS(rc))
3546 rc = kldrModMachOObjDoFixups(pThis, pvBits, NewBaseAddress);
3547
3548 }
3549 else
3550 rc = VERR_LDRMACHO_TODO;
3551 /*{
3552 rc = kldrModMachODoFixups(pThis, pvBits, NewBaseAddress, OldBaseAddress, pfnGetImport, pvUser);
3553 if (RT_SUCCESS(rc))
3554 rc = kldrModMachODoImports(pThis, pvBits, pfnGetImport, pvUser);
3555 }*/
3556
3557 /*
3558 * Construct the global offset table if necessary, it's always the last
3559 * segment when present.
3560 */
3561 if (RT_SUCCESS(rc) && pThis->fMakeGot)
3562 rc = kldrModMachOMakeGOT(pThis, pvBits, NewBaseAddress);
3563
3564 return rc;
3565}
3566
3567
3568/**
3569 * Builds the GOT.
3570 *
3571 * Assumes the symbol table has all external symbols resolved correctly and that
3572 * the bits has been cleared up front.
3573 */
3574static int kldrModMachOMakeGOT(PRTLDRMODMACHO pThis, void *pvBits, RTLDRADDR NewBaseAddress)
3575{
3576 uint32_t iSym = pThis->cSymbols;
3577 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
3578 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
3579 {
3580 macho_nlist_32_t const *paSyms = (macho_nlist_32_t const *)pThis->pvaSymbols;
3581 uint32_t *paGOT = (uint32_t *)((uint8_t *)pvBits + pThis->GotRVA);
3582 while (iSym-- > 0)
3583 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
3584 {
3585 case MACHO_N_SECT:
3586 {
3587 PRTLDRMODMACHOSECT pSymSect;
3588 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3589 pSymSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
3590 paGOT[iSym] = (uint32_t)(paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress);
3591 break;
3592 }
3593
3594 case MACHO_N_UNDF:
3595 case MACHO_N_ABS:
3596 paGOT[iSym] = paSyms[iSym].n_value;
3597 break;
3598 }
3599 }
3600 else
3601 {
3602 macho_nlist_64_t const *paSyms = (macho_nlist_64_t const *)pThis->pvaSymbols;
3603 uint64_t *paGOT = (uint64_t *)((uint8_t *)pvBits + pThis->GotRVA);
3604 while (iSym-- > 0)
3605 {
3606 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
3607 {
3608 case MACHO_N_SECT:
3609 {
3610 PRTLDRMODMACHOSECT pSymSect;
3611 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3612 pSymSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
3613 paGOT[iSym] = paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3614 break;
3615 }
3616
3617 case MACHO_N_UNDF:
3618 case MACHO_N_ABS:
3619 paGOT[iSym] = paSyms[iSym].n_value;
3620 break;
3621 }
3622 }
3623
3624 if (pThis->JmpStubsRVA != NIL_RTLDRADDR)
3625 {
3626 iSym = pThis->cSymbols;
3627 switch (pThis->Hdr.cputype)
3628 {
3629 /*
3630 * AMD64 is simple since the GOT and the indirect jmps are parallel
3631 * arrays with entries of the same size. The relative offset will
3632 * be the the same for each entry, kind of nice. :-)
3633 */
3634 case CPU_TYPE_X86_64:
3635 {
3636 uint64_t *paJmps = (uint64_t *)((uint8_t *)pvBits + pThis->JmpStubsRVA);
3637 int32_t off;
3638 uint64_t u64Tmpl;
3639 union
3640 {
3641 uint8_t ab[8];
3642 uint64_t u64;
3643 } Tmpl;
3644
3645 /* create the template. */
3646 off = (int32_t)(pThis->GotRVA - (pThis->JmpStubsRVA + 6));
3647 Tmpl.ab[0] = 0xff; /* jmp [GOT-entry wrt RIP] */
3648 Tmpl.ab[1] = 0x25;
3649 Tmpl.ab[2] = off & 0xff;
3650 Tmpl.ab[3] = (off >> 8) & 0xff;
3651 Tmpl.ab[4] = (off >> 16) & 0xff;
3652 Tmpl.ab[5] = (off >> 24) & 0xff;
3653 Tmpl.ab[6] = 0xcc;
3654 Tmpl.ab[7] = 0xcc;
3655 u64Tmpl = Tmpl.u64;
3656
3657 /* copy the template to every jmp table entry. */
3658 while (iSym-- > 0)
3659 paJmps[iSym] = u64Tmpl;
3660 break;
3661 }
3662
3663 default:
3664 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3665 }
3666 }
3667 }
3668 return VINF_SUCCESS;
3669}
3670
3671
3672/**
3673 * @interface_method_impl{RTLDROPS,pfnEnumSegments}
3674 */
3675static DECLCALLBACK(int) rtldrMachO_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
3676{
3677 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3678 uint32_t const cSegments = pThis->cSegments;
3679 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3680 {
3681 int rc = pfnCallback(pMod, &pThis->aSegments[iSeg].SegInfo, pvUser);
3682 if (rc != VINF_SUCCESS)
3683 return rc;
3684 }
3685
3686 return VINF_SUCCESS;
3687}
3688
3689
3690/**
3691 * @interface_method_impl{RTLDROPS,pfnLinkAddressToSegOffset}
3692 */
3693static DECLCALLBACK(int) rtldrMachO_LinkAddressToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress,
3694 uint32_t *piSeg, PRTLDRADDR poffSeg)
3695{
3696 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3697 uint32_t const cSegments = pThis->cSegments;
3698 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3699 {
3700 RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
3701 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3702 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3703 {
3704 *piSeg = iSeg;
3705 *poffSeg = offSeg;
3706 return VINF_SUCCESS;
3707 }
3708 }
3709
3710 return VERR_LDR_INVALID_LINK_ADDRESS;
3711}
3712
3713
3714/**
3715 * @interface_method_impl{RTLDROPS,pfnLinkAddressToRva}
3716 */
3717static DECLCALLBACK(int) rtldrMachO_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
3718{
3719 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3720 uint32_t const cSegments = pThis->cSegments;
3721 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3722 {
3723 RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
3724 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3725 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3726 {
3727 *pRva = pThis->aSegments[iSeg].SegInfo.RVA + offSeg;
3728 return VINF_SUCCESS;
3729 }
3730 }
3731
3732 return VERR_LDR_INVALID_RVA;
3733}
3734
3735
3736/**
3737 * @interface_method_impl{RTLDROPS,pfnSegOffsetToRva}
3738 */
3739static DECLCALLBACK(int) rtldrMachO_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg, PRTLDRADDR pRva)
3740{
3741 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3742
3743 if (iSeg >= pThis->cSegments)
3744 return VERR_LDR_INVALID_SEG_OFFSET;
3745 RTLDRMODMACHOSEG const *pSegment = &pThis->aSegments[iSeg];
3746
3747 if ( offSeg > pSegment->SegInfo.cbMapped
3748 && offSeg > pSegment->SegInfo.cb
3749 && ( pSegment->SegInfo.cbFile < 0
3750 || offSeg > (uint64_t)pSegment->SegInfo.cbFile))
3751 return VERR_LDR_INVALID_SEG_OFFSET;
3752
3753 *pRva = pSegment->SegInfo.RVA + offSeg;
3754 return VINF_SUCCESS;
3755}
3756
3757
3758/**
3759 * @interface_method_impl{RTLDROPS,pfnRvaToSegOffset}
3760 */
3761static DECLCALLBACK(int) rtldrMachO_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva, uint32_t *piSeg, PRTLDRADDR poffSeg)
3762{
3763 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3764 uint32_t const cSegments = pThis->cSegments;
3765 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3766 {
3767 RTLDRADDR offSeg = Rva - pThis->aSegments[iSeg].SegInfo.RVA;
3768 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3769 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3770 {
3771 *piSeg = iSeg;
3772 *poffSeg = offSeg;
3773 return VINF_SUCCESS;
3774 }
3775 }
3776
3777 return VERR_LDR_INVALID_RVA;
3778}
3779
3780
3781/**
3782 * @interface_method_impl{RTLDROPS,pfnReadDbgInfo}
3783 */
3784static DECLCALLBACK(int) rtldrMachO_ReadDbgInfo(PRTLDRMODINTERNAL pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void *pvBuf)
3785{
3786 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3787
3788 /** @todo May have to apply fixups here. */
3789 if (iDbgInfo < pThis->cSections)
3790 return pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvBuf, cb, off);
3791 return VERR_OUT_OF_RANGE;
3792}
3793
3794
3795/**
3796 * Loads the code signing blob if necessary (RTLDRMODMACHO::PtrCodeSignature).
3797 *
3798 * @returns IPRT status code.
3799 * @param pThis The mach-o instance.
3800 */
3801static int rtldrMachO_LoadSignatureBlob(PRTLDRMODMACHO pThis)
3802{
3803 Assert(pThis->cbCodeSignature > 0);
3804 if (pThis->PtrCodeSignature.pb != NULL)
3805 return VINF_SUCCESS;
3806
3807 if ( pThis->cbCodeSignature > sizeof(RTCRAPLCSHDR)
3808 && pThis->cbCodeSignature <= _1M)
3809 {
3810 /* Allocate and read. */
3811 void *pv = RTMemAllocZ(RT_ALIGN_Z(pThis->cbCodeSignature, 16));
3812 AssertReturn(pv, VERR_NO_MEMORY);
3813 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pv, pThis->cbCodeSignature, pThis->offCodeSignature);
3814 if (RT_SUCCESS(rc))
3815 {
3816 /* Check blob signature. */
3817 PCRTCRAPLCSSUPERBLOB pSuper = (PCRTCRAPLCSSUPERBLOB)pv;
3818 if (pSuper->Hdr.uMagic == RTCRAPLCS_MAGIC_EMBEDDED_SIGNATURE)
3819 {
3820 uint32_t cbHdr = RT_BE2H_U32(pSuper->Hdr.cb);
3821 uint32_t cSlots = RT_BE2H_U32(pSuper->cSlots);
3822 if ( cbHdr <= pThis->cbCodeSignature
3823 && cbHdr > RT_UOFFSETOF(RTCRAPLCSSUPERBLOB, aSlots)
3824 && cSlots > 0
3825 && cSlots < 128
3826 && RT_UOFFSETOF_DYN(RTCRAPLCSSUPERBLOB, aSlots[cSlots]) <= cbHdr)
3827 {
3828 pThis->PtrCodeSignature.pSuper = pSuper;
3829 return VINF_SUCCESS;
3830 }
3831 rc = VERR_LDRVI_BAD_CERT_HDR_LENGTH;
3832 }
3833 else
3834 rc = VERR_LDRVI_BAD_CERT_HDR_TYPE;
3835 }
3836 RTMemFree(pv);
3837 return rc;
3838 }
3839 return VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY;
3840}
3841
3842
3843/**
3844 * Handles a RTLDRPROP_PKCS7_SIGNED_DATA query.
3845 */
3846static int rtldrMachO_QueryPkcs7SignedData(PRTLDRMODMACHO pThis, void *pvBuf, size_t cbBuf, size_t *pcbRet)
3847{
3848 int rc = rtldrMachO_LoadSignatureBlob(pThis);
3849 if (RT_SUCCESS(rc))
3850 {
3851 /*
3852 * Locate the signature slot.
3853 */
3854 uint32_t iSlot = RT_BE2H_U32(pThis->PtrCodeSignature.pSuper->cSlots);
3855 PCRTCRAPLCSBLOBSLOT pSlot = &pThis->PtrCodeSignature.pSuper->aSlots[iSlot];
3856 while (iSlot-- > 0)
3857 {
3858 pSlot--;
3859 if (pSlot->uType == RTCRAPLCS_SLOT_SIGNATURE)
3860 {
3861 /*
3862 * Validate the data offset.
3863 */
3864 uint32_t offData = RT_BE2H_U32(pSlot->offData);
3865 if ( offData < pThis->cbCodeSignature - sizeof(RTCRAPLCSHDR)
3866 || !(offData & 3) )
3867 {
3868 /*
3869 * The data is prefixed by a header with magic set to blob wrapper.
3870 * Check that the size is within the bounds of the code signing blob.
3871 */
3872 PCRTCRAPLCSHDR pHdr = (PCRTCRAPLCSHDR)&pThis->PtrCodeSignature.pb[offData];
3873 if (pHdr->uMagic == RTCRAPLCS_MAGIC_BLOBWRAPPER)
3874 {
3875 uint32_t cbData = RT_BE2H_U32(pHdr->cb);
3876 uint32_t cbMax = pThis->cbCodeSignature - offData ;
3877 if ( cbData <= cbMax
3878 && cbData > sizeof(RTCRAPLCSHDR))
3879 {
3880 /*
3881 * Copy out the requirest data.
3882 */
3883 *pcbRet = cbData;
3884 if (cbData <= cbBuf)
3885 {
3886 memcpy(pvBuf, pHdr + 1, cbData);
3887 return VINF_SUCCESS;
3888 }
3889 memcpy(pvBuf, pHdr + 1, cbBuf);
3890 return VERR_BUFFER_OVERFLOW;
3891 }
3892 }
3893 }
3894 return VERR_LDRVI_BAD_CERT_FORMAT;
3895 }
3896 }
3897 rc = VERR_NOT_FOUND;
3898 }
3899 return rc;
3900}
3901
3902
3903/** @interface_method_impl{RTLDROPS,pfnQueryProp} */
3904static DECLCALLBACK(int) rtldrMachO_QueryProp(PRTLDRMODINTERNAL pMod, RTLDRPROP enmProp, void const *pvBits,
3905 void *pvBuf, size_t cbBuf, size_t *pcbRet)
3906{
3907 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3908 int rc = VERR_NOT_FOUND;
3909 switch (enmProp)
3910 {
3911 case RTLDRPROP_UUID:
3912 Assert(cbBuf >= sizeof(pThis->abImageUuid));
3913 if (!ASMMemIsZero(pThis->abImageUuid, sizeof(pThis->abImageUuid)))
3914 {
3915 *pcbRet = sizeof(pThis->abImageUuid);
3916 memcpy(pvBuf, pThis->abImageUuid, sizeof(pThis->abImageUuid));
3917 return VINF_SUCCESS;
3918 }
3919 break;
3920
3921 case RTLDRPROP_FILE_OFF_HEADER:
3922 Assert(cbBuf == sizeof(uint32_t) || cbBuf == sizeof(uint64_t));
3923 if (cbBuf == sizeof(uint32_t))
3924 *(uint32_t *)pvBuf = pThis->offImage;
3925 else
3926 *(uint64_t *)pvBuf = pThis->offImage;
3927 return VINF_SUCCESS;
3928
3929 case RTLDRPROP_IS_SIGNED:
3930 Assert(cbBuf == sizeof(bool));
3931 Assert(*pcbRet == cbBuf);
3932 *(bool *)pvBuf = pThis->cbCodeSignature > 0;
3933 return VINF_SUCCESS;
3934
3935 case RTLDRPROP_PKCS7_SIGNED_DATA:
3936 if (pThis->cbCodeSignature > 0)
3937 return rtldrMachO_QueryPkcs7SignedData(pThis, pvBuf, cbBuf, pcbRet);
3938 break;
3939
3940
3941#if 0 /** @todo return LC_ID_DYLIB */
3942 case RTLDRPROP_INTERNAL_NAME:
3943#endif
3944
3945 default:
3946 break;
3947 }
3948 NOREF(cbBuf);
3949 RT_NOREF_PV(pvBits);
3950 return rc;
3951}
3952
3953
3954/**
3955 * Operations for a Mach-O module interpreter.
3956 */
3957static const RTLDROPS s_rtldrMachOOps=
3958{
3959 "mach-o",
3960 rtldrMachO_Close,
3961 NULL,
3962 NULL /*pfnDone*/,
3963 rtldrMachO_EnumSymbols,
3964 /* ext */
3965 rtldrMachO_GetImageSize,
3966 rtldrMachO_GetBits,
3967 rtldrMachO_RelocateBits,
3968 rtldrMachO_GetSymbolEx,
3969 NULL /*pfnQueryForwarderInfo*/,
3970 rtldrMachO_EnumDbgInfo,
3971 rtldrMachO_EnumSegments,
3972 rtldrMachO_LinkAddressToSegOffset,
3973 rtldrMachO_LinkAddressToRva,
3974 rtldrMachO_SegOffsetToRva,
3975 rtldrMachO_RvaToSegOffset,
3976 rtldrMachO_ReadDbgInfo,
3977 rtldrMachO_QueryProp,
3978 NULL /*pfnVerifySignature*/,
3979 NULL /*pfnHashImage*/,
3980 NULL /*pfnUnwindFrame*/,
3981 42
3982};
3983
3984
3985/**
3986 * Handles opening Mach-O images (non-fat).
3987 */
3988DECLHIDDEN(int) rtldrMachOOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offImage,
3989 PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
3990{
3991
3992 /*
3993 * Create the instance data and do a minimal header validation.
3994 */
3995 PRTLDRMODMACHO pThis = NULL;
3996 int rc = kldrModMachODoCreate(pReader, offImage, fFlags, &pThis, pErrInfo);
3997 if (RT_SUCCESS(rc))
3998 {
3999 /*
4000 * Match up against the requested CPU architecture.
4001 */
4002 if ( enmArch == RTLDRARCH_WHATEVER
4003 || pThis->Core.enmArch == enmArch)
4004 {
4005 pThis->Core.pOps = &s_rtldrMachOOps;
4006 pThis->Core.u32Magic = RTLDRMOD_MAGIC;
4007 *phLdrMod = &pThis->Core;
4008 return VINF_SUCCESS;
4009 }
4010 rc = VERR_LDR_ARCH_MISMATCH;
4011 }
4012 if (pThis)
4013 {
4014 RTMemFree(pThis->pbLoadCommands);
4015 RTMemFree(pThis);
4016 }
4017 return rc;
4018
4019}
4020
4021
4022/**
4023 * Handles opening FAT Mach-O image.
4024 */
4025DECLHIDDEN(int) rtldrFatOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
4026{
4027 fat_header_t FatHdr;
4028 int rc = pReader->pfnRead(pReader, &FatHdr, sizeof(FatHdr), 0);
4029 if (RT_FAILURE(rc))
4030 return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
4031
4032 if (FatHdr.magic == IMAGE_FAT_SIGNATURE)
4033 { /* likely */ }
4034 else if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
4035 FatHdr.nfat_arch = RT_BSWAP_U32(FatHdr.nfat_arch);
4036 else
4037 return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "magic=%#x", FatHdr.magic);
4038 if (FatHdr.nfat_arch < 64)
4039 return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "Bad nfat_arch value: %#x", FatHdr.nfat_arch);
4040
4041 uint32_t offEntry = sizeof(FatHdr);
4042 for (uint32_t i = 0; i < FatHdr.nfat_arch; i++, offEntry += sizeof(fat_arch_t))
4043 {
4044 fat_arch_t FatEntry;
4045 rc = pReader->pfnRead(pReader, &FatEntry, sizeof(FatEntry), offEntry);
4046 if (RT_FAILURE(rc))
4047 return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
4048 if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
4049 {
4050 FatEntry.cputype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cputype);
4051 //FatEntry.cpusubtype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cpusubtype);
4052 FatEntry.offset = RT_BSWAP_U32(FatEntry.offset);
4053 //FatEntry.size = RT_BSWAP_U32(FatEntry.size);
4054 //FatEntry.align = RT_BSWAP_U32(FatEntry.align);
4055 }
4056
4057 /*
4058 * Match enmArch.
4059 */
4060 bool fMatch = false;
4061 switch (enmArch)
4062 {
4063 case RTLDRARCH_WHATEVER:
4064 fMatch = true;
4065 break;
4066
4067 case RTLDRARCH_X86_32:
4068 fMatch = FatEntry.cputype == CPU_TYPE_X86;
4069 break;
4070
4071 case RTLDRARCH_AMD64:
4072 fMatch = FatEntry.cputype == CPU_TYPE_X86_64;
4073 break;
4074
4075 case RTLDRARCH_ARM32:
4076 case RTLDRARCH_ARM64:
4077 case RTLDRARCH_X86_16:
4078 fMatch = false;
4079 break;
4080
4081 case RTLDRARCH_INVALID:
4082 case RTLDRARCH_HOST:
4083 case RTLDRARCH_END:
4084 case RTLDRARCH_32BIT_HACK:
4085 AssertFailedReturn(VERR_INVALID_PARAMETER);
4086 }
4087 if (fMatch)
4088 return rtldrMachOOpen(pReader, fFlags, enmArch, FatEntry.offset, phLdrMod, pErrInfo);
4089 }
4090
4091 return VERR_LDR_ARCH_MISMATCH;
4092
4093}
4094
Note: See TracBrowser for help on using the repository browser.

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