VirtualBox

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

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

IPRT/ldr: Mach-O underscore kludge. bugref:9232

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 158.2 KB
Line 
1/* $Id: ldrMachO.cpp 74676 2018-10-08 12:51:07Z 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 RT_FALL_THRU(); \
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 RT_FALL_THRU(); \
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 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
1751 if (cchStrings <= cchSymbol + 1)
1752 return VERR_SYMBOL_NOT_FOUND;
1753 cchStrings -= cchSymbol + 1;
1754
1755 /* external symbols are usually at the end, so search the other way. */
1756 for (iSymbol = cSyms - 1; iSymbol != UINT32_MAX; iSymbol--)
1757 {
1758 const char *psz;
1759
1760 /* Skip irrellevant and non-public symbols. */
1761 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1762 continue;
1763 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1764 continue;
1765 if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
1766 continue;
1767 if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
1768 continue;
1769
1770 /* get name */
1771 if (!paSyms[iSymbol].n_un.n_strx)
1772 continue;
1773 if ((uint32_t)paSyms[iSymbol].n_un.n_strx >= cchStrings)
1774 continue;
1775 psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
1776 if (psz[cchSymbol + 1])
1777 continue;
1778 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
1779 if (*psz != '_' || memcmp(psz + 1, pchSymbol, cchSymbol))
1780 continue;
1781
1782 /* match! */
1783 break;
1784 }
1785 if (iSymbol == UINT32_MAX)
1786 return VERR_SYMBOL_NOT_FOUND;
1787 }
1788 else
1789 {
1790 if (iSymbol >= cSyms)
1791 return VERR_SYMBOL_NOT_FOUND;
1792 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1793 return VERR_SYMBOL_NOT_FOUND;
1794 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1795 return VERR_SYMBOL_NOT_FOUND;
1796 }
1797
1798 /*
1799 * Calc the return values.
1800 */
1801 if (pfKind)
1802 {
1803 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1804 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1805 *pfKind = RTLDRSYMKIND_32BIT | RTLDRSYMKIND_NO_TYPE;
1806 else
1807 *pfKind = RTLDRSYMKIND_64BIT | RTLDRSYMKIND_NO_TYPE;
1808 if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
1809 *pfKind |= RTLDRSYMKIND_WEAK;
1810 }
1811
1812 switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
1813 {
1814 case MACHO_N_SECT:
1815 {
1816 PRTLDRMODMACHOSECT pSect;
1817 RTLDRADDR offSect;
1818 RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSymbol].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
1819 pSect = &pThis->paSections[paSyms[iSymbol].n_sect - 1];
1820
1821 offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
1822 RTLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
1823 || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
1824 && offSect == 0U - pSect->RVA
1825 && pThis->uEffFileType != MH_OBJECT),
1826 VERR_LDRMACHO_BAD_SYMBOL);
1827 if (puValue)
1828 *puValue = BaseAddress + pSect->RVA + offSect;
1829
1830 if ( pfKind
1831 && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
1832 *pfKind = (*pfKind & ~RTLDRSYMKIND_TYPE_MASK) | RTLDRSYMKIND_CODE;
1833 break;
1834 }
1835
1836 case MACHO_N_ABS:
1837 if (puValue)
1838 *puValue = paSyms[iSymbol].n_value;
1839 /*if (pfKind)
1840 pfKind |= RTLDRSYMKIND_ABS;*/
1841 break;
1842
1843 case MACHO_N_PBUD:
1844 case MACHO_N_INDR:
1845 /** @todo implement indirect and prebound symbols. */
1846 default:
1847 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
1848 }
1849
1850 return VINF_SUCCESS;
1851}
1852
1853
1854/**
1855 * Lookup a symbol in a 64-bit symbol table.
1856 *
1857 * @returns IPRT status code.
1858 * @param pThis
1859 * @param paSyms Pointer to the symbol table.
1860 * @param cSyms Number of symbols in the table.
1861 * @param pchStrings Pointer to the string table.
1862 * @param cchStrings Size of the string table.
1863 * @param BaseAddress Adjusted base address, see kLdrModQuerySymbol.
1864 * @param iSymbol See kLdrModQuerySymbol.
1865 * @param pchSymbol See kLdrModQuerySymbol.
1866 * @param cchSymbol See kLdrModQuerySymbol.
1867 * @param puValue See kLdrModQuerySymbol.
1868 * @param pfKind See kLdrModQuerySymbol.
1869 */
1870static int kldrModMachODoQuerySymbol64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
1871 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol,
1872 const char *pchSymbol, uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind)
1873{
1874 /*
1875 * Find a valid symbol matching the search criteria.
1876 */
1877 if (iSymbol == UINT32_MAX)
1878 {
1879 /* simplify validation. */
1880 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
1881 if (cchStrings <= cchSymbol + 1)
1882 return VERR_SYMBOL_NOT_FOUND;
1883 cchStrings -= cchSymbol + 1;
1884
1885 /* external symbols are usually at the end, so search the other way. */
1886 for (iSymbol = cSyms - 1; iSymbol != UINT32_MAX; iSymbol--)
1887 {
1888 const char *psz;
1889
1890 /* Skip irrellevant and non-public symbols. */
1891 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1892 continue;
1893 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1894 continue;
1895 if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
1896 continue;
1897 if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
1898 continue;
1899
1900 /* get name */
1901 if (!paSyms[iSymbol].n_un.n_strx)
1902 continue;
1903 if ((uint32_t)paSyms[iSymbol].n_un.n_strx >= cchStrings)
1904 continue;
1905 psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
1906 if (psz[cchSymbol + 1])
1907 continue;
1908 if (*psz != '_' || memcmp(psz + 1, pchSymbol, cchSymbol))
1909 continue;
1910
1911 /* match! */
1912 break;
1913 }
1914 if (iSymbol == UINT32_MAX)
1915 return VERR_SYMBOL_NOT_FOUND;
1916 }
1917 else
1918 {
1919 if (iSymbol >= cSyms)
1920 return VERR_SYMBOL_NOT_FOUND;
1921 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1922 return VERR_SYMBOL_NOT_FOUND;
1923 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1924 return VERR_SYMBOL_NOT_FOUND;
1925 }
1926
1927 /*
1928 * Calc the return values.
1929 */
1930 if (pfKind)
1931 {
1932 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1933 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1934 *pfKind = RTLDRSYMKIND_32BIT | RTLDRSYMKIND_NO_TYPE;
1935 else
1936 *pfKind = RTLDRSYMKIND_64BIT | RTLDRSYMKIND_NO_TYPE;
1937 if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
1938 *pfKind |= RTLDRSYMKIND_WEAK;
1939 }
1940
1941 switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
1942 {
1943 case MACHO_N_SECT:
1944 {
1945 PRTLDRMODMACHOSECT pSect;
1946 RTLDRADDR offSect;
1947 RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSymbol].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
1948 pSect = &pThis->paSections[paSyms[iSymbol].n_sect - 1];
1949
1950 offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
1951 RTLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
1952 || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
1953 && offSect == 0U - pSect->RVA
1954 && pThis->uEffFileType != MH_OBJECT),
1955 VERR_LDRMACHO_BAD_SYMBOL);
1956 if (puValue)
1957 *puValue = BaseAddress + pSect->RVA + offSect;
1958
1959 if ( pfKind
1960 && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
1961 *pfKind = (*pfKind & ~RTLDRSYMKIND_TYPE_MASK) | RTLDRSYMKIND_CODE;
1962 break;
1963 }
1964
1965 case MACHO_N_ABS:
1966 if (puValue)
1967 *puValue = paSyms[iSymbol].n_value;
1968 /*if (pfKind)
1969 pfKind |= RTLDRSYMKIND_ABS;*/
1970 break;
1971
1972 case MACHO_N_PBUD:
1973 case MACHO_N_INDR:
1974 /** @todo implement indirect and prebound symbols. */
1975 default:
1976 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
1977 }
1978
1979 return VINF_SUCCESS;
1980}
1981
1982
1983/**
1984 * @interface_method_impl{RTLDROPS,pfnEnumSymbols}
1985 */
1986static DECLCALLBACK(int) rtldrMachO_EnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFlags, const void *pvBits,
1987 RTUINTPTR BaseAddress, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
1988{
1989 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
1990 RT_NOREF(pvBits);
1991
1992 /*
1993 * Resolve defaults.
1994 */
1995 kldrModMachOAdjustBaseAddress(pThis, &BaseAddress);
1996
1997 /*
1998 * Take action according to file type.
1999 */
2000 int rc;
2001 if ( pThis->Hdr.filetype == MH_OBJECT
2002 || pThis->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
2003 || pThis->Hdr.filetype == MH_DYLIB
2004 || pThis->Hdr.filetype == MH_BUNDLE
2005 || pThis->Hdr.filetype == MH_DSYM
2006 || pThis->Hdr.filetype == MH_KEXT_BUNDLE)
2007 {
2008 rc = kldrModMachOLoadObjSymTab(pThis);
2009 if (RT_SUCCESS(rc))
2010 {
2011 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
2012 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
2013 rc = kldrModMachODoEnumSymbols32Bit(pThis, (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols,
2014 pThis->pchStrings, pThis->cchStrings, BaseAddress,
2015 fFlags, pfnCallback, pvUser);
2016 else
2017 rc = kldrModMachODoEnumSymbols64Bit(pThis, (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols,
2018 pThis->pchStrings, pThis->cchStrings, BaseAddress,
2019 fFlags, pfnCallback, pvUser);
2020 }
2021 }
2022 else
2023 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2024
2025 return rc;
2026}
2027
2028
2029/**
2030 * Enum a 32-bit symbol table.
2031 *
2032 * @returns IPRT status code.
2033 * @param pThis
2034 * @param paSyms Pointer to the symbol table.
2035 * @param cSyms Number of symbols in the table.
2036 * @param pchStrings Pointer to the string table.
2037 * @param cchStrings Size of the string table.
2038 * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols.
2039 * @param fFlags See kLdrModEnumSymbols.
2040 * @param pfnCallback See kLdrModEnumSymbols.
2041 * @param pvUser See kLdrModEnumSymbols.
2042 */
2043static int kldrModMachODoEnumSymbols32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
2044 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
2045 uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
2046{
2047 const uint32_t fKindBase = pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
2048 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
2049 ? RTLDRSYMKIND_32BIT : RTLDRSYMKIND_64BIT;
2050 uint32_t iSym;
2051 int rc;
2052
2053 /*
2054 * Iterate the symbol table.
2055 */
2056 for (iSym = 0; iSym < cSyms; iSym++)
2057 {
2058 uint32_t fKind;
2059 RTLDRADDR uValue;
2060 const char *psz;
2061 size_t cch;
2062
2063 /* Skip debug symbols and undefined symbols. */
2064 if (paSyms[iSym].n_type & MACHO_N_STAB)
2065 continue;
2066 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2067 continue;
2068
2069 /* Skip non-public symbols unless they are requested explicitly. */
2070 if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_ALL))
2071 {
2072 if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
2073 continue;
2074 if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
2075 continue;
2076 if (!paSyms[iSym].n_un.n_strx)
2077 continue;
2078 }
2079
2080 /*
2081 * Gather symbol info
2082 */
2083
2084 /* name */
2085 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2086 psz = &pchStrings[paSyms[iSym].n_un.n_strx];
2087 cch = strlen(psz);
2088 if (!cch)
2089 psz = NULL;
2090
2091 /* kind & value */
2092 fKind = fKindBase;
2093 if (paSyms[iSym].n_desc & N_WEAK_DEF)
2094 fKind |= RTLDRSYMKIND_WEAK;
2095 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
2096 {
2097 case MACHO_N_SECT:
2098 {
2099 PRTLDRMODMACHOSECT pSect;
2100 RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSym].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2101 pSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
2102
2103 uValue = paSyms[iSym].n_value - pSect->LinkAddress;
2104 RTLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
2105 || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
2106 && uValue == 0U - pSect->RVA
2107 && pThis->uEffFileType != MH_OBJECT),
2108 VERR_LDRMACHO_BAD_SYMBOL);
2109 uValue += BaseAddress + pSect->RVA;
2110
2111 if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
2112 fKind |= RTLDRSYMKIND_CODE;
2113 else
2114 fKind |= RTLDRSYMKIND_NO_TYPE;
2115 break;
2116 }
2117
2118 case MACHO_N_ABS:
2119 uValue = paSyms[iSym].n_value;
2120 fKind |= RTLDRSYMKIND_NO_TYPE /*RTLDRSYMKIND_ABS*/;
2121 break;
2122
2123 case MACHO_N_PBUD:
2124 case MACHO_N_INDR:
2125 /** @todo implement indirect and prebound symbols. */
2126 default:
2127 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2128 }
2129
2130 /*
2131 * Do callback.
2132 */
2133 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
2134 if (cch > 1 && *psz == '_')
2135 psz++;
2136 rc = pfnCallback(&pThis->Core, psz, iSym, uValue/*, fKind*/, pvUser);
2137 if (rc != VINF_SUCCESS)
2138 return rc;
2139 }
2140 return VINF_SUCCESS;
2141}
2142
2143
2144/**
2145 * Enum a 64-bit symbol table.
2146 *
2147 * @returns IPRT status code.
2148 * @param pThis
2149 * @param paSyms Pointer to the symbol table.
2150 * @param cSyms Number of symbols in the table.
2151 * @param pchStrings Pointer to the string table.
2152 * @param cchStrings Size of the string table.
2153 * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols.
2154 * @param fFlags See kLdrModEnumSymbols.
2155 * @param pfnCallback See kLdrModEnumSymbols.
2156 * @param pvUser See kLdrModEnumSymbols.
2157 */
2158static int kldrModMachODoEnumSymbols64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
2159 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
2160 uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
2161{
2162 const uint32_t fKindBase = pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
2163 || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE
2164 ? RTLDRSYMKIND_64BIT : RTLDRSYMKIND_32BIT;
2165 uint32_t iSym;
2166 int rc;
2167
2168 /*
2169 * Iterate the symbol table.
2170 */
2171 for (iSym = 0; iSym < cSyms; iSym++)
2172 {
2173 uint32_t fKind;
2174 RTLDRADDR uValue;
2175 const char *psz;
2176 size_t cch;
2177
2178 /* Skip debug symbols and undefined symbols. */
2179 if (paSyms[iSym].n_type & MACHO_N_STAB)
2180 continue;
2181 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2182 continue;
2183
2184 /* Skip non-public symbols unless they are requested explicitly. */
2185 if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_ALL))
2186 {
2187 if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
2188 continue;
2189 if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
2190 continue;
2191 if (!paSyms[iSym].n_un.n_strx)
2192 continue;
2193 }
2194
2195 /*
2196 * Gather symbol info
2197 */
2198
2199 /* name */
2200 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2201 psz = &pchStrings[paSyms[iSym].n_un.n_strx];
2202 cch = strlen(psz);
2203 if (!cch)
2204 psz = NULL;
2205
2206 /* kind & value */
2207 fKind = fKindBase;
2208 if (paSyms[iSym].n_desc & N_WEAK_DEF)
2209 fKind |= RTLDRSYMKIND_WEAK;
2210 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
2211 {
2212 case MACHO_N_SECT:
2213 {
2214 PRTLDRMODMACHOSECT pSect;
2215 RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSym].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2216 pSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
2217
2218 uValue = paSyms[iSym].n_value - pSect->LinkAddress;
2219 RTLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
2220 || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
2221 && uValue == 0U - pSect->RVA
2222 && pThis->uEffFileType != MH_OBJECT),
2223 VERR_LDRMACHO_BAD_SYMBOL);
2224 uValue += BaseAddress + pSect->RVA;
2225
2226 if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
2227 fKind |= RTLDRSYMKIND_CODE;
2228 else
2229 fKind |= RTLDRSYMKIND_NO_TYPE;
2230 break;
2231 }
2232
2233 case MACHO_N_ABS:
2234 uValue = paSyms[iSym].n_value;
2235 fKind |= RTLDRSYMKIND_NO_TYPE /*RTLDRSYMKIND_ABS*/;
2236 break;
2237
2238 case MACHO_N_PBUD:
2239 case MACHO_N_INDR:
2240 /** @todo implement indirect and prebound symbols. */
2241 default:
2242 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2243 }
2244
2245 /*
2246 * Do callback.
2247 */
2248 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
2249 if (cch > 1 && *psz == '_')
2250 psz++;
2251 rc = pfnCallback(&pThis->Core, psz, iSym, uValue/*, fKind*/, pvUser);
2252 if (rc != VINF_SUCCESS)
2253 return rc;
2254 }
2255 return VINF_SUCCESS;
2256}
2257
2258#if 0
2259
2260/** @copydoc kLdrModGetImport */
2261static int kldrModMachOGetImport(PRTLDRMODINTERNAL pMod, const void *pvBits, uint32_t iImport, char *pszName, size_t cchName)
2262{
2263 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2264 RT_NOREF(pvBits);
2265 RT_NOREF(iImport);
2266 RT_NOREF(pszName);
2267 RT_NOREF(cchName);
2268
2269 if (pThis->Hdr.filetype == MH_OBJECT)
2270 return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
2271
2272 /* later */
2273 return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
2274}
2275
2276
2277
2278/** @copydoc kLdrModNumberOfImports */
2279static int32_t kldrModMachONumberOfImports(PRTLDRMODINTERNAL pMod, const void *pvBits)
2280{
2281 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2282 RT_NOREF(pvBits);
2283
2284 if (pThis->Hdr.filetype == MH_OBJECT)
2285 return VINF_SUCCESS;
2286
2287 /* later */
2288 return VINF_SUCCESS;
2289}
2290
2291
2292/** @copydoc kLdrModGetStackInfo */
2293static int kldrModMachOGetStackInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
2294{
2295 /*PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);*/
2296 RT_NOREF(pMod);
2297 RT_NOREF(pvBits);
2298 RT_NOREF(BaseAddress);
2299
2300 pStackInfo->Address = NIL_RTLDRADDR;
2301 pStackInfo->LinkAddress = NIL_RTLDRADDR;
2302 pStackInfo->cbStack = pStackInfo->cbStackThread = 0;
2303 /* later */
2304
2305 return VINF_SUCCESS;
2306}
2307
2308
2309/** @copydoc kLdrModQueryMainEntrypoint */
2310static int kldrModMachOQueryMainEntrypoint(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PRTLDRADDR pMainEPAddress)
2311{
2312#if 0
2313 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2314 int rc;
2315
2316 /*
2317 * Resolve base address alias if any.
2318 */
2319 rc = kldrModMachOBitsAndBaseAddress(pThis, NULL, &BaseAddress);
2320 if (RT_FAILURE(rc))
2321 return rc;
2322
2323 /*
2324 * Convert the address from the header.
2325 */
2326 *pMainEPAddress = pThis->Hdrs.OptionalHeader.AddressOfEntryPoint
2327 ? BaseAddress + pThis->Hdrs.OptionalHeader.AddressOfEntryPoint
2328 : NIL_RTLDRADDR;
2329#else
2330 *pMainEPAddress = NIL_RTLDRADDR;
2331 RT_NOREF(pvBits);
2332 RT_NOREF(BaseAddress);
2333 RT_NOREF(pMod);
2334#endif
2335 return VINF_SUCCESS;
2336}
2337
2338#endif
2339
2340
2341/**
2342 * @interface_method_impl{RTLDROPS,pfnEnumDbgInfo}
2343 */
2344static DECLCALLBACK(int) rtldrMachO_EnumDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, PFNRTLDRENUMDBG pfnCallback, void *pvUser)
2345{
2346 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2347 int rc = VINF_SUCCESS;
2348 uint32_t iSect;
2349 RT_NOREF(pvBits);
2350
2351 for (iSect = 0; iSect < pThis->cSections; iSect++)
2352 {
2353 /* (32-bit & 64-bit starts the same way) */
2354 section_32_t *pMachOSect = (section_32_t *)pThis->paSections[iSect].pvMachoSection;
2355 char szTmp[sizeof(pMachOSect->sectname) + 1];
2356
2357 if (strcmp(pMachOSect->segname, "__DWARF"))
2358 continue;
2359
2360 memcpy(szTmp, pMachOSect->sectname, sizeof(pMachOSect->sectname));
2361 szTmp[sizeof(pMachOSect->sectname)] = '\0';
2362
2363 RTLDRDBGINFO DbgInfo;
2364 DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF;
2365 DbgInfo.iDbgInfo = iSect;
2366 DbgInfo.LinkAddress = pThis->paSections[iSect].LinkAddress;
2367 DbgInfo.cb = pThis->paSections[iSect].cb;
2368 DbgInfo.pszExtFile = NULL;
2369 DbgInfo.u.Dwarf.pszSection = szTmp;
2370 rc = pfnCallback(&pThis->Core, &DbgInfo, pvUser);
2371 if (rc != VINF_SUCCESS)
2372 break;
2373 }
2374
2375 return rc;
2376}
2377
2378#if 0
2379
2380/** @copydoc kLdrModHasDbgInfo */
2381static int kldrModMachOHasDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits)
2382{
2383 /*PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);*/
2384
2385#if 0
2386 /*
2387 * Base this entirely on the presence of a debug directory.
2388 */
2389 if ( pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size
2390 < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */
2391 || !pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)
2392 return KLDR_ERR_NO_DEBUG_INFO;
2393 return VINF_SUCCESS;
2394#else
2395 RT_NOREF(pMod);
2396 RT_NOREF(pvBits);
2397 return VERR_LDR_NO_DEBUG_INFO;
2398#endif
2399}
2400
2401
2402/** @copydoc kLdrModMap */
2403static int kldrModMachOMap(PRTLDRMODINTERNAL pMod)
2404{
2405 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2406 unsigned fFixed;
2407 uint32_t i;
2408 void *pvBase;
2409 int rc;
2410
2411 if (!pThis->fCanLoad)
2412 return VERR_LDRMACHO_TODO;
2413
2414 /*
2415 * Already mapped?
2416 */
2417 if (pThis->pvMapping)
2418 return KLDR_ERR_ALREADY_MAPPED;
2419
2420 /*
2421 * Map it.
2422 */
2423 /* fixed image? */
2424 fFixed = pMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2425 || pMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2426 if (!fFixed)
2427 pvBase = NULL;
2428 else
2429 {
2430 pvBase = (void *)(uintptr_t)pMod->aSegments[0].LinkAddress;
2431 if ((uintptr_t)pvBase != pMod->aSegments[0].LinkAddress)
2432 return VERR_LDR_ADDRESS_OVERFLOW;
2433 }
2434
2435 /* try do the prepare */
2436 rc = kRdrMap(pMod->pRdr, &pvBase, pMod->cSegments, pMod->aSegments, fFixed);
2437 if (RT_FAILURE(rc))
2438 return rc;
2439
2440 /*
2441 * Update the segments with their map addresses.
2442 */
2443 for (i = 0; i < pMod->cSegments; i++)
2444 {
2445 if (pMod->aSegments[i].RVA != NIL_RTLDRADDR)
2446 pMod->aSegments[i].MapAddress = (uintptr_t)pvBase + (uintptr_t)pMod->aSegments[i].RVA;
2447 }
2448 pThis->pvMapping = pvBase;
2449
2450 return VINF_SUCCESS;
2451}
2452
2453
2454/** @copydoc kLdrModUnmap */
2455static int kldrModMachOUnmap(PRTLDRMODINTERNAL pMod)
2456{
2457 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2458 uint32_t i;
2459 int rc;
2460
2461 /*
2462 * Mapped?
2463 */
2464 if (!pThis->pvMapping)
2465 return KLDR_ERR_NOT_MAPPED;
2466
2467 /*
2468 * Try unmap the image.
2469 */
2470 rc = kRdrUnmap(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
2471 if (RT_FAILURE(rc))
2472 return rc;
2473
2474 /*
2475 * Update the segments to reflect that they aren't mapped any longer.
2476 */
2477 pThis->pvMapping = NULL;
2478 for (i = 0; i < pMod->cSegments; i++)
2479 pMod->aSegments[i].MapAddress = 0;
2480
2481 return VINF_SUCCESS;
2482}
2483
2484
2485/** @copydoc kLdrModAllocTLS */
2486static int kldrModMachOAllocTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
2487{
2488 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2489
2490 /*
2491 * Mapped?
2492 */
2493 if ( pvMapping == KLDRMOD_INT_MAP
2494 && !pThis->pvMapping )
2495 return KLDR_ERR_NOT_MAPPED;
2496 return VINF_SUCCESS;
2497}
2498
2499
2500/** @copydoc kLdrModFreeTLS */
2501static void kldrModMachOFreeTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
2502{
2503 RT_NOREF(pMod);
2504 RT_NOREF(pvMapping);
2505}
2506
2507
2508
2509/** @copydoc kLdrModReload */
2510static int kldrModMachOReload(PRTLDRMODINTERNAL pMod)
2511{
2512 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2513
2514 /*
2515 * Mapped?
2516 */
2517 if (!pThis->pvMapping)
2518 return KLDR_ERR_NOT_MAPPED;
2519
2520 /* the file provider does it all */
2521 return kRdrRefresh(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
2522}
2523
2524
2525/** @copydoc kLdrModFixupMapping */
2526static int kldrModMachOFixupMapping(PRTLDRMODINTERNAL pMod, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
2527{
2528 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
2529 int rc, rc2;
2530
2531 /*
2532 * Mapped?
2533 */
2534 if (!pThis->pvMapping)
2535 return KLDR_ERR_NOT_MAPPED;
2536
2537 /*
2538 * Before doing anything we'll have to make all pages writable.
2539 */
2540 rc = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */);
2541 if (RT_FAILURE(rc))
2542 return rc;
2543
2544 /*
2545 * Resolve imports and apply base relocations.
2546 */
2547 rc = rtldrMachO_RelocateBits(pMod, pThis->pvMapping, (uintptr_t)pThis->pvMapping, pThis->LinkAddress,
2548 pfnGetImport, pvUser);
2549
2550 /*
2551 * Restore protection.
2552 */
2553 rc2 = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */);
2554 if (RT_SUCCESS(rc) && RT_FAILURE(rc2)
2555 rc = rc2;
2556 return rc;
2557}
2558
2559#endif
2560
2561/**
2562 * MH_OBJECT: Resolves undefined symbols (imports).
2563 *
2564 * @returns IPRT status code.
2565 * @param pThis The Mach-O module interpreter instance.
2566 * @param BaseAddress The module base address.
2567 * @param pfnGetImport The callback for resolving an imported symbol.
2568 * @param pvUser User argument to the callback.
2569 */
2570static int kldrModMachOObjDoImports(PRTLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
2571{
2572 const uint32_t cSyms = pThis->cSymbols;
2573 uint32_t iSym;
2574
2575 /*
2576 * Ensure that we've got the symbol table and section fixups handy.
2577 */
2578 int rc = kldrModMachOLoadObjSymTab(pThis);
2579 if (RT_FAILURE(rc))
2580 return rc;
2581
2582 /*
2583 * Iterate the symbol table and resolve undefined symbols.
2584 * We currently ignore REFERENCE_TYPE.
2585 */
2586 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
2587 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
2588 {
2589 macho_nlist_32_t *paSyms = (macho_nlist_32_t *)pThis->pvaSymbols;
2590 for (iSym = 0; iSym < cSyms; iSym++)
2591 {
2592 /* skip stabs */
2593 if (paSyms[iSym].n_type & MACHO_N_STAB)
2594 continue;
2595
2596 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2597 {
2598 const char *pszSymbol;
2599 size_t cchSymbol;
2600 //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
2601 RTLDRADDR Value = NIL_RTLDRADDR;
2602
2603 /** @todo Implement N_REF_TO_WEAK. */
2604 RTLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
2605
2606 /* Get the symbol name. */
2607 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2608 pszSymbol = &pThis->pchStrings[paSyms[iSym].n_un.n_strx];
2609 cchSymbol = strlen(pszSymbol);
2610
2611 /* Check for linker defined symbols relating to sections and segments. */
2612 if ( cchSymbol > sizeof("section$end$") - 1
2613 && *pszSymbol == 's'
2614 && memchr(pszSymbol, '$', cchSymbol))
2615 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
2616 else
2617 rc = VERR_SYMBOL_NOT_FOUND;
2618
2619 /* Ask the user for an address to the symbol. */
2620 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
2621 if (RT_FAILURE_NP(rc))
2622 rc = pfnGetImport(&pThis->Core, NULL /*pszModule*/, pszSymbol + (pszSymbol[0] == '_'),
2623 UINT32_MAX, &Value/*, &fKind*/, pvUser);
2624 if (RT_FAILURE(rc))
2625 {
2626 /* weak reference? */
2627 if (!(paSyms[iSym].n_desc & N_WEAK_REF))
2628 break;
2629 Value = 0;
2630 }
2631
2632 /* Update the symbol. */
2633 paSyms[iSym].n_value = (uint32_t)Value;
2634 if (paSyms[iSym].n_value != Value)
2635 {
2636 rc = VERR_LDR_ADDRESS_OVERFLOW;
2637 break;
2638 }
2639 }
2640 else if (paSyms[iSym].n_desc & N_WEAK_DEF)
2641 {
2642 /** @todo implement weak symbols. */
2643 /*return VERR_LDRMACHO_TODO; - ignored for now. */
2644 }
2645 }
2646 }
2647 else
2648 {
2649 /* (Identical to the 32-bit code, just different paSym type. (and n_strx is unsigned)) */
2650 macho_nlist_64_t *paSyms = (macho_nlist_64_t *)pThis->pvaSymbols;
2651 for (iSym = 0; iSym < cSyms; iSym++)
2652 {
2653 /* skip stabs */
2654 if (paSyms[iSym].n_type & MACHO_N_STAB)
2655 continue;
2656
2657 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2658 {
2659 const char *pszSymbol;
2660 size_t cchSymbol;
2661 //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
2662 RTLDRADDR Value = NIL_RTLDRADDR;
2663
2664 /** @todo Implement N_REF_TO_WEAK. */
2665 RTLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
2666
2667 /* Get the symbol name. */
2668 RTLDRMODMACHO_CHECK_RETURN(paSyms[iSym].n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2669 pszSymbol = &pThis->pchStrings[paSyms[iSym].n_un.n_strx];
2670 cchSymbol = strlen(pszSymbol);
2671
2672 /* Check for linker defined symbols relating to sections and segments. */
2673 if ( cchSymbol > sizeof("section$end$") - 1
2674 && *pszSymbol == 's'
2675 && memchr(pszSymbol, '$', cchSymbol))
2676 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
2677 else
2678 rc = VERR_SYMBOL_NOT_FOUND;
2679
2680 /* Ask the user for an address to the symbol. */
2681 /** @todo figure out a better way to deal with underscore prefixes. sigh. */
2682 if (RT_FAILURE_NP(rc))
2683 rc = pfnGetImport(&pThis->Core, NULL, pszSymbol + (*pszSymbol == '_'),
2684 UINT32_MAX, &Value, /*&fKind,*/ pvUser);
2685 if (RT_FAILURE(rc))
2686 {
2687 /* weak reference? */
2688 if (!(paSyms[iSym].n_desc & N_WEAK_REF))
2689 break;
2690 Value = 0;
2691 }
2692
2693 /* Update the symbol. */
2694 paSyms[iSym].n_value = Value;
2695 if (paSyms[iSym].n_value != Value)
2696 {
2697 rc = VERR_LDR_ADDRESS_OVERFLOW;
2698 break;
2699 }
2700 }
2701 else if (paSyms[iSym].n_desc & N_WEAK_DEF)
2702 {
2703 /** @todo implement weak symbols. */
2704 /*return VERR_LDRMACHO_TODO; - ignored for now. */
2705 }
2706 }
2707 }
2708
2709 return rc;
2710}
2711
2712
2713/**
2714 * MH_OBJECT: Applies base relocations to a (unprotected) image mapping.
2715 *
2716 * @returns IPRT status code.
2717 * @param pThis The Mach-O module interpreter instance.
2718 * @param pvMapping The mapping to fixup.
2719 * @param NewBaseAddress The address to fixup the mapping to.
2720 */
2721static int kldrModMachOObjDoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress)
2722{
2723 /*
2724 * Ensure that we've got the symbol table and section fixups handy.
2725 */
2726 int rc = kldrModMachOLoadObjSymTab(pThis);
2727 if (RT_FAILURE(rc))
2728 return rc;
2729
2730 /*
2731 * Iterate over the segments and their sections and apply fixups.
2732 */
2733 rc = VINF_SUCCESS;
2734 for (uint32_t iSeg = 0; RT_SUCCESS(rc) && iSeg < pThis->cSegments; iSeg++)
2735 {
2736 PRTLDRMODMACHOSEG pSeg = &pThis->aSegments[iSeg];
2737 for (uint32_t iSect = 0; iSect < pSeg->cSections; iSect++)
2738 {
2739 PRTLDRMODMACHOSECT pSect = &pSeg->paSections[iSect];
2740
2741 /* skip sections without fixups. */
2742 if (!pSect->cFixups)
2743 continue;
2744
2745 /* lazy load (and endian convert) the fixups. */
2746 if (!pSect->paFixups)
2747 {
2748 rc = kldrModMachOLoadFixups(pThis, pSect->offFixups, pSect->cFixups, &pSect->paFixups);
2749 if (RT_FAILURE(rc))
2750 break;
2751 }
2752
2753 /*
2754 * Apply the fixups.
2755 */
2756 uint8_t *pbSectBits = (uint8_t *)pvMapping + (uintptr_t)pSect->RVA;
2757 if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE) /** @todo this aint right. */
2758 rc = kldrModMachOFixupSectionGeneric32Bit(pThis, pbSectBits, pSect,
2759 (macho_nlist_32_t *)pThis->pvaSymbols,
2760 pThis->cSymbols, NewBaseAddress);
2761 else if ( pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
2762 && pThis->Hdr.cputype == CPU_TYPE_X86_64)
2763 rc = kldrModMachOFixupSectionAMD64(pThis, pbSectBits, pSect,
2764 (macho_nlist_64_t *)pThis->pvaSymbols,
2765 pThis->cSymbols, NewBaseAddress);
2766 else
2767 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2768 if (RT_FAILURE(rc))
2769 break;
2770 }
2771 }
2772
2773 return rc;
2774}
2775
2776
2777/**
2778 * Applies generic fixups to a section in an image of the same endian-ness
2779 * as the host CPU.
2780 *
2781 * @returns IPRT status code.
2782 * @param pThis The Mach-O module interpreter instance.
2783 * @param pbSectBits Pointer to the section bits.
2784 * @param pFixupSect The section being fixed up.
2785 * @param paSyms Pointer to the symbol table.
2786 * @param cSyms Number of symbols.
2787 * @param NewBaseAddress The new base image address.
2788 */
2789static int kldrModMachOFixupSectionGeneric32Bit(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, PRTLDRMODMACHOSECT pFixupSect,
2790 macho_nlist_32_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
2791{
2792 const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
2793 const uint32_t cFixups = pFixupSect->cFixups;
2794 size_t cbSectBits = (size_t)pFixupSect->cb;
2795 const uint8_t *pbSectVirginBits;
2796 uint32_t iFixup;
2797 RTLDRADDR SymAddr = ~(RTLDRADDR)0;
2798 int rc;
2799
2800 /*
2801 * Find the virgin bits.
2802 */
2803 if (pFixupSect->offFile != -1)
2804 {
2805 rc = kldrModMachOMapVirginBits(pThis);
2806 if (RT_FAILURE(rc))
2807 return rc;
2808 pbSectVirginBits = (const uint8_t *)pThis->pvBits + pFixupSect->offFile;
2809 }
2810 else
2811 pbSectVirginBits = NULL;
2812
2813 /*
2814 * Iterate the fixups and apply them.
2815 */
2816 for (iFixup = 0; iFixup < cFixups; iFixup++)
2817 {
2818 RTPTRUNION uFix;
2819 RTPTRUNION uFixVirgin;
2820 union
2821 {
2822 macho_relocation_info_t r;
2823 scattered_relocation_info_t s;
2824 } Fixup;
2825 Fixup.r = paFixups[iFixup];
2826
2827 if (!(Fixup.r.r_address & R_SCATTERED))
2828 {
2829 /* sanity */
2830 if ((uint32_t)Fixup.r.r_address >= cbSectBits)
2831 return VERR_LDR_BAD_FIXUP;
2832
2833 /* calc fixup addresses. */
2834 uFix.pv = pbSectBits + Fixup.r.r_address;
2835 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.r.r_address : 0;
2836
2837 /*
2838 * Calc the symbol value.
2839 */
2840 /* Calc the linked symbol address / addend. */
2841 switch (Fixup.r.r_length)
2842 {
2843 /** @todo Deal with unaligned accesses on non x86 platforms. */
2844 case 0: SymAddr = *uFixVirgin.pi8; break;
2845 case 1: SymAddr = *uFixVirgin.pi16; break;
2846 case 2: SymAddr = *uFixVirgin.pi32; break;
2847 case 3: SymAddr = *uFixVirgin.pi64; break;
2848 }
2849 if (Fixup.r.r_pcrel)
2850 SymAddr += Fixup.r.r_address + pFixupSect->LinkAddress;
2851
2852 /* Add symbol / section address. */
2853 if (Fixup.r.r_extern)
2854 {
2855 const macho_nlist_32_t *pSym;
2856 if (Fixup.r.r_symbolnum >= cSyms)
2857 return VERR_LDR_BAD_FIXUP;
2858 pSym = &paSyms[Fixup.r.r_symbolnum];
2859
2860 if (pSym->n_type & MACHO_N_STAB)
2861 return VERR_LDR_BAD_FIXUP;
2862
2863 switch (pSym->n_type & MACHO_N_TYPE)
2864 {
2865 case MACHO_N_SECT:
2866 {
2867 PRTLDRMODMACHOSECT pSymSect;
2868 RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2869 pSymSect = &pThis->paSections[pSym->n_sect - 1];
2870
2871 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
2872 break;
2873 }
2874
2875 case MACHO_N_UNDF:
2876 case MACHO_N_ABS:
2877 SymAddr += pSym->n_value;
2878 break;
2879
2880 case MACHO_N_INDR:
2881 case MACHO_N_PBUD:
2882 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2883 default:
2884 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
2885 }
2886 }
2887 else if (Fixup.r.r_symbolnum != R_ABS)
2888 {
2889 PRTLDRMODMACHOSECT pSymSect;
2890 if (Fixup.r.r_symbolnum > pThis->cSections)
2891 return VERR_LDR_BAD_FIXUP;
2892 pSymSect = &pThis->paSections[Fixup.r.r_symbolnum - 1];
2893
2894 SymAddr -= pSymSect->LinkAddress;
2895 SymAddr += pSymSect->RVA + NewBaseAddress;
2896 }
2897
2898 /* adjust for PC relative */
2899 if (Fixup.r.r_pcrel)
2900 SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
2901 }
2902 else
2903 {
2904 PRTLDRMODMACHOSECT pSymSect;
2905 uint32_t iSymSect;
2906 RTLDRADDR Value;
2907
2908 /* sanity */
2909 RTLDRMODMACHO_ASSERT(Fixup.s.r_scattered);
2910 if ((uint32_t)Fixup.s.r_address >= cbSectBits)
2911 return VERR_LDR_BAD_FIXUP;
2912
2913 /* calc fixup addresses. */
2914 uFix.pv = pbSectBits + Fixup.s.r_address;
2915 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.s.r_address : 0;
2916
2917 /*
2918 * Calc the symbol value.
2919 */
2920 /* The addend is stored in the code. */
2921 switch (Fixup.s.r_length)
2922 {
2923 case 0: SymAddr = *uFixVirgin.pi8; break;
2924 case 1: SymAddr = *uFixVirgin.pi16; break;
2925 case 2: SymAddr = *uFixVirgin.pi32; break;
2926 case 3: SymAddr = *uFixVirgin.pi64; break;
2927 }
2928 if (Fixup.s.r_pcrel)
2929 SymAddr += Fixup.s.r_address;
2930 Value = Fixup.s.r_value;
2931 SymAddr -= Value; /* (-> addend only) */
2932
2933 /* Find the section number from the r_value. */
2934 pSymSect = NULL;
2935 for (iSymSect = 0; iSymSect < pThis->cSections; iSymSect++)
2936 {
2937 RTLDRADDR off = Value - pThis->paSections[iSymSect].LinkAddress;
2938 if (off < pThis->paSections[iSymSect].cb)
2939 {
2940 pSymSect = &pThis->paSections[iSymSect];
2941 break;
2942 }
2943 else if (off == pThis->paSections[iSymSect].cb) /* edge case */
2944 pSymSect = &pThis->paSections[iSymSect];
2945 }
2946 if (!pSymSect)
2947 return VERR_LDR_BAD_FIXUP;
2948
2949 /* Calc the symbol address. */
2950 SymAddr += Value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
2951 if (Fixup.s.r_pcrel)
2952 SymAddr -= Fixup.s.r_address + pFixupSect->RVA + NewBaseAddress;
2953
2954 Fixup.r.r_length = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_length;
2955 Fixup.r.r_type = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_type;
2956 }
2957
2958 /*
2959 * Write back the fixed up value.
2960 */
2961 if (Fixup.r.r_type == GENERIC_RELOC_VANILLA)
2962 {
2963 switch (Fixup.r.r_length)
2964 {
2965 case 0: *uFix.pu8 = (uint8_t)SymAddr; break;
2966 case 1: *uFix.pu16 = (uint16_t)SymAddr; break;
2967 case 2: *uFix.pu32 = (uint32_t)SymAddr; break;
2968 case 3: *uFix.pu64 = (uint64_t)SymAddr; break;
2969 }
2970 }
2971 else if (Fixup.r.r_type <= GENERIC_RELOC_LOCAL_SECTDIFF)
2972 return VERR_LDRMACHO_UNSUPPORTED_FIXUP_TYPE;
2973 else
2974 return VERR_LDR_BAD_FIXUP;
2975 }
2976
2977 return VINF_SUCCESS;
2978}
2979
2980
2981/**
2982 * Applies AMD64 fixups to a section.
2983 *
2984 * @returns IPRT status code.
2985 * @param pThis The Mach-O module interpreter instance.
2986 * @param pbSectBits Pointer to the section bits.
2987 * @param pFixupSect The section being fixed up.
2988 * @param paSyms Pointer to the symbol table.
2989 * @param cSyms Number of symbols.
2990 * @param NewBaseAddress The new base image address.
2991 */
2992static int kldrModMachOFixupSectionAMD64(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, PRTLDRMODMACHOSECT pFixupSect,
2993 macho_nlist_64_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
2994{
2995 const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
2996 const uint32_t cFixups = pFixupSect->cFixups;
2997 size_t cbSectBits = (size_t)pFixupSect->cb;
2998 const uint8_t *pbSectVirginBits;
2999 uint32_t iFixup;
3000 RTLDRADDR SymAddr;
3001 int rc;
3002
3003 /*
3004 * Find the virgin bits.
3005 */
3006 if (pFixupSect->offFile != -1)
3007 {
3008 rc = kldrModMachOMapVirginBits(pThis);
3009 if (RT_FAILURE(rc))
3010 return rc;
3011 pbSectVirginBits = (const uint8_t *)pThis->pvBits + pFixupSect->offFile;
3012 }
3013 else
3014 pbSectVirginBits = NULL;
3015
3016 /*
3017 * Iterate the fixups and apply them.
3018 */
3019 for (iFixup = 0; iFixup < cFixups; iFixup++)
3020 {
3021 union
3022 {
3023 macho_relocation_info_t r;
3024 scattered_relocation_info_t s;
3025 } Fixup;
3026 Fixup.r = paFixups[iFixup];
3027
3028 /* AMD64 doesn't use scattered fixups. */
3029 RTLDRMODMACHO_CHECK_RETURN(!(Fixup.r.r_address & R_SCATTERED), VERR_LDR_BAD_FIXUP);
3030
3031 /* sanity */
3032 RTLDRMODMACHO_CHECK_RETURN((uint32_t)Fixup.r.r_address < cbSectBits, VERR_LDR_BAD_FIXUP);
3033
3034 /* calc fixup addresses. */
3035 RTPTRUNION uFix;
3036 uFix.pv = pbSectBits + Fixup.r.r_address;
3037 RTPTRUNION uFixVirgin;
3038 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.r.r_address : 0;
3039
3040 /*
3041 * Calc the symbol value.
3042 */
3043 /* Calc the linked symbol address / addend. */
3044 switch (Fixup.r.r_length)
3045 {
3046 /** @todo Deal with unaligned accesses on non x86 platforms. */
3047 case 2: SymAddr = *uFixVirgin.pi32; break;
3048 case 3: SymAddr = *uFixVirgin.pi64; break;
3049 default:
3050 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3051 }
3052
3053 /* Add symbol / section address. */
3054 if (Fixup.r.r_extern)
3055 {
3056 const macho_nlist_64_t *pSym;
3057
3058 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
3059 pSym = &paSyms[Fixup.r.r_symbolnum];
3060 RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_BAD_FIXUP);
3061
3062 switch (Fixup.r.r_type)
3063 {
3064 /* GOT references just needs to have their symbol verified.
3065 Later, we'll optimize GOT building here using a parallel sym->got array. */
3066 case X86_64_RELOC_GOT_LOAD:
3067 case X86_64_RELOC_GOT:
3068 switch (pSym->n_type & MACHO_N_TYPE)
3069 {
3070 case MACHO_N_SECT:
3071 case MACHO_N_UNDF:
3072 case MACHO_N_ABS:
3073 break;
3074 case MACHO_N_INDR:
3075 case MACHO_N_PBUD:
3076 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3077 default:
3078 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3079 }
3080 SymAddr = sizeof(uint64_t) * Fixup.r.r_symbolnum + pThis->GotRVA + NewBaseAddress;
3081 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_BAD_FIXUP);
3082 SymAddr -= 4;
3083 break;
3084
3085 /* Verify the r_pcrel field for signed fixups on the way into the default case. */
3086 case X86_64_RELOC_BRANCH:
3087 case X86_64_RELOC_SIGNED:
3088 case X86_64_RELOC_SIGNED_1:
3089 case X86_64_RELOC_SIGNED_2:
3090 case X86_64_RELOC_SIGNED_4:
3091 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3092 RT_FALL_THRU();
3093 default:
3094 {
3095 /* Adjust with fixup specific addend and vierfy unsigned/r_pcrel. */
3096 switch (Fixup.r.r_type)
3097 {
3098 case X86_64_RELOC_UNSIGNED:
3099 RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3100 break;
3101 case X86_64_RELOC_BRANCH:
3102 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_BAD_FIXUP);
3103 SymAddr -= 4;
3104 break;
3105 case X86_64_RELOC_SIGNED:
3106 case X86_64_RELOC_SIGNED_1:
3107 case X86_64_RELOC_SIGNED_2:
3108 case X86_64_RELOC_SIGNED_4:
3109 SymAddr -= 4;
3110 break;
3111 default:
3112 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3113 }
3114
3115 switch (pSym->n_type & MACHO_N_TYPE)
3116 {
3117 case MACHO_N_SECT:
3118 {
3119 PRTLDRMODMACHOSECT pSymSect;
3120 RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3121 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3122 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3123 break;
3124 }
3125
3126 case MACHO_N_UNDF:
3127 /* branch to an external symbol may have to take a short detour. */
3128 if ( Fixup.r.r_type == X86_64_RELOC_BRANCH
3129 && SymAddr + Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress
3130 - pSym->n_value
3131 + UINT64_C(0x80000000)
3132 >= UINT64_C(0xffffff20))
3133 SymAddr += pThis->cbJmpStub * Fixup.r.r_symbolnum + pThis->JmpStubsRVA + NewBaseAddress;
3134 else
3135 SymAddr += pSym->n_value;
3136 break;
3137
3138 case MACHO_N_ABS:
3139 SymAddr += pSym->n_value;
3140 break;
3141
3142 case MACHO_N_INDR:
3143 case MACHO_N_PBUD:
3144 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3145 default:
3146 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3147 }
3148 break;
3149 }
3150
3151 /*
3152 * This is a weird customer, it will always be follows by an UNSIGNED fixup.
3153 */
3154 case X86_64_RELOC_SUBTRACTOR:
3155 {
3156 macho_relocation_info_t Fixup2;
3157
3158 /* Deal with the SUBTRACT symbol first, by subtracting it from SymAddr. */
3159 switch (pSym->n_type & MACHO_N_TYPE)
3160 {
3161 case MACHO_N_SECT:
3162 {
3163 PRTLDRMODMACHOSECT pSymSect;
3164 RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3165 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3166 SymAddr -= pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3167 break;
3168 }
3169
3170 case MACHO_N_UNDF:
3171 case MACHO_N_ABS:
3172 SymAddr -= pSym->n_value;
3173 break;
3174
3175 case MACHO_N_INDR:
3176 case MACHO_N_PBUD:
3177 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3178 default:
3179 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3180 }
3181
3182 /* Load the 2nd fixup, check sanity. */
3183 iFixup++;
3184 RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel && iFixup < cFixups, VERR_LDR_BAD_FIXUP);
3185 Fixup2 = paFixups[iFixup];
3186 RTLDRMODMACHO_CHECK_RETURN( Fixup2.r_address == Fixup.r.r_address
3187 && Fixup2.r_length == Fixup.r.r_length
3188 && Fixup2.r_type == X86_64_RELOC_UNSIGNED
3189 && !Fixup2.r_pcrel
3190 && Fixup2.r_symbolnum < cSyms,
3191 VERR_LDR_BAD_FIXUP);
3192
3193 if (Fixup2.r_extern)
3194 {
3195 RTLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
3196 pSym = &paSyms[Fixup2.r_symbolnum];
3197 RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_BAD_FIXUP);
3198
3199 /* Add it's value to SymAddr. */
3200 switch (pSym->n_type & MACHO_N_TYPE)
3201 {
3202 case MACHO_N_SECT:
3203 {
3204 PRTLDRMODMACHOSECT pSymSect;
3205 RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3206 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3207 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3208 break;
3209 }
3210
3211 case MACHO_N_UNDF:
3212 case MACHO_N_ABS:
3213 SymAddr += pSym->n_value;
3214 break;
3215
3216 case MACHO_N_INDR:
3217 case MACHO_N_PBUD:
3218 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3219 default:
3220 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3221 }
3222 }
3223 else if (Fixup2.r_symbolnum != R_ABS)
3224 {
3225 PRTLDRMODMACHOSECT pSymSect;
3226 RTLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
3227 pSymSect = &pThis->paSections[Fixup2.r_symbolnum - 1];
3228 SymAddr += pSymSect->RVA + NewBaseAddress;
3229 }
3230 else
3231 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3232 }
3233 break;
3234 }
3235 }
3236 else
3237 {
3238 /* verify against fixup type and make adjustments */
3239 switch (Fixup.r.r_type)
3240 {
3241 case X86_64_RELOC_UNSIGNED:
3242 RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3243 break;
3244 case X86_64_RELOC_BRANCH:
3245 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3246 SymAddr += 4; /* dunno what the assmbler/linker really is doing here... */
3247 break;
3248 case X86_64_RELOC_SIGNED:
3249 case X86_64_RELOC_SIGNED_1:
3250 case X86_64_RELOC_SIGNED_2:
3251 case X86_64_RELOC_SIGNED_4:
3252 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3253 break;
3254 /*case X86_64_RELOC_GOT_LOAD:*/
3255 /*case X86_64_RELOC_GOT: */
3256 /*case X86_64_RELOC_SUBTRACTOR: - must be r_extern=1 says as. */
3257 default:
3258 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3259 }
3260 if (Fixup.r.r_symbolnum != R_ABS)
3261 {
3262 PRTLDRMODMACHOSECT pSymSect;
3263 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
3264 pSymSect = &pThis->paSections[Fixup.r.r_symbolnum - 1];
3265
3266 SymAddr -= pSymSect->LinkAddress;
3267 SymAddr += pSymSect->RVA + NewBaseAddress;
3268 if (Fixup.r.r_pcrel)
3269 SymAddr += Fixup.r.r_address;
3270 }
3271 }
3272
3273 /* adjust for PC relative */
3274 if (Fixup.r.r_pcrel)
3275 SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
3276
3277 /*
3278 * Write back the fixed up value.
3279 */
3280 switch (Fixup.r.r_length)
3281 {
3282 case 3:
3283 *uFix.pu64 = (uint64_t)SymAddr;
3284 break;
3285 case 2:
3286 RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel || Fixup.r.r_type == X86_64_RELOC_SUBTRACTOR, VERR_LDR_BAD_FIXUP);
3287 RTLDRMODMACHO_CHECK_RETURN((int32_t)SymAddr == (int64_t)SymAddr, VERR_LDR_ADDRESS_OVERFLOW);
3288 *uFix.pu32 = (uint32_t)SymAddr;
3289 break;
3290 default:
3291 RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3292 }
3293 }
3294
3295 return VINF_SUCCESS;
3296}
3297
3298
3299/**
3300 * Loads the symbol table for a MH_OBJECT file.
3301 *
3302 * The symbol table is pointed to by RTLDRMODMACHO::pvaSymbols.
3303 *
3304 * @returns IPRT status code.
3305 * @param pThis The Mach-O module interpreter instance.
3306 */
3307static int kldrModMachOLoadObjSymTab(PRTLDRMODMACHO pThis)
3308{
3309 int rc = VINF_SUCCESS;
3310
3311 if ( !pThis->pvaSymbols
3312 && pThis->cSymbols)
3313 {
3314 size_t cbSyms;
3315 size_t cbSym;
3316 void *pvSyms;
3317 void *pvStrings;
3318
3319 /* sanity */
3320 RTLDRMODMACHO_CHECK_RETURN( pThis->offSymbols
3321 && (!pThis->cchStrings || pThis->offStrings),
3322 VERR_LDRMACHO_BAD_OBJECT_FILE);
3323
3324 /* allocate */
3325 cbSym = pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
3326 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
3327 ? sizeof(macho_nlist_32_t)
3328 : sizeof(macho_nlist_64_t);
3329 cbSyms = pThis->cSymbols * cbSym;
3330 RTLDRMODMACHO_CHECK_RETURN(cbSyms / cbSym == pThis->cSymbols, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
3331 rc = VERR_NO_MEMORY;
3332 pvSyms = RTMemAlloc(cbSyms);
3333 if (pvSyms)
3334 {
3335 if (pThis->cchStrings)
3336 pvStrings = RTMemAlloc(pThis->cchStrings);
3337 else
3338 pvStrings = RTMemAllocZ(4);
3339 if (pvStrings)
3340 {
3341 /* read */
3342 rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvSyms, cbSyms, pThis->offSymbols);
3343 if (RT_SUCCESS(rc) && pThis->cchStrings)
3344 rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvStrings,
3345 pThis->cchStrings, pThis->offStrings);
3346 if (RT_SUCCESS(rc))
3347 {
3348 pThis->pvaSymbols = pvSyms;
3349 pThis->pchStrings = (char *)pvStrings;
3350
3351 /* perform endian conversion? */
3352 if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
3353 {
3354 uint32_t cLeft = pThis->cSymbols;
3355 macho_nlist_32_t *pSym = (macho_nlist_32_t *)pvSyms;
3356 while (cLeft-- > 0)
3357 {
3358 pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
3359 pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
3360 pSym->n_value = RT_BSWAP_U32(pSym->n_value);
3361 pSym++;
3362 }
3363 }
3364 else if (pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
3365 {
3366 uint32_t cLeft = pThis->cSymbols;
3367 macho_nlist_64_t *pSym = (macho_nlist_64_t *)pvSyms;
3368 while (cLeft-- > 0)
3369 {
3370 pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
3371 pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
3372 pSym->n_value = RT_BSWAP_U64(pSym->n_value);
3373 pSym++;
3374 }
3375 }
3376
3377 return VINF_SUCCESS;
3378 }
3379 RTMemFree(pvStrings);
3380 }
3381 RTMemFree(pvSyms);
3382 }
3383 }
3384 else
3385 RTLDRMODMACHO_ASSERT(pThis->pchStrings || pThis->Hdr.filetype == MH_DSYM);
3386
3387 return rc;
3388}
3389
3390
3391/**
3392 * Loads the fixups at the given address and performs endian
3393 * conversion if necessary.
3394 *
3395 * @returns IPRT status code.
3396 * @param pThis The Mach-O module interpreter instance.
3397 * @param offFixups The file offset of the fixups.
3398 * @param cFixups The number of fixups to load.
3399 * @param ppaFixups Where to put the pointer to the allocated fixup array.
3400 */
3401static int kldrModMachOLoadFixups(PRTLDRMODMACHO pThis, RTFOFF offFixups, uint32_t cFixups, macho_relocation_info_t **ppaFixups)
3402{
3403 macho_relocation_info_t *paFixups;
3404 size_t cbFixups;
3405
3406 /* allocate the memory. */
3407 cbFixups = cFixups * sizeof(*paFixups);
3408 RTLDRMODMACHO_CHECK_RETURN(cbFixups / sizeof(*paFixups) == cFixups, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
3409 paFixups = (macho_relocation_info_t *)RTMemAlloc(cbFixups);
3410 if (!paFixups)
3411 return VERR_NO_MEMORY;
3412
3413 /* read the fixups. */
3414 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, paFixups, cbFixups, offFixups);
3415 if (RT_SUCCESS(rc))
3416 {
3417 *ppaFixups = paFixups;
3418
3419 /* do endian conversion if necessary. */
3420 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
3421 || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
3422 {
3423 uint32_t iFixup;
3424 for (iFixup = 0; iFixup < cFixups; iFixup++)
3425 {
3426 uint32_t *pu32 = (uint32_t *)&paFixups[iFixup];
3427 pu32[0] = RT_BSWAP_U32(pu32[0]);
3428 pu32[1] = RT_BSWAP_U32(pu32[1]);
3429 }
3430 }
3431 }
3432 else
3433 RTMemFree(paFixups);
3434 return rc;
3435}
3436
3437
3438/**
3439 * Maps the virgin file bits into memory if not already done.
3440 *
3441 * @returns IPRT status code.
3442 * @param pThis The Mach-O module interpreter instance.
3443 */
3444static int kldrModMachOMapVirginBits(PRTLDRMODMACHO pThis)
3445{
3446 int rc = VINF_SUCCESS;
3447 if (!pThis->pvBits)
3448 rc = pThis->Core.pReader->pfnMap(pThis->Core.pReader, &pThis->pvBits);
3449 return rc;
3450}
3451
3452#if 0
3453
3454/** @copydoc kLdrModCallInit */
3455static int kldrModMachOCallInit(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
3456{
3457 /* later */
3458 RT_NOREF(pMod);
3459 RT_NOREF(pvMapping);
3460 RT_NOREF(uHandle);
3461 return VINF_SUCCESS;
3462}
3463
3464
3465/** @copydoc kLdrModCallTerm */
3466static int kldrModMachOCallTerm(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
3467{
3468 /* later */
3469 RT_NOREF(pMod);
3470 RT_NOREF(pvMapping);
3471 RT_NOREF(uHandle);
3472 return VINF_SUCCESS;
3473}
3474
3475
3476/** @copydoc kLdrModCallThread */
3477static int kldrModMachOCallThread(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle, unsigned fAttachingOrDetaching)
3478{
3479 /* Relevant for Mach-O? */
3480 RT_NOREF(pMod);
3481 RT_NOREF(pvMapping);
3482 RT_NOREF(uHandle);
3483 RT_NOREF(fAttachingOrDetaching);
3484 return VINF_SUCCESS;
3485}
3486
3487#endif
3488
3489
3490/**
3491 * @interface_method_impl{RTLDROPS,pfnGetImageSize}
3492 */
3493static DECLCALLBACK(size_t) rtldrMachO_GetImageSize(PRTLDRMODINTERNAL pMod)
3494{
3495 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3496 return pThis->cbImage;
3497}
3498
3499
3500/**
3501 * @interface_method_impl{RTLDROPS,pfnGetBits}
3502 */
3503static DECLCALLBACK(int) rtldrMachO_GetBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR BaseAddress,
3504 PFNRTLDRIMPORT pfnGetImport, void *pvUser)
3505{
3506 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3507
3508 if (!pThis->fCanLoad)
3509 return VERR_LDRMACHO_TODO;
3510
3511 /*
3512 * Zero the entire buffer first to simplify things.
3513 */
3514 memset(pvBits, 0, (size_t)pThis->cbImage);
3515
3516 /*
3517 * When possible use the segment table to load the data.
3518 */
3519 for (uint32_t i = 0; i < pThis->cSegments; i++)
3520 {
3521 /* skip it? */
3522 if ( pThis->aSegments[i].SegInfo.cbFile == -1
3523 || pThis->aSegments[i].SegInfo.offFile == -1
3524 || pThis->aSegments[i].SegInfo.LinkAddress == NIL_RTLDRADDR
3525 || !pThis->aSegments[i].SegInfo.Alignment)
3526 continue;
3527 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader,
3528 (uint8_t *)pvBits + pThis->aSegments[i].SegInfo.RVA,
3529 pThis->aSegments[i].SegInfo.cbFile,
3530 pThis->aSegments[i].SegInfo.offFile);
3531 if (RT_FAILURE(rc))
3532 return rc;
3533 }
3534
3535 /*
3536 * Perform relocations.
3537 */
3538 return rtldrMachO_RelocateBits(pMod, pvBits, BaseAddress, pThis->LinkAddress, pfnGetImport, pvUser);
3539}
3540
3541
3542/**
3543 * @interface_method_impl{RTLDROPS,pfnRelocate}
3544 */
3545static DECLCALLBACK(int) rtldrMachO_RelocateBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
3546 RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
3547{
3548 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3549 int rc;
3550 RT_NOREF(OldBaseAddress);
3551
3552 /*
3553 * Call workers to do the jobs.
3554 */
3555 if (pThis->Hdr.filetype == MH_OBJECT)
3556 {
3557 rc = kldrModMachOObjDoImports(pThis, NewBaseAddress, pfnGetImport, pvUser);
3558 if (RT_SUCCESS(rc))
3559 rc = kldrModMachOObjDoFixups(pThis, pvBits, NewBaseAddress);
3560
3561 }
3562 else
3563 rc = VERR_LDRMACHO_TODO;
3564 /*{
3565 rc = kldrModMachODoFixups(pThis, pvBits, NewBaseAddress, OldBaseAddress, pfnGetImport, pvUser);
3566 if (RT_SUCCESS(rc))
3567 rc = kldrModMachODoImports(pThis, pvBits, pfnGetImport, pvUser);
3568 }*/
3569
3570 /*
3571 * Construct the global offset table if necessary, it's always the last
3572 * segment when present.
3573 */
3574 if (RT_SUCCESS(rc) && pThis->fMakeGot)
3575 rc = kldrModMachOMakeGOT(pThis, pvBits, NewBaseAddress);
3576
3577 return rc;
3578}
3579
3580
3581/**
3582 * Builds the GOT.
3583 *
3584 * Assumes the symbol table has all external symbols resolved correctly and that
3585 * the bits has been cleared up front.
3586 */
3587static int kldrModMachOMakeGOT(PRTLDRMODMACHO pThis, void *pvBits, RTLDRADDR NewBaseAddress)
3588{
3589 uint32_t iSym = pThis->cSymbols;
3590 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
3591 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
3592 {
3593 macho_nlist_32_t const *paSyms = (macho_nlist_32_t const *)pThis->pvaSymbols;
3594 uint32_t *paGOT = (uint32_t *)((uint8_t *)pvBits + pThis->GotRVA);
3595 while (iSym-- > 0)
3596 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
3597 {
3598 case MACHO_N_SECT:
3599 {
3600 PRTLDRMODMACHOSECT pSymSect;
3601 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3602 pSymSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
3603 paGOT[iSym] = (uint32_t)(paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress);
3604 break;
3605 }
3606
3607 case MACHO_N_UNDF:
3608 case MACHO_N_ABS:
3609 paGOT[iSym] = paSyms[iSym].n_value;
3610 break;
3611 }
3612 }
3613 else
3614 {
3615 macho_nlist_64_t const *paSyms = (macho_nlist_64_t const *)pThis->pvaSymbols;
3616 uint64_t *paGOT = (uint64_t *)((uint8_t *)pvBits + pThis->GotRVA);
3617 while (iSym-- > 0)
3618 {
3619 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
3620 {
3621 case MACHO_N_SECT:
3622 {
3623 PRTLDRMODMACHOSECT pSymSect;
3624 RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3625 pSymSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
3626 paGOT[iSym] = paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3627 break;
3628 }
3629
3630 case MACHO_N_UNDF:
3631 case MACHO_N_ABS:
3632 paGOT[iSym] = paSyms[iSym].n_value;
3633 break;
3634 }
3635 }
3636
3637 if (pThis->JmpStubsRVA != NIL_RTLDRADDR)
3638 {
3639 iSym = pThis->cSymbols;
3640 switch (pThis->Hdr.cputype)
3641 {
3642 /*
3643 * AMD64 is simple since the GOT and the indirect jmps are parallel
3644 * arrays with entries of the same size. The relative offset will
3645 * be the the same for each entry, kind of nice. :-)
3646 */
3647 case CPU_TYPE_X86_64:
3648 {
3649 uint64_t *paJmps = (uint64_t *)((uint8_t *)pvBits + pThis->JmpStubsRVA);
3650 int32_t off;
3651 uint64_t u64Tmpl;
3652 union
3653 {
3654 uint8_t ab[8];
3655 uint64_t u64;
3656 } Tmpl;
3657
3658 /* create the template. */
3659 off = (int32_t)(pThis->GotRVA - (pThis->JmpStubsRVA + 6));
3660 Tmpl.ab[0] = 0xff; /* jmp [GOT-entry wrt RIP] */
3661 Tmpl.ab[1] = 0x25;
3662 Tmpl.ab[2] = off & 0xff;
3663 Tmpl.ab[3] = (off >> 8) & 0xff;
3664 Tmpl.ab[4] = (off >> 16) & 0xff;
3665 Tmpl.ab[5] = (off >> 24) & 0xff;
3666 Tmpl.ab[6] = 0xcc;
3667 Tmpl.ab[7] = 0xcc;
3668 u64Tmpl = Tmpl.u64;
3669
3670 /* copy the template to every jmp table entry. */
3671 while (iSym-- > 0)
3672 paJmps[iSym] = u64Tmpl;
3673 break;
3674 }
3675
3676 default:
3677 RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3678 }
3679 }
3680 }
3681 return VINF_SUCCESS;
3682}
3683
3684
3685/**
3686 * @interface_method_impl{RTLDROPS,pfnEnumSegments}
3687 */
3688static DECLCALLBACK(int) rtldrMachO_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
3689{
3690 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3691 uint32_t const cSegments = pThis->cSegments;
3692 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3693 {
3694 int rc = pfnCallback(pMod, &pThis->aSegments[iSeg].SegInfo, pvUser);
3695 if (rc != VINF_SUCCESS)
3696 return rc;
3697 }
3698
3699 return VINF_SUCCESS;
3700}
3701
3702
3703/**
3704 * @interface_method_impl{RTLDROPS,pfnLinkAddressToSegOffset}
3705 */
3706static DECLCALLBACK(int) rtldrMachO_LinkAddressToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress,
3707 uint32_t *piSeg, PRTLDRADDR poffSeg)
3708{
3709 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3710 uint32_t const cSegments = pThis->cSegments;
3711 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3712 {
3713 RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
3714 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3715 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3716 {
3717 *piSeg = iSeg;
3718 *poffSeg = offSeg;
3719 return VINF_SUCCESS;
3720 }
3721 }
3722
3723 return VERR_LDR_INVALID_LINK_ADDRESS;
3724}
3725
3726
3727/**
3728 * @interface_method_impl{RTLDROPS,pfnLinkAddressToRva}
3729 */
3730static DECLCALLBACK(int) rtldrMachO_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
3731{
3732 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3733 uint32_t const cSegments = pThis->cSegments;
3734 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3735 {
3736 RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
3737 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3738 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3739 {
3740 *pRva = pThis->aSegments[iSeg].SegInfo.RVA + offSeg;
3741 return VINF_SUCCESS;
3742 }
3743 }
3744
3745 return VERR_LDR_INVALID_RVA;
3746}
3747
3748
3749/**
3750 * @interface_method_impl{RTLDROPS,pfnSegOffsetToRva}
3751 */
3752static DECLCALLBACK(int) rtldrMachO_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg, PRTLDRADDR pRva)
3753{
3754 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3755
3756 if (iSeg >= pThis->cSegments)
3757 return VERR_LDR_INVALID_SEG_OFFSET;
3758 RTLDRMODMACHOSEG const *pSegment = &pThis->aSegments[iSeg];
3759
3760 if ( offSeg > pSegment->SegInfo.cbMapped
3761 && offSeg > pSegment->SegInfo.cb
3762 && ( pSegment->SegInfo.cbFile < 0
3763 || offSeg > (uint64_t)pSegment->SegInfo.cbFile))
3764 return VERR_LDR_INVALID_SEG_OFFSET;
3765
3766 *pRva = pSegment->SegInfo.RVA + offSeg;
3767 return VINF_SUCCESS;
3768}
3769
3770
3771/**
3772 * @interface_method_impl{RTLDROPS,pfnRvaToSegOffset}
3773 */
3774static DECLCALLBACK(int) rtldrMachO_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva, uint32_t *piSeg, PRTLDRADDR poffSeg)
3775{
3776 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3777 uint32_t const cSegments = pThis->cSegments;
3778 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3779 {
3780 RTLDRADDR offSeg = Rva - pThis->aSegments[iSeg].SegInfo.RVA;
3781 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3782 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3783 {
3784 *piSeg = iSeg;
3785 *poffSeg = offSeg;
3786 return VINF_SUCCESS;
3787 }
3788 }
3789
3790 return VERR_LDR_INVALID_RVA;
3791}
3792
3793
3794/**
3795 * @interface_method_impl{RTLDROPS,pfnReadDbgInfo}
3796 */
3797static DECLCALLBACK(int) rtldrMachO_ReadDbgInfo(PRTLDRMODINTERNAL pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void *pvBuf)
3798{
3799 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3800
3801 /** @todo May have to apply fixups here. */
3802 if (iDbgInfo < pThis->cSections)
3803 return pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvBuf, cb, off);
3804 return VERR_OUT_OF_RANGE;
3805}
3806
3807
3808/**
3809 * Loads the code signing blob if necessary (RTLDRMODMACHO::PtrCodeSignature).
3810 *
3811 * @returns IPRT status code.
3812 * @param pThis The mach-o instance.
3813 */
3814static int rtldrMachO_LoadSignatureBlob(PRTLDRMODMACHO pThis)
3815{
3816 Assert(pThis->cbCodeSignature > 0);
3817 if (pThis->PtrCodeSignature.pb != NULL)
3818 return VINF_SUCCESS;
3819
3820 if ( pThis->cbCodeSignature > sizeof(RTCRAPLCSHDR)
3821 && pThis->cbCodeSignature <= _1M)
3822 {
3823 /* Allocate and read. */
3824 void *pv = RTMemAllocZ(RT_ALIGN_Z(pThis->cbCodeSignature, 16));
3825 AssertReturn(pv, VERR_NO_MEMORY);
3826 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pv, pThis->cbCodeSignature, pThis->offCodeSignature);
3827 if (RT_SUCCESS(rc))
3828 {
3829 /* Check blob signature. */
3830 PCRTCRAPLCSSUPERBLOB pSuper = (PCRTCRAPLCSSUPERBLOB)pv;
3831 if (pSuper->Hdr.uMagic == RTCRAPLCS_MAGIC_EMBEDDED_SIGNATURE)
3832 {
3833 uint32_t cbHdr = RT_BE2H_U32(pSuper->Hdr.cb);
3834 uint32_t cSlots = RT_BE2H_U32(pSuper->cSlots);
3835 if ( cbHdr <= pThis->cbCodeSignature
3836 && cbHdr > RT_UOFFSETOF(RTCRAPLCSSUPERBLOB, aSlots)
3837 && cSlots > 0
3838 && cSlots < 128
3839 && RT_UOFFSETOF_DYN(RTCRAPLCSSUPERBLOB, aSlots[cSlots]) <= cbHdr)
3840 {
3841 pThis->PtrCodeSignature.pSuper = pSuper;
3842 return VINF_SUCCESS;
3843 }
3844 rc = VERR_LDRVI_BAD_CERT_HDR_LENGTH;
3845 }
3846 else
3847 rc = VERR_LDRVI_BAD_CERT_HDR_TYPE;
3848 }
3849 RTMemFree(pv);
3850 return rc;
3851 }
3852 return VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY;
3853}
3854
3855
3856/**
3857 * Handles a RTLDRPROP_PKCS7_SIGNED_DATA query.
3858 */
3859static int rtldrMachO_QueryPkcs7SignedData(PRTLDRMODMACHO pThis, void *pvBuf, size_t cbBuf, size_t *pcbRet)
3860{
3861 int rc = rtldrMachO_LoadSignatureBlob(pThis);
3862 if (RT_SUCCESS(rc))
3863 {
3864 /*
3865 * Locate the signature slot.
3866 */
3867 uint32_t iSlot = RT_BE2H_U32(pThis->PtrCodeSignature.pSuper->cSlots);
3868 PCRTCRAPLCSBLOBSLOT pSlot = &pThis->PtrCodeSignature.pSuper->aSlots[iSlot];
3869 while (iSlot-- > 0)
3870 {
3871 pSlot--;
3872 if (pSlot->uType == RTCRAPLCS_SLOT_SIGNATURE)
3873 {
3874 /*
3875 * Validate the data offset.
3876 */
3877 uint32_t offData = RT_BE2H_U32(pSlot->offData);
3878 if ( offData < pThis->cbCodeSignature - sizeof(RTCRAPLCSHDR)
3879 || !(offData & 3) )
3880 {
3881 /*
3882 * The data is prefixed by a header with magic set to blob wrapper.
3883 * Check that the size is within the bounds of the code signing blob.
3884 */
3885 PCRTCRAPLCSHDR pHdr = (PCRTCRAPLCSHDR)&pThis->PtrCodeSignature.pb[offData];
3886 if (pHdr->uMagic == RTCRAPLCS_MAGIC_BLOBWRAPPER)
3887 {
3888 uint32_t cbData = RT_BE2H_U32(pHdr->cb);
3889 uint32_t cbMax = pThis->cbCodeSignature - offData ;
3890 if ( cbData <= cbMax
3891 && cbData > sizeof(RTCRAPLCSHDR))
3892 {
3893 /*
3894 * Copy out the requirest data.
3895 */
3896 *pcbRet = cbData;
3897 if (cbData <= cbBuf)
3898 {
3899 memcpy(pvBuf, pHdr + 1, cbData);
3900 return VINF_SUCCESS;
3901 }
3902 memcpy(pvBuf, pHdr + 1, cbBuf);
3903 return VERR_BUFFER_OVERFLOW;
3904 }
3905 }
3906 }
3907 return VERR_LDRVI_BAD_CERT_FORMAT;
3908 }
3909 }
3910 rc = VERR_NOT_FOUND;
3911 }
3912 return rc;
3913}
3914
3915
3916/** @interface_method_impl{RTLDROPS,pfnQueryProp} */
3917static DECLCALLBACK(int) rtldrMachO_QueryProp(PRTLDRMODINTERNAL pMod, RTLDRPROP enmProp, void const *pvBits,
3918 void *pvBuf, size_t cbBuf, size_t *pcbRet)
3919{
3920 PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
3921 int rc = VERR_NOT_FOUND;
3922 switch (enmProp)
3923 {
3924 case RTLDRPROP_UUID:
3925 Assert(cbBuf >= sizeof(pThis->abImageUuid));
3926 if (!ASMMemIsZero(pThis->abImageUuid, sizeof(pThis->abImageUuid)))
3927 {
3928 *pcbRet = sizeof(pThis->abImageUuid);
3929 memcpy(pvBuf, pThis->abImageUuid, sizeof(pThis->abImageUuid));
3930 return VINF_SUCCESS;
3931 }
3932 break;
3933
3934 case RTLDRPROP_FILE_OFF_HEADER:
3935 Assert(cbBuf == sizeof(uint32_t) || cbBuf == sizeof(uint64_t));
3936 if (cbBuf == sizeof(uint32_t))
3937 *(uint32_t *)pvBuf = pThis->offImage;
3938 else
3939 *(uint64_t *)pvBuf = pThis->offImage;
3940 return VINF_SUCCESS;
3941
3942 case RTLDRPROP_IS_SIGNED:
3943 Assert(cbBuf == sizeof(bool));
3944 Assert(*pcbRet == cbBuf);
3945 *(bool *)pvBuf = pThis->cbCodeSignature > 0;
3946 return VINF_SUCCESS;
3947
3948 case RTLDRPROP_PKCS7_SIGNED_DATA:
3949 if (pThis->cbCodeSignature > 0)
3950 return rtldrMachO_QueryPkcs7SignedData(pThis, pvBuf, cbBuf, pcbRet);
3951 break;
3952
3953
3954#if 0 /** @todo return LC_ID_DYLIB */
3955 case RTLDRPROP_INTERNAL_NAME:
3956#endif
3957
3958 default:
3959 break;
3960 }
3961 NOREF(cbBuf);
3962 RT_NOREF_PV(pvBits);
3963 return rc;
3964}
3965
3966
3967/**
3968 * Operations for a Mach-O module interpreter.
3969 */
3970static const RTLDROPS s_rtldrMachOOps=
3971{
3972 "mach-o",
3973 rtldrMachO_Close,
3974 NULL,
3975 NULL /*pfnDone*/,
3976 rtldrMachO_EnumSymbols,
3977 /* ext */
3978 rtldrMachO_GetImageSize,
3979 rtldrMachO_GetBits,
3980 rtldrMachO_RelocateBits,
3981 rtldrMachO_GetSymbolEx,
3982 NULL /*pfnQueryForwarderInfo*/,
3983 rtldrMachO_EnumDbgInfo,
3984 rtldrMachO_EnumSegments,
3985 rtldrMachO_LinkAddressToSegOffset,
3986 rtldrMachO_LinkAddressToRva,
3987 rtldrMachO_SegOffsetToRva,
3988 rtldrMachO_RvaToSegOffset,
3989 rtldrMachO_ReadDbgInfo,
3990 rtldrMachO_QueryProp,
3991 NULL /*pfnVerifySignature*/,
3992 NULL /*pfnHashImage*/,
3993 NULL /*pfnUnwindFrame*/,
3994 42
3995};
3996
3997
3998/**
3999 * Handles opening Mach-O images (non-fat).
4000 */
4001DECLHIDDEN(int) rtldrMachOOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offImage,
4002 PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
4003{
4004
4005 /*
4006 * Create the instance data and do a minimal header validation.
4007 */
4008 PRTLDRMODMACHO pThis = NULL;
4009 int rc = kldrModMachODoCreate(pReader, offImage, fFlags, &pThis, pErrInfo);
4010 if (RT_SUCCESS(rc))
4011 {
4012 /*
4013 * Match up against the requested CPU architecture.
4014 */
4015 if ( enmArch == RTLDRARCH_WHATEVER
4016 || pThis->Core.enmArch == enmArch)
4017 {
4018 pThis->Core.pOps = &s_rtldrMachOOps;
4019 pThis->Core.u32Magic = RTLDRMOD_MAGIC;
4020 *phLdrMod = &pThis->Core;
4021 return VINF_SUCCESS;
4022 }
4023 rc = VERR_LDR_ARCH_MISMATCH;
4024 }
4025 if (pThis)
4026 {
4027 RTMemFree(pThis->pbLoadCommands);
4028 RTMemFree(pThis);
4029 }
4030 return rc;
4031
4032}
4033
4034
4035/**
4036 * Handles opening FAT Mach-O image.
4037 */
4038DECLHIDDEN(int) rtldrFatOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
4039{
4040 fat_header_t FatHdr;
4041 int rc = pReader->pfnRead(pReader, &FatHdr, sizeof(FatHdr), 0);
4042 if (RT_FAILURE(rc))
4043 return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
4044
4045 if (FatHdr.magic == IMAGE_FAT_SIGNATURE)
4046 { /* likely */ }
4047 else if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
4048 FatHdr.nfat_arch = RT_BSWAP_U32(FatHdr.nfat_arch);
4049 else
4050 return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "magic=%#x", FatHdr.magic);
4051 if (FatHdr.nfat_arch < 64)
4052 return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "Bad nfat_arch value: %#x", FatHdr.nfat_arch);
4053
4054 uint32_t offEntry = sizeof(FatHdr);
4055 for (uint32_t i = 0; i < FatHdr.nfat_arch; i++, offEntry += sizeof(fat_arch_t))
4056 {
4057 fat_arch_t FatEntry;
4058 rc = pReader->pfnRead(pReader, &FatEntry, sizeof(FatEntry), offEntry);
4059 if (RT_FAILURE(rc))
4060 return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
4061 if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
4062 {
4063 FatEntry.cputype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cputype);
4064 //FatEntry.cpusubtype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cpusubtype);
4065 FatEntry.offset = RT_BSWAP_U32(FatEntry.offset);
4066 //FatEntry.size = RT_BSWAP_U32(FatEntry.size);
4067 //FatEntry.align = RT_BSWAP_U32(FatEntry.align);
4068 }
4069
4070 /*
4071 * Match enmArch.
4072 */
4073 bool fMatch = false;
4074 switch (enmArch)
4075 {
4076 case RTLDRARCH_WHATEVER:
4077 fMatch = true;
4078 break;
4079
4080 case RTLDRARCH_X86_32:
4081 fMatch = FatEntry.cputype == CPU_TYPE_X86;
4082 break;
4083
4084 case RTLDRARCH_AMD64:
4085 fMatch = FatEntry.cputype == CPU_TYPE_X86_64;
4086 break;
4087
4088 case RTLDRARCH_ARM32:
4089 case RTLDRARCH_ARM64:
4090 case RTLDRARCH_X86_16:
4091 fMatch = false;
4092 break;
4093
4094 case RTLDRARCH_INVALID:
4095 case RTLDRARCH_HOST:
4096 case RTLDRARCH_END:
4097 case RTLDRARCH_32BIT_HACK:
4098 AssertFailedReturn(VERR_INVALID_PARAMETER);
4099 }
4100 if (fMatch)
4101 return rtldrMachOOpen(pReader, fFlags, enmArch, FatEntry.offset, phLdrMod, pErrInfo);
4102 }
4103
4104 return VERR_LDR_ARCH_MISMATCH;
4105
4106}
4107
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