VirtualBox

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

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

IPRT: Fixed segment alignment bug in the mach-o code. bugref:9232

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