VirtualBox

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

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

IPRT: More adjustments to the LX and Mach-O loader code from kStuff. bugref:9232

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 151.0 KB
Line 
1/* $Id: ldrMachO.cpp 74646 2018-10-06 21:03:45Z 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 KLDRMODMACHO_STRICT
54 * Define KLDRMODMACHO_STRICT to enabled strict checks in KLDRMODMACHO. */
55#define KLDRMODMACHO_STRICT 1
56
57/** @def KLDRMODMACHO_ASSERT
58 * Assert that an expression is true when KLDR_STRICT is defined.
59 */
60#ifdef KLDRMODMACHO_STRICT
61# define KLDRMODMACHO_ASSERT(expr) Assert(expr)
62#else
63# define KLDRMODMACHO_ASSERT(expr) do {} while (0)
64#endif
65
66/** @def KLDRMODMACHO_CHECK_RETURN
67 * Checks that an expression is true and return if it isn't.
68 * This is a debug aid.
69 */
70#ifdef KLDRMODMACHO_STRICT2
71# define KLDRMODMACHO_CHECK_RETURN(expr, rc) AssertReturn(expr, rc)
72#else
73# define KLDRMODMACHO_CHECK_RETURN(expr, rc) do { if (!(expr)) { return (rc); } } while (0)
74#endif
75
76/** @def KLDRMODMACHO_CHECK_RETURN
77 * Checks that an expression is true and return if it isn't.
78 * This is a debug aid.
79 */
80#ifdef KLDRMODMACHO_STRICT2
81# define KLDRMODMACHO_FAILED_RETURN(rc) AssertFailedReturn(rc)
82#else
83# define KLDRMODMACHO_FAILED_RETURN(rc) return (rc)
84#endif
85
86
87/*********************************************************************************************************************************
88* Structures and Typedefs *
89*********************************************************************************************************************************/
90/**
91 * Mach-O section details.
92 */
93typedef struct KLDRMODMACHOSECT
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} KLDRMODMACHOSECT, *PKLDRMODMACHOSECT;
118
119/**
120 * Extra per-segment info.
121 *
122 * This is corresponds to a kLdr segment, not a Mach-O segment!
123 */
124typedef struct KLDRMODMACHOSEG
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 PKLDRMODMACHOSECT paSections;
137
138} KLDRMODMACHOSEG, *PKLDRMODMACHOSEG;
139
140/**
141 * Instance data for the Mach-O MH_OBJECT module interpreter.
142 * @todo interpret the other MH_* formats.
143 */
144typedef struct KLDRMODMACHO
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 PKLDRMODMACHOSECT paSections;
212
213 /** Array of segments parallel to the one in KLDRMOD. */
214 KLDRMODMACHOSEG aSegments[1];
215} KLDRMODMACHO, *PKLDRMODMACHO;
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(PKLDRMODMACHO pThis, char *pbStringPool, uint32_t cbStringPool);
234
235static int kldrModMachOLoadObjSymTab(PKLDRMODMACHO pThis);
236static int kldrModMachOLoadFixups(PKLDRMODMACHO pThis, RTFOFF offFixups, uint32_t cFixups, macho_relocation_info_t **ppaFixups);
237static int kldrModMachOMapVirginBits(PKLDRMODMACHO pThis);
238
239static int kldrModMachODoQuerySymbol32Bit(PKLDRMODMACHO 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(PKLDRMODMACHO 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(PKLDRMODMACHO 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(PKLDRMODMACHO 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(PKLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser);
252static int kldrModMachOObjDoFixups(PKLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress);
253static int kldrModMachOFixupSectionGeneric32Bit(PKLDRMODMACHO pThis, uint8_t *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
254 macho_nlist_32_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress);
255static int kldrModMachOFixupSectionAMD64(PKLDRMODMACHO pThis, uint8_t *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
256 macho_nlist_64_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress);
257
258static int kldrModMachOMakeGOT(PKLDRMODMACHO pThis, void *pvBits, RTLDRADDR NewBaseAddress);
259
260/*static int kldrModMachODoFixups(PKLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress, RTLDRADDR OldBaseAddress);
261static int kldrModMachODoImports(PKLDRMODMACHO 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 PKLDRMODMACHO *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(KLDRMODMACHO, aSegments[cSegments])
360 + sizeof(KLDRMODMACHOSECT) * cSections, 16);
361 PKLDRMODMACHO pThis = (PKLDRMODMACHO)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 /* KLDRMODMACHO */
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 = (PKLDRMODMACHOSECT)&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 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_32_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
587 KLDRMODMACHO_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 KLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize == 0 \
616 || ( pSrcSeg->fileoff <= cbFile \
617 && (uint64_t)pSrcSeg->fileoff + pSrcSeg->filesize <= cbFile), \
618 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
619 KLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize <= pSrcSeg->vmsize \
620 || (fSkipSeg && !strcmp(pSrcSeg->segname, "__CTF") /* see above */), \
621 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
622 KLDRMODMACHO_CHECK_RETURN(!(~pSrcSeg->maxprot & pSrcSeg->initprot), \
623 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
624 KLDRMODMACHO_CHECK_RETURN(!(pSrcSeg->flags & ~(SG_HIGHVM | SG_FVMLIB | SG_NORELOC | SG_PROTECTED_VERSION_1)), \
625 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
626 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
684 KLDRMODMACHO_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 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
694 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
695 fFileBits = 1; \
696 break; \
697 \
698 case S_SYMBOL_STUBS: \
699 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
700 /* reserved2 == stub size. 0 has been seen (corecrypto.kext) */ \
701 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_CHECK_RETURN(fOpenFlags & RTLDR_O_FOR_DEBUG, \
722 VERR_LDRMACHO_UNSUPPORTED_TERM_SECTION); \
723 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
724 KLDRMODMACHO_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 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
731 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
732 fFileBits = 1; \
733 break; \
734 \
735 case S_INTERPOSING: \
736 case S_GB_ZEROFILL: \
737 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNSUPPORTED_SECTION); \
738 \
739 default: \
740 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNKNOWN_SECTION); \
741 } \
742 KLDRMODMACHO_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 KLDRMODMACHO_CHECK_RETURN((pSect->flags & S_ATTR_DEBUG) == (pFirstSect->flags & S_ATTR_DEBUG), \
748 VERR_LDRMACHO_MIXED_DEBUG_SECTION_FLAGS); \
749 \
750 KLDRMODMACHO_CHECK_RETURN(pSect->addr - pSrcSeg->vmaddr <= pSrcSeg->vmsize, \
751 VERR_LDRMACHO_BAD_SECTION); \
752 KLDRMODMACHO_CHECK_RETURN( pSect->addr - pSrcSeg->vmaddr + pSect->size <= pSrcSeg->vmsize \
753 || !strcmp(pSrcSeg->segname, "__CTF") /* see above */, \
754 VERR_LDRMACHO_BAD_SECTION); \
755 KLDRMODMACHO_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 KLDRMODMACHO_CHECK_RETURN(!((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSect->addr), \
764 VERR_LDRMACHO_BAD_SECTION); \
765 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_CHECK_RETURN(pSect->offset == pSrcSeg->fileoff + offSect, \
784 VERR_LDRMACHO_NON_CONT_SEG_BITS); \
785 KLDRMODMACHO_CHECK_RETURN(pSect->offset - pSrcSeg->fileoff <= pSrcSeg->filesize, \
786 VERR_LDRMACHO_BAD_SECTION); \
787 } \
788 KLDRMODMACHO_CHECK_RETURN(pSect->offset <= cbFile, \
789 VERR_LDRMACHO_BAD_SECTION); \
790 KLDRMODMACHO_CHECK_RETURN((uint64_t)pSect->offset + pSect->size <= cbFile, \
791 VERR_LDRMACHO_BAD_SECTION); \
792 } \
793 else \
794 KLDRMODMACHO_CHECK_RETURN(pSect->offset == 0, VERR_LDRMACHO_BAD_SECTION); \
795 \
796 if (!pSect->nreloc) \
797 KLDRMODMACHO_CHECK_RETURN(!pSect->reloff, \
798 VERR_LDRMACHO_BAD_SECTION); \
799 else \
800 { \
801 KLDRMODMACHO_CHECK_RETURN(pSect->reloff <= cbFile, \
802 VERR_LDRMACHO_BAD_SECTION); \
803 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_64_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
856 KLDRMODMACHO_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 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
918 if ( u.pSymTab->stroff >= cbFile
919 || (uint64_t)u.pSymTab->stroff + u.pSymTab->strsize > cbFile)
920 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_FAILED_RETURN(RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_UNSUPPORTED_LOAD_COMMAND,
1029 "cmd=%#x", u.pLoadCmd->cmd));
1030
1031 default:
1032 KLDRMODMACHO_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 KLDRMODMACHO_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 KLDRMODMACHO_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(PKLDRMODMACHO 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 PKLDRMODMACHOSEG pDstSeg = &pThis->aSegments[0];
1087 PKLDRMODMACHOSECT pSectExtra = pThis->paSections;
1088 const uint32_t cSegments = pThis->cSegments;
1089 PKLDRMODMACHOSEG 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 KLDRMODMACHO_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 PKLDRMODMACHOSECT 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 cb = pDstSeg->SegInfo.RVA - uNextRVA;
1328 if (cb >= 0x00100000) /* 1MB */
1329 {
1330 pDstSeg->SegInfo.RVA = uNextRVA;
1331 //pThis->pMod->fFlags |= KLDRMOD_FLAGS_NON_CONTIGUOUS_LINK_ADDRS;
1332 }
1333 uNextRVA = pDstSeg->SegInfo.RVA + RTLDR_ALIGN_ADDR(pDstSeg->SegInfo.cb, pDstSeg->SegInfo.Alignment);
1334 }
1335
1336 /* Calculate the cbMapping members. */
1337 c = cSegmentsToAdjust;
1338 for (pDstSeg = &pThis->aSegments[0]; c-- > 1; pDstSeg++)
1339 {
1340
1341 cb = pDstSeg[1].SegInfo.RVA - pDstSeg->SegInfo.RVA;
1342 pDstSeg->SegInfo.cbMapped = (size_t)cb == cb ? (size_t)cb : ~(size_t)0;
1343 }
1344
1345 cb = RTLDR_ALIGN_ADDR(pDstSeg->SegInfo.cb, pDstSeg->SegInfo.Alignment);
1346 pDstSeg->SegInfo.cbMapped = (size_t)cb == cb ? (size_t)cb : ~(size_t)0;
1347
1348 /* Set the image size. */
1349 pThis->cbImage = pDstSeg->SegInfo.RVA + cb;
1350
1351 /* Fixup the section RVAs (internal). */
1352 c = cSegmentsToAdjust;
1353 uNextRVA = pThis->cbImage;
1354 pDstSeg = &pThis->aSegments[0];
1355 for (pSectExtraItr = pThis->paSections; pSectExtraItr != pSectExtra; pSectExtraItr++)
1356 {
1357 if (pSectExtraItr->iSegment < c)
1358 pSectExtraItr->RVA += pDstSeg[pSectExtraItr->iSegment].SegInfo.RVA;
1359 else
1360 {
1361 pSectExtraItr->RVA = uNextRVA;
1362 uNextRVA += RTLDR_ALIGN_ADDR(pSectExtraItr->cb, 64);
1363 }
1364 }
1365 }
1366
1367 /*
1368 * Make the GOT segment if necessary.
1369 */
1370 if (pThis->fMakeGot)
1371 {
1372 uint32_t cbPtr = ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1373 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1374 ? sizeof(uint32_t)
1375 : sizeof(uint64_t);
1376 uint32_t cbGot = pThis->cSymbols * cbPtr;
1377 uint32_t cbJmpStubs;
1378
1379 pThis->GotRVA = pThis->cbImage;
1380
1381 if (pThis->cbJmpStub)
1382 {
1383 cbGot = RT_ALIGN_Z(cbGot, 64);
1384 pThis->JmpStubsRVA = pThis->GotRVA + cbGot;
1385 cbJmpStubs = pThis->cbJmpStub * pThis->cSymbols;
1386 }
1387 else
1388 {
1389 pThis->JmpStubsRVA = NIL_RTLDRADDR;
1390 cbJmpStubs = 0;
1391 }
1392
1393 pDstSeg = &pThis->aSegments[cSegments - 1];
1394 pDstSeg->SegInfo.pszName = "GOT";
1395 pDstSeg->SegInfo.cchName = 3;
1396 pDstSeg->SegInfo.SelFlat = 0;
1397 pDstSeg->SegInfo.Sel16bit = 0;
1398 pDstSeg->SegInfo.fFlags = 0;
1399 pDstSeg->SegInfo.fProt = RTMEM_PROT_READ;
1400 pDstSeg->SegInfo.cb = cbGot + cbJmpStubs;
1401 pDstSeg->SegInfo.Alignment = 64;
1402 pDstSeg->SegInfo.LinkAddress = pThis->LinkAddress + pThis->GotRVA;
1403 pDstSeg->SegInfo.offFile = -1;
1404 pDstSeg->SegInfo.cbFile = -1;
1405 pDstSeg->SegInfo.RVA = pThis->GotRVA;
1406 pDstSeg->SegInfo.cbMapped = (size_t)RTLDR_ALIGN_ADDR(cbGot + cbJmpStubs, pDstSeg->SegInfo.Alignment);
1407
1408 pDstSeg->iOrgSegNo = UINT32_MAX;
1409 pDstSeg->cSections = 0;
1410 pDstSeg->paSections = NULL;
1411
1412 pThis->cbImage += pDstSeg->SegInfo.cbMapped;
1413 }
1414
1415 return VINF_SUCCESS;
1416}
1417
1418
1419/**
1420 * @interface_method_impl{RTLDROPS,pfnClose}
1421 */
1422static DECLCALLBACK(int) rtldrMachO_Close(PRTLDRMODINTERNAL pMod)
1423{
1424 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
1425 KLDRMODMACHO_ASSERT(!pThis->pvMapping);
1426
1427 uint32_t i = pThis->cSegments;
1428 while (i-- > 0)
1429 {
1430 uint32_t j = pThis->aSegments[i].cSections;
1431 while (j-- > 0)
1432 {
1433 RTMemFree(pThis->aSegments[i].paSections[j].paFixups);
1434 pThis->aSegments[i].paSections[j].paFixups = NULL;
1435 }
1436 }
1437
1438 RTMemFree(pThis->pbLoadCommands);
1439 pThis->pbLoadCommands = NULL;
1440 RTMemFree(pThis->pchStrings);
1441 pThis->pchStrings = NULL;
1442 RTMemFree(pThis->pvaSymbols);
1443 pThis->pvaSymbols = NULL;
1444
1445 return VINF_SUCCESS;
1446}
1447
1448
1449/**
1450 * Gets the right base address.
1451 *
1452 * @param pThis The interpreter module instance
1453 * @param pBaseAddress The base address, IN & OUT. Optional.
1454 */
1455static void kldrModMachOAdjustBaseAddress(PKLDRMODMACHO pThis, PRTLDRADDR pBaseAddress)
1456{
1457 /*
1458 * Adjust the base address.
1459 */
1460 if (*pBaseAddress == RTLDR_BASEADDRESS_LINK)
1461 *pBaseAddress = pThis->LinkAddress;
1462}
1463
1464
1465/**
1466 * Resolves a linker generated symbol.
1467 *
1468 * The Apple linker generates symbols indicating the start and end of sections
1469 * and segments. This function checks for these and returns the right value.
1470 *
1471 * @returns VINF_SUCCESS or VERR_SYMBOL_NOT_FOUND.
1472 * @param pThis The interpreter module instance.
1473 * @param pchSymbol The symbol.
1474 * @param cchSymbol The length of the symbol.
1475 * @param BaseAddress The base address to apply when calculating the
1476 * value.
1477 * @param puValue Where to return the symbol value.
1478 */
1479static int kldrModMachOQueryLinkerSymbol(PKLDRMODMACHO pThis, const char *pchSymbol, size_t cchSymbol,
1480 RTLDRADDR BaseAddress, PRTLDRADDR puValue)
1481{
1482 /*
1483 * Match possible name prefixes.
1484 */
1485 static const struct
1486 {
1487 const char *pszPrefix;
1488 uint32_t cchPrefix;
1489 bool fSection;
1490 bool fStart;
1491 } s_aPrefixes[] =
1492 {
1493 { "section$start$", (uint8_t)sizeof("section$start$") - 1, true, true },
1494 { "section$end$", (uint8_t)sizeof("section$end$") - 1, true, false},
1495 { "segment$start$", (uint8_t)sizeof("segment$start$") - 1, false, true },
1496 { "segment$end$", (uint8_t)sizeof("segment$end$") - 1, false, false},
1497 };
1498 size_t cchSectName = 0;
1499 const char *pchSectName = "";
1500 size_t cchSegName = 0;
1501 const char *pchSegName = NULL;
1502 uint32_t iPrefix = RT_ELEMENTS(s_aPrefixes) - 1;
1503 uint32_t iSeg;
1504 RTLDRADDR uValue;
1505
1506 for (;;)
1507 {
1508 uint8_t const cchPrefix = s_aPrefixes[iPrefix].cchPrefix;
1509 if ( cchSymbol > cchPrefix
1510 && strncmp(pchSymbol, s_aPrefixes[iPrefix].pszPrefix, cchPrefix) == 0)
1511 {
1512 pchSegName = pchSymbol + cchPrefix;
1513 cchSegName = cchSymbol - cchPrefix;
1514 break;
1515 }
1516
1517 /* next */
1518 if (!iPrefix)
1519 return VERR_SYMBOL_NOT_FOUND;
1520 iPrefix--;
1521 }
1522
1523 /*
1524 * Split the remainder into segment and section name, if necessary.
1525 */
1526 if (s_aPrefixes[iPrefix].fSection)
1527 {
1528 pchSectName = (const char *)memchr(pchSegName, '$', cchSegName);
1529 if (!pchSectName)
1530 return VERR_SYMBOL_NOT_FOUND;
1531 cchSegName = pchSectName - pchSegName;
1532 pchSectName++;
1533 cchSectName = cchSymbol - (pchSectName - pchSymbol);
1534 }
1535
1536 /*
1537 * Locate the segment.
1538 */
1539 if (!pThis->cSegments)
1540 return VERR_SYMBOL_NOT_FOUND;
1541 for (iSeg = 0; iSeg < pThis->cSegments; iSeg++)
1542 {
1543 if ( pThis->aSegments[iSeg].SegInfo.cchName >= cchSegName
1544 && memcmp(pThis->aSegments[iSeg].SegInfo.pszName, pchSegName, cchSegName) == 0)
1545 {
1546 section_32_t const *pSect;
1547 if ( pThis->aSegments[iSeg].SegInfo.cchName == cchSegName
1548 && pThis->Hdr.filetype != MH_OBJECT /* Good enough for __DWARF segs in MH_DHSYM, I hope. */)
1549 break;
1550
1551 pSect = (section_32_t *)pThis->aSegments[iSeg].paSections[0].pvMachoSection;
1552 if ( pThis->uEffFileType == MH_OBJECT
1553 && pThis->aSegments[iSeg].SegInfo.cchName > cchSegName + 1
1554 && pThis->aSegments[iSeg].SegInfo.pszName[cchSegName] == '.'
1555 && strncmp(&pThis->aSegments[iSeg].SegInfo.pszName[cchSegName + 1], pSect->sectname, sizeof(pSect->sectname)) == 0
1556 && pThis->aSegments[iSeg].SegInfo.cchName - cchSegName - 1 <= sizeof(pSect->sectname) )
1557 break;
1558 }
1559 }
1560 if (iSeg >= pThis->cSegments)
1561 return VERR_SYMBOL_NOT_FOUND;
1562
1563 if (!s_aPrefixes[iPrefix].fSection)
1564 {
1565 /*
1566 * Calculate the segment start/end address.
1567 */
1568 uValue = pThis->aSegments[iSeg].SegInfo.RVA;
1569 if (!s_aPrefixes[iPrefix].fStart)
1570 uValue += pThis->aSegments[iSeg].SegInfo.cb;
1571 }
1572 else
1573 {
1574 /*
1575 * Locate the section.
1576 */
1577 uint32_t iSect = pThis->aSegments[iSeg].cSections;
1578 if (!iSect)
1579 return VERR_SYMBOL_NOT_FOUND;
1580 for (;;)
1581 {
1582 section_32_t *pSect = (section_32_t *)pThis->aSegments[iSeg].paSections[iSect].pvMachoSection;
1583 if ( cchSectName <= sizeof(pSect->sectname)
1584 && memcmp(pSect->sectname, pchSectName, cchSectName) == 0
1585 && ( cchSectName == sizeof(pSect->sectname)
1586 || pSect->sectname[cchSectName] == '\0') )
1587 break;
1588 /* next */
1589 if (!iSect)
1590 return VERR_SYMBOL_NOT_FOUND;
1591 iSect--;
1592 }
1593
1594 uValue = pThis->aSegments[iSeg].paSections[iSect].RVA;
1595 if (!s_aPrefixes[iPrefix].fStart)
1596 uValue += pThis->aSegments[iSeg].paSections[iSect].cb;
1597 }
1598
1599 /*
1600 * Convert from RVA to load address.
1601 */
1602 uValue += BaseAddress;
1603 if (puValue)
1604 *puValue = uValue;
1605
1606 return VINF_SUCCESS;
1607}
1608
1609
1610/**
1611 * @interface_method_impl{RTLDROPS,pfnGetSymbolEx}
1612 */
1613static DECLCALLBACK(int) rtldrMachO_GetSymbolEx(PRTLDRMODINTERNAL pMod, const void *pvBits, RTUINTPTR BaseAddress,
1614 uint32_t iOrdinal, const char *pszSymbol, RTUINTPTR *pValue)
1615{
1616 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
1617 RT_NOREF(pvBits);
1618 //RT_NOREF(pszVersion);
1619 //RT_NOREF(pfnGetForwarder);
1620 //RT_NOREF(pvUser);
1621 uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
1622 uint32_t *pfKind = &fKind;
1623 size_t cchSymbol = pszSymbol ? strlen(pszSymbol) : 0;
1624
1625 /*
1626 * Resolve defaults.
1627 */
1628 kldrModMachOAdjustBaseAddress(pThis, &BaseAddress);
1629
1630 /*
1631 * Refuse segmented requests for now.
1632 */
1633 KLDRMODMACHO_CHECK_RETURN( !pfKind
1634 || (*pfKind & RTLDRSYMKIND_REQ_TYPE_MASK) == RTLDRSYMKIND_REQ_FLAT,
1635 VERR_LDRMACHO_TODO);
1636
1637 /*
1638 * Take action according to file type.
1639 */
1640 int rc;
1641 if ( pThis->Hdr.filetype == MH_OBJECT
1642 || pThis->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
1643 || pThis->Hdr.filetype == MH_DYLIB
1644 || pThis->Hdr.filetype == MH_BUNDLE
1645 || pThis->Hdr.filetype == MH_DSYM
1646 || pThis->Hdr.filetype == MH_KEXT_BUNDLE)
1647 {
1648 rc = kldrModMachOLoadObjSymTab(pThis);
1649 if (RT_SUCCESS(rc))
1650 {
1651 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1652 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1653 rc = kldrModMachODoQuerySymbol32Bit(pThis, (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols,
1654 pThis->pchStrings, pThis->cchStrings, BaseAddress, iOrdinal, pszSymbol,
1655 (uint32_t)cchSymbol, pValue, pfKind);
1656 else
1657 rc = kldrModMachODoQuerySymbol64Bit(pThis, (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols,
1658 pThis->pchStrings, pThis->cchStrings, BaseAddress, iOrdinal, pszSymbol,
1659 (uint32_t)cchSymbol, pValue, pfKind);
1660 }
1661
1662 /*
1663 * Check for link-editor generated symbols and supply what we can.
1664 *
1665 * As small service to clients that insists on adding a '_' prefix
1666 * before querying symbols, we will ignore the prefix.
1667 */
1668 if ( rc == VERR_SYMBOL_NOT_FOUND
1669 && cchSymbol > sizeof("section$end$") - 1
1670 && ( pszSymbol[0] == 's'
1671 || (pszSymbol[1] == 's' && pszSymbol[0] == '_') )
1672 && memchr(pszSymbol, '$', cchSymbol) )
1673 {
1674 if (pszSymbol[0] == '_')
1675 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol + 1, cchSymbol - 1, BaseAddress, pValue);
1676 else
1677 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, pValue);
1678 }
1679 }
1680 else
1681 rc = VERR_LDRMACHO_TODO;
1682
1683 return rc;
1684}
1685
1686
1687/**
1688 * Lookup a symbol in a 32-bit symbol table.
1689 *
1690 * @returns IPRT status code.
1691 * @param pThis
1692 * @param paSyms Pointer to the symbol table.
1693 * @param cSyms Number of symbols in the table.
1694 * @param pchStrings Pointer to the string table.
1695 * @param cchStrings Size of the string table.
1696 * @param BaseAddress Adjusted base address, see kLdrModQuerySymbol.
1697 * @param iSymbol See kLdrModQuerySymbol.
1698 * @param pchSymbol See kLdrModQuerySymbol.
1699 * @param cchSymbol See kLdrModQuerySymbol.
1700 * @param puValue See kLdrModQuerySymbol.
1701 * @param pfKind See kLdrModQuerySymbol.
1702 */
1703static int kldrModMachODoQuerySymbol32Bit(PKLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
1704 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol,
1705 const char *pchSymbol, uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind)
1706{
1707 /*
1708 * Find a valid symbol matching the search criteria.
1709 */
1710 if (iSymbol == UINT32_MAX)
1711 {
1712 /* simplify validation. */
1713 if (cchStrings <= cchSymbol)
1714 return VERR_SYMBOL_NOT_FOUND;
1715 cchStrings -= cchSymbol;
1716
1717 /* external symbols are usually at the end, so search the other way. */
1718 for (iSymbol = cSyms - 1; iSymbol != UINT32_MAX; iSymbol--)
1719 {
1720 const char *psz;
1721
1722 /* Skip irrellevant and non-public symbols. */
1723 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1724 continue;
1725 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1726 continue;
1727 if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
1728 continue;
1729 if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
1730 continue;
1731
1732 /* get name */
1733 if (!paSyms[iSymbol].n_un.n_strx)
1734 continue;
1735 if ((uint32_t)paSyms[iSymbol].n_un.n_strx >= cchStrings)
1736 continue;
1737 psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
1738 if (psz[cchSymbol])
1739 continue;
1740 if (memcmp(psz, pchSymbol, cchSymbol))
1741 continue;
1742
1743 /* match! */
1744 break;
1745 }
1746 if (iSymbol == UINT32_MAX)
1747 return VERR_SYMBOL_NOT_FOUND;
1748 }
1749 else
1750 {
1751 if (iSymbol >= cSyms)
1752 return VERR_SYMBOL_NOT_FOUND;
1753 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1754 return VERR_SYMBOL_NOT_FOUND;
1755 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1756 return VERR_SYMBOL_NOT_FOUND;
1757 }
1758
1759 /*
1760 * Calc the return values.
1761 */
1762 if (pfKind)
1763 {
1764 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1765 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1766 *pfKind = RTLDRSYMKIND_32BIT | RTLDRSYMKIND_NO_TYPE;
1767 else
1768 *pfKind = RTLDRSYMKIND_64BIT | RTLDRSYMKIND_NO_TYPE;
1769 if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
1770 *pfKind |= RTLDRSYMKIND_WEAK;
1771 }
1772
1773 switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
1774 {
1775 case MACHO_N_SECT:
1776 {
1777 PKLDRMODMACHOSECT pSect;
1778 RTLDRADDR offSect;
1779 KLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSymbol].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
1780 pSect = &pThis->paSections[paSyms[iSymbol].n_sect - 1];
1781
1782 offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
1783 KLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
1784 || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
1785 && offSect == 0U - pSect->RVA
1786 && pThis->uEffFileType != MH_OBJECT),
1787 VERR_LDRMACHO_BAD_SYMBOL);
1788 if (puValue)
1789 *puValue = BaseAddress + pSect->RVA + offSect;
1790
1791 if ( pfKind
1792 && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
1793 *pfKind = (*pfKind & ~RTLDRSYMKIND_TYPE_MASK) | RTLDRSYMKIND_CODE;
1794 break;
1795 }
1796
1797 case MACHO_N_ABS:
1798 if (puValue)
1799 *puValue = paSyms[iSymbol].n_value;
1800 /*if (pfKind)
1801 pfKind |= RTLDRSYMKIND_ABS;*/
1802 break;
1803
1804 case MACHO_N_PBUD:
1805 case MACHO_N_INDR:
1806 /** @todo implement indirect and prebound symbols. */
1807 default:
1808 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
1809 }
1810
1811 return VINF_SUCCESS;
1812}
1813
1814
1815/**
1816 * Lookup a symbol in a 64-bit symbol table.
1817 *
1818 * @returns IPRT status code.
1819 * @param pThis
1820 * @param paSyms Pointer to the symbol table.
1821 * @param cSyms Number of symbols in the table.
1822 * @param pchStrings Pointer to the string table.
1823 * @param cchStrings Size of the string table.
1824 * @param BaseAddress Adjusted base address, see kLdrModQuerySymbol.
1825 * @param iSymbol See kLdrModQuerySymbol.
1826 * @param pchSymbol See kLdrModQuerySymbol.
1827 * @param cchSymbol See kLdrModQuerySymbol.
1828 * @param puValue See kLdrModQuerySymbol.
1829 * @param pfKind See kLdrModQuerySymbol.
1830 */
1831static int kldrModMachODoQuerySymbol64Bit(PKLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
1832 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol,
1833 const char *pchSymbol, uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind)
1834{
1835 /*
1836 * Find a valid symbol matching the search criteria.
1837 */
1838 if (iSymbol == UINT32_MAX)
1839 {
1840 /* simplify validation. */
1841 if (cchStrings <= cchSymbol)
1842 return VERR_SYMBOL_NOT_FOUND;
1843 cchStrings -= cchSymbol;
1844
1845 /* external symbols are usually at the end, so search the other way. */
1846 for (iSymbol = cSyms - 1; iSymbol != UINT32_MAX; iSymbol--)
1847 {
1848 const char *psz;
1849
1850 /* Skip irrellevant and non-public symbols. */
1851 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1852 continue;
1853 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1854 continue;
1855 if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
1856 continue;
1857 if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
1858 continue;
1859
1860 /* get name */
1861 if (!paSyms[iSymbol].n_un.n_strx)
1862 continue;
1863 if ((uint32_t)paSyms[iSymbol].n_un.n_strx >= cchStrings)
1864 continue;
1865 psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
1866 if (psz[cchSymbol])
1867 continue;
1868 if (memcmp(psz, pchSymbol, cchSymbol))
1869 continue;
1870
1871 /* match! */
1872 break;
1873 }
1874 if (iSymbol == UINT32_MAX)
1875 return VERR_SYMBOL_NOT_FOUND;
1876 }
1877 else
1878 {
1879 if (iSymbol >= cSyms)
1880 return VERR_SYMBOL_NOT_FOUND;
1881 if (paSyms[iSymbol].n_type & MACHO_N_STAB)
1882 return VERR_SYMBOL_NOT_FOUND;
1883 if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
1884 return VERR_SYMBOL_NOT_FOUND;
1885 }
1886
1887 /*
1888 * Calc the return values.
1889 */
1890 if (pfKind)
1891 {
1892 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1893 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1894 *pfKind = RTLDRSYMKIND_32BIT | RTLDRSYMKIND_NO_TYPE;
1895 else
1896 *pfKind = RTLDRSYMKIND_64BIT | RTLDRSYMKIND_NO_TYPE;
1897 if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
1898 *pfKind |= RTLDRSYMKIND_WEAK;
1899 }
1900
1901 switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
1902 {
1903 case MACHO_N_SECT:
1904 {
1905 PKLDRMODMACHOSECT pSect;
1906 RTLDRADDR offSect;
1907 KLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSymbol].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
1908 pSect = &pThis->paSections[paSyms[iSymbol].n_sect - 1];
1909
1910 offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
1911 KLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
1912 || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
1913 && offSect == 0U - pSect->RVA
1914 && pThis->uEffFileType != MH_OBJECT),
1915 VERR_LDRMACHO_BAD_SYMBOL);
1916 if (puValue)
1917 *puValue = BaseAddress + pSect->RVA + offSect;
1918
1919 if ( pfKind
1920 && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
1921 *pfKind = (*pfKind & ~RTLDRSYMKIND_TYPE_MASK) | RTLDRSYMKIND_CODE;
1922 break;
1923 }
1924
1925 case MACHO_N_ABS:
1926 if (puValue)
1927 *puValue = paSyms[iSymbol].n_value;
1928 /*if (pfKind)
1929 pfKind |= RTLDRSYMKIND_ABS;*/
1930 break;
1931
1932 case MACHO_N_PBUD:
1933 case MACHO_N_INDR:
1934 /** @todo implement indirect and prebound symbols. */
1935 default:
1936 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
1937 }
1938
1939 return VINF_SUCCESS;
1940}
1941
1942
1943/**
1944 * @interface_method_impl{RTLDROPS,pfnEnumSymbols}
1945 */
1946static DECLCALLBACK(int) rtldrMachO_EnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFlags, const void *pvBits,
1947 RTUINTPTR BaseAddress, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
1948{
1949 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
1950 RT_NOREF(pvBits);
1951
1952 /*
1953 * Resolve defaults.
1954 */
1955 kldrModMachOAdjustBaseAddress(pThis, &BaseAddress);
1956
1957 /*
1958 * Take action according to file type.
1959 */
1960 int rc;
1961 if ( pThis->Hdr.filetype == MH_OBJECT
1962 || pThis->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
1963 || pThis->Hdr.filetype == MH_DYLIB
1964 || pThis->Hdr.filetype == MH_BUNDLE
1965 || pThis->Hdr.filetype == MH_DSYM
1966 || pThis->Hdr.filetype == MH_KEXT_BUNDLE)
1967 {
1968 rc = kldrModMachOLoadObjSymTab(pThis);
1969 if (RT_SUCCESS(rc))
1970 {
1971 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1972 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1973 rc = kldrModMachODoEnumSymbols32Bit(pThis, (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols,
1974 pThis->pchStrings, pThis->cchStrings, BaseAddress,
1975 fFlags, pfnCallback, pvUser);
1976 else
1977 rc = kldrModMachODoEnumSymbols64Bit(pThis, (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols,
1978 pThis->pchStrings, pThis->cchStrings, BaseAddress,
1979 fFlags, pfnCallback, pvUser);
1980 }
1981 }
1982 else
1983 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
1984
1985 return rc;
1986}
1987
1988
1989/**
1990 * Enum a 32-bit symbol table.
1991 *
1992 * @returns IPRT status code.
1993 * @param pThis
1994 * @param paSyms Pointer to the symbol table.
1995 * @param cSyms Number of symbols in the table.
1996 * @param pchStrings Pointer to the string table.
1997 * @param cchStrings Size of the string table.
1998 * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols.
1999 * @param fFlags See kLdrModEnumSymbols.
2000 * @param pfnCallback See kLdrModEnumSymbols.
2001 * @param pvUser See kLdrModEnumSymbols.
2002 */
2003static int kldrModMachODoEnumSymbols32Bit(PKLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
2004 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
2005 uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
2006{
2007 const uint32_t fKindBase = pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
2008 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
2009 ? RTLDRSYMKIND_32BIT : RTLDRSYMKIND_64BIT;
2010 uint32_t iSym;
2011 int rc;
2012
2013 /*
2014 * Iterate the symbol table.
2015 */
2016 for (iSym = 0; iSym < cSyms; iSym++)
2017 {
2018 uint32_t fKind;
2019 RTLDRADDR uValue;
2020 const char *psz;
2021 size_t cch;
2022
2023 /* Skip debug symbols and undefined symbols. */
2024 if (paSyms[iSym].n_type & MACHO_N_STAB)
2025 continue;
2026 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2027 continue;
2028
2029 /* Skip non-public symbols unless they are requested explicitly. */
2030 if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_ALL))
2031 {
2032 if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
2033 continue;
2034 if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
2035 continue;
2036 if (!paSyms[iSym].n_un.n_strx)
2037 continue;
2038 }
2039
2040 /*
2041 * Gather symbol info
2042 */
2043
2044 /* name */
2045 KLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2046 psz = &pchStrings[paSyms[iSym].n_un.n_strx];
2047 cch = strlen(psz);
2048 if (!cch)
2049 psz = NULL;
2050
2051 /* kind & value */
2052 fKind = fKindBase;
2053 if (paSyms[iSym].n_desc & N_WEAK_DEF)
2054 fKind |= RTLDRSYMKIND_WEAK;
2055 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
2056 {
2057 case MACHO_N_SECT:
2058 {
2059 PKLDRMODMACHOSECT pSect;
2060 KLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSym].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2061 pSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
2062
2063 uValue = paSyms[iSym].n_value - pSect->LinkAddress;
2064 KLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
2065 || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
2066 && uValue == 0U - pSect->RVA
2067 && pThis->uEffFileType != MH_OBJECT),
2068 VERR_LDRMACHO_BAD_SYMBOL);
2069 uValue += BaseAddress + pSect->RVA;
2070
2071 if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
2072 fKind |= RTLDRSYMKIND_CODE;
2073 else
2074 fKind |= RTLDRSYMKIND_NO_TYPE;
2075 break;
2076 }
2077
2078 case MACHO_N_ABS:
2079 uValue = paSyms[iSym].n_value;
2080 fKind |= RTLDRSYMKIND_NO_TYPE /*RTLDRSYMKIND_ABS*/;
2081 break;
2082
2083 case MACHO_N_PBUD:
2084 case MACHO_N_INDR:
2085 /** @todo implement indirect and prebound symbols. */
2086 default:
2087 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2088 }
2089
2090 /*
2091 * Do callback.
2092 */
2093 rc = pfnCallback(&pThis->Core, psz, iSym, uValue/*, fKind*/, pvUser);
2094 if (rc != VINF_SUCCESS)
2095 return rc;
2096 }
2097 return VINF_SUCCESS;
2098}
2099
2100
2101/**
2102 * Enum a 64-bit symbol table.
2103 *
2104 * @returns IPRT status code.
2105 * @param pThis
2106 * @param paSyms Pointer to the symbol table.
2107 * @param cSyms Number of symbols in the table.
2108 * @param pchStrings Pointer to the string table.
2109 * @param cchStrings Size of the string table.
2110 * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols.
2111 * @param fFlags See kLdrModEnumSymbols.
2112 * @param pfnCallback See kLdrModEnumSymbols.
2113 * @param pvUser See kLdrModEnumSymbols.
2114 */
2115static int kldrModMachODoEnumSymbols64Bit(PKLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
2116 const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
2117 uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
2118{
2119 const uint32_t fKindBase = pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
2120 || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE
2121 ? RTLDRSYMKIND_64BIT : RTLDRSYMKIND_32BIT;
2122 uint32_t iSym;
2123 int rc;
2124
2125 /*
2126 * Iterate the symbol table.
2127 */
2128 for (iSym = 0; iSym < cSyms; iSym++)
2129 {
2130 uint32_t fKind;
2131 RTLDRADDR uValue;
2132 const char *psz;
2133 size_t cch;
2134
2135 /* Skip debug symbols and undefined symbols. */
2136 if (paSyms[iSym].n_type & MACHO_N_STAB)
2137 continue;
2138 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2139 continue;
2140
2141 /* Skip non-public symbols unless they are requested explicitly. */
2142 if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_ALL))
2143 {
2144 if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
2145 continue;
2146 if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
2147 continue;
2148 if (!paSyms[iSym].n_un.n_strx)
2149 continue;
2150 }
2151
2152 /*
2153 * Gather symbol info
2154 */
2155
2156 /* name */
2157 KLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2158 psz = &pchStrings[paSyms[iSym].n_un.n_strx];
2159 cch = strlen(psz);
2160 if (!cch)
2161 psz = NULL;
2162
2163 /* kind & value */
2164 fKind = fKindBase;
2165 if (paSyms[iSym].n_desc & N_WEAK_DEF)
2166 fKind |= RTLDRSYMKIND_WEAK;
2167 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
2168 {
2169 case MACHO_N_SECT:
2170 {
2171 PKLDRMODMACHOSECT pSect;
2172 KLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSym].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2173 pSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
2174
2175 uValue = paSyms[iSym].n_value - pSect->LinkAddress;
2176 KLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
2177 || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
2178 && uValue == 0U - pSect->RVA
2179 && pThis->uEffFileType != MH_OBJECT),
2180 VERR_LDRMACHO_BAD_SYMBOL);
2181 uValue += BaseAddress + pSect->RVA;
2182
2183 if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
2184 fKind |= RTLDRSYMKIND_CODE;
2185 else
2186 fKind |= RTLDRSYMKIND_NO_TYPE;
2187 break;
2188 }
2189
2190 case MACHO_N_ABS:
2191 uValue = paSyms[iSym].n_value;
2192 fKind |= RTLDRSYMKIND_NO_TYPE /*RTLDRSYMKIND_ABS*/;
2193 break;
2194
2195 case MACHO_N_PBUD:
2196 case MACHO_N_INDR:
2197 /** @todo implement indirect and prebound symbols. */
2198 default:
2199 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2200 }
2201
2202 /*
2203 * Do callback.
2204 */
2205 rc = pfnCallback(&pThis->Core, psz, iSym, uValue/*, fKind*/, pvUser);
2206 if (rc != VINF_SUCCESS)
2207 return rc;
2208 }
2209 return VINF_SUCCESS;
2210}
2211
2212#if 0
2213
2214/** @copydoc kLdrModGetImport */
2215static int kldrModMachOGetImport(PRTLDRMODINTERNAL pMod, const void *pvBits, uint32_t iImport, char *pszName, size_t cchName)
2216{
2217 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2218 RT_NOREF(pvBits);
2219 RT_NOREF(iImport);
2220 RT_NOREF(pszName);
2221 RT_NOREF(cchName);
2222
2223 if (pThis->Hdr.filetype == MH_OBJECT)
2224 return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
2225
2226 /* later */
2227 return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
2228}
2229
2230
2231
2232/** @copydoc kLdrModNumberOfImports */
2233static int32_t kldrModMachONumberOfImports(PRTLDRMODINTERNAL pMod, const void *pvBits)
2234{
2235 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2236 RT_NOREF(pvBits);
2237
2238 if (pThis->Hdr.filetype == MH_OBJECT)
2239 return VINF_SUCCESS;
2240
2241 /* later */
2242 return VINF_SUCCESS;
2243}
2244
2245
2246/** @copydoc kLdrModGetStackInfo */
2247static int kldrModMachOGetStackInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
2248{
2249 /*PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);*/
2250 RT_NOREF(pMod);
2251 RT_NOREF(pvBits);
2252 RT_NOREF(BaseAddress);
2253
2254 pStackInfo->Address = NIL_RTLDRADDR;
2255 pStackInfo->LinkAddress = NIL_RTLDRADDR;
2256 pStackInfo->cbStack = pStackInfo->cbStackThread = 0;
2257 /* later */
2258
2259 return VINF_SUCCESS;
2260}
2261
2262
2263/** @copydoc kLdrModQueryMainEntrypoint */
2264static int kldrModMachOQueryMainEntrypoint(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PRTLDRADDR pMainEPAddress)
2265{
2266#if 0
2267 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2268 int rc;
2269
2270 /*
2271 * Resolve base address alias if any.
2272 */
2273 rc = kldrModMachOBitsAndBaseAddress(pThis, NULL, &BaseAddress);
2274 if (RT_FAILURE(rc))
2275 return rc;
2276
2277 /*
2278 * Convert the address from the header.
2279 */
2280 *pMainEPAddress = pThis->Hdrs.OptionalHeader.AddressOfEntryPoint
2281 ? BaseAddress + pThis->Hdrs.OptionalHeader.AddressOfEntryPoint
2282 : NIL_RTLDRADDR;
2283#else
2284 *pMainEPAddress = NIL_RTLDRADDR;
2285 RT_NOREF(pvBits);
2286 RT_NOREF(BaseAddress);
2287 RT_NOREF(pMod);
2288#endif
2289 return VINF_SUCCESS;
2290}
2291
2292#endif
2293
2294
2295/**
2296 * @interface_method_impl{RTLDROPS,pfnEnumDbgInfo}
2297 */
2298static DECLCALLBACK(int) rtldrMachO_EnumDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, PFNRTLDRENUMDBG pfnCallback, void *pvUser)
2299{
2300 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2301 int rc = VINF_SUCCESS;
2302 uint32_t iSect;
2303 RT_NOREF(pvBits);
2304
2305 for (iSect = 0; iSect < pThis->cSections; iSect++)
2306 {
2307 /* (32-bit & 64-bit starts the same way) */
2308 section_32_t *pMachOSect = (section_32_t *)pThis->paSections[iSect].pvMachoSection;
2309 char szTmp[sizeof(pMachOSect->sectname) + 1];
2310
2311 if (strcmp(pMachOSect->segname, "__DWARF"))
2312 continue;
2313
2314 memcpy(szTmp, pMachOSect->sectname, sizeof(pMachOSect->sectname));
2315 szTmp[sizeof(pMachOSect->sectname)] = '\0';
2316
2317 RTLDRDBGINFO DbgInfo;
2318 DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF;
2319 DbgInfo.iDbgInfo = iSect;
2320 DbgInfo.LinkAddress = pThis->paSections[iSect].LinkAddress;
2321 DbgInfo.cb = pThis->paSections[iSect].cb;
2322 DbgInfo.pszExtFile = NULL;
2323 DbgInfo.u.Dwarf.pszSection = szTmp;
2324 rc = pfnCallback(&pThis->Core, &DbgInfo, pvUser);
2325 if (rc != VINF_SUCCESS)
2326 break;
2327 }
2328
2329 return rc;
2330}
2331
2332#if 0
2333
2334/** @copydoc kLdrModHasDbgInfo */
2335static int kldrModMachOHasDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits)
2336{
2337 /*PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);*/
2338
2339#if 0
2340 /*
2341 * Base this entirely on the presence of a debug directory.
2342 */
2343 if ( pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size
2344 < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */
2345 || !pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)
2346 return KLDR_ERR_NO_DEBUG_INFO;
2347 return VINF_SUCCESS;
2348#else
2349 RT_NOREF(pMod);
2350 RT_NOREF(pvBits);
2351 return VERR_LDR_NO_DEBUG_INFO;
2352#endif
2353}
2354
2355
2356/** @copydoc kLdrModMap */
2357static int kldrModMachOMap(PRTLDRMODINTERNAL pMod)
2358{
2359 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2360 unsigned fFixed;
2361 uint32_t i;
2362 void *pvBase;
2363 int rc;
2364
2365 if (!pThis->fCanLoad)
2366 return VERR_LDRMACHO_TODO;
2367
2368 /*
2369 * Already mapped?
2370 */
2371 if (pThis->pvMapping)
2372 return KLDR_ERR_ALREADY_MAPPED;
2373
2374 /*
2375 * Map it.
2376 */
2377 /* fixed image? */
2378 fFixed = pMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2379 || pMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2380 if (!fFixed)
2381 pvBase = NULL;
2382 else
2383 {
2384 pvBase = (void *)(uintptr_t)pMod->aSegments[0].LinkAddress;
2385 if ((uintptr_t)pvBase != pMod->aSegments[0].LinkAddress)
2386 return VERR_LDR_ADDRESS_OVERFLOW;
2387 }
2388
2389 /* try do the prepare */
2390 rc = kRdrMap(pMod->pRdr, &pvBase, pMod->cSegments, pMod->aSegments, fFixed);
2391 if (RT_FAILURE(rc))
2392 return rc;
2393
2394 /*
2395 * Update the segments with their map addresses.
2396 */
2397 for (i = 0; i < pMod->cSegments; i++)
2398 {
2399 if (pMod->aSegments[i].RVA != NIL_RTLDRADDR)
2400 pMod->aSegments[i].MapAddress = (uintptr_t)pvBase + (uintptr_t)pMod->aSegments[i].RVA;
2401 }
2402 pThis->pvMapping = pvBase;
2403
2404 return VINF_SUCCESS;
2405}
2406
2407
2408/** @copydoc kLdrModUnmap */
2409static int kldrModMachOUnmap(PRTLDRMODINTERNAL pMod)
2410{
2411 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2412 uint32_t i;
2413 int rc;
2414
2415 /*
2416 * Mapped?
2417 */
2418 if (!pThis->pvMapping)
2419 return KLDR_ERR_NOT_MAPPED;
2420
2421 /*
2422 * Try unmap the image.
2423 */
2424 rc = kRdrUnmap(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
2425 if (RT_FAILURE(rc))
2426 return rc;
2427
2428 /*
2429 * Update the segments to reflect that they aren't mapped any longer.
2430 */
2431 pThis->pvMapping = NULL;
2432 for (i = 0; i < pMod->cSegments; i++)
2433 pMod->aSegments[i].MapAddress = 0;
2434
2435 return VINF_SUCCESS;
2436}
2437
2438
2439/** @copydoc kLdrModAllocTLS */
2440static int kldrModMachOAllocTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
2441{
2442 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2443
2444 /*
2445 * Mapped?
2446 */
2447 if ( pvMapping == KLDRMOD_INT_MAP
2448 && !pThis->pvMapping )
2449 return KLDR_ERR_NOT_MAPPED;
2450 return VINF_SUCCESS;
2451}
2452
2453
2454/** @copydoc kLdrModFreeTLS */
2455static void kldrModMachOFreeTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
2456{
2457 RT_NOREF(pMod);
2458 RT_NOREF(pvMapping);
2459}
2460
2461
2462
2463/** @copydoc kLdrModReload */
2464static int kldrModMachOReload(PRTLDRMODINTERNAL pMod)
2465{
2466 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2467
2468 /*
2469 * Mapped?
2470 */
2471 if (!pThis->pvMapping)
2472 return KLDR_ERR_NOT_MAPPED;
2473
2474 /* the file provider does it all */
2475 return kRdrRefresh(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
2476}
2477
2478
2479/** @copydoc kLdrModFixupMapping */
2480static int kldrModMachOFixupMapping(PRTLDRMODINTERNAL pMod, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
2481{
2482 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2483 int rc, rc2;
2484
2485 /*
2486 * Mapped?
2487 */
2488 if (!pThis->pvMapping)
2489 return KLDR_ERR_NOT_MAPPED;
2490
2491 /*
2492 * Before doing anything we'll have to make all pages writable.
2493 */
2494 rc = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */);
2495 if (RT_FAILURE(rc))
2496 return rc;
2497
2498 /*
2499 * Resolve imports and apply base relocations.
2500 */
2501 rc = rtldrMachO_RelocateBits(pMod, pThis->pvMapping, (uintptr_t)pThis->pvMapping, pThis->LinkAddress,
2502 pfnGetImport, pvUser);
2503
2504 /*
2505 * Restore protection.
2506 */
2507 rc2 = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */);
2508 if (RT_SUCCESS(rc) && RT_FAILURE(rc2)
2509 rc = rc2;
2510 return rc;
2511}
2512
2513#endif
2514
2515/**
2516 * MH_OBJECT: Resolves undefined symbols (imports).
2517 *
2518 * @returns IPRT status code.
2519 * @param pThis The Mach-O module interpreter instance.
2520 * @param BaseAddress The module base address.
2521 * @param pfnGetImport The callback for resolving an imported symbol.
2522 * @param pvUser User argument to the callback.
2523 */
2524static int kldrModMachOObjDoImports(PKLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
2525{
2526 const uint32_t cSyms = pThis->cSymbols;
2527 uint32_t iSym;
2528
2529 /*
2530 * Ensure that we've got the symbol table and section fixups handy.
2531 */
2532 int rc = kldrModMachOLoadObjSymTab(pThis);
2533 if (RT_FAILURE(rc))
2534 return rc;
2535
2536 /*
2537 * Iterate the symbol table and resolve undefined symbols.
2538 * We currently ignore REFERENCE_TYPE.
2539 */
2540 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
2541 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
2542 {
2543 macho_nlist_32_t *paSyms = (macho_nlist_32_t *)pThis->pvaSymbols;
2544 for (iSym = 0; iSym < cSyms; iSym++)
2545 {
2546 /* skip stabs */
2547 if (paSyms[iSym].n_type & MACHO_N_STAB)
2548 continue;
2549
2550 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2551 {
2552 const char *pszSymbol;
2553 size_t cchSymbol;
2554 //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
2555 RTLDRADDR Value = NIL_RTLDRADDR;
2556
2557 /** @todo Implement N_REF_TO_WEAK. */
2558 KLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
2559
2560 /* Get the symbol name. */
2561 KLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2562 pszSymbol = &pThis->pchStrings[paSyms[iSym].n_un.n_strx];
2563 cchSymbol = strlen(pszSymbol);
2564
2565 /* Check for linker defined symbols relating to sections and segments. */
2566 if ( cchSymbol > sizeof("section$end$") - 1
2567 && *pszSymbol == 's'
2568 && memchr(pszSymbol, '$', cchSymbol))
2569 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
2570 else
2571 rc = VERR_SYMBOL_NOT_FOUND;
2572
2573 /* Ask the user for an address to the symbol. */
2574 if (RT_FAILURE_NP(rc))
2575 rc = pfnGetImport(&pThis->Core, NULL /*pszModule*/, pszSymbol, iSym, &Value/*, &fKind*/, pvUser);
2576 if (RT_FAILURE(rc))
2577 {
2578 /* weak reference? */
2579 if (!(paSyms[iSym].n_desc & N_WEAK_REF))
2580 break;
2581 Value = 0;
2582 }
2583
2584 /* Update the symbol. */
2585 paSyms[iSym].n_value = (uint32_t)Value;
2586 if (paSyms[iSym].n_value != Value)
2587 {
2588 rc = VERR_LDR_ADDRESS_OVERFLOW;
2589 break;
2590 }
2591 }
2592 else if (paSyms[iSym].n_desc & N_WEAK_DEF)
2593 {
2594 /** @todo implement weak symbols. */
2595 /*return VERR_LDRMACHO_TODO; - ignored for now. */
2596 }
2597 }
2598 }
2599 else
2600 {
2601 /* (Identical to the 32-bit code, just different paSym type. (and n_strx is unsigned)) */
2602 macho_nlist_64_t *paSyms = (macho_nlist_64_t *)pThis->pvaSymbols;
2603 for (iSym = 0; iSym < cSyms; iSym++)
2604 {
2605 /* skip stabs */
2606 if (paSyms[iSym].n_type & MACHO_N_STAB)
2607 continue;
2608
2609 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2610 {
2611 const char *pszSymbol;
2612 size_t cchSymbol;
2613 //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
2614 RTLDRADDR Value = NIL_RTLDRADDR;
2615
2616 /** @todo Implement N_REF_TO_WEAK. */
2617 KLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
2618
2619 /* Get the symbol name. */
2620 KLDRMODMACHO_CHECK_RETURN(paSyms[iSym].n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2621 pszSymbol = &pThis->pchStrings[paSyms[iSym].n_un.n_strx];
2622 cchSymbol = strlen(pszSymbol);
2623
2624 /* Check for linker defined symbols relating to sections and segments. */
2625 if ( cchSymbol > sizeof("section$end$") - 1
2626 && *pszSymbol == 's'
2627 && memchr(pszSymbol, '$', cchSymbol))
2628 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
2629 else
2630 rc = VERR_SYMBOL_NOT_FOUND;
2631
2632 /* Ask the user for an address to the symbol. */
2633 if (RT_FAILURE_NP(rc))
2634 rc = pfnGetImport(&pThis->Core, NULL, pszSymbol, iSym, &Value, /*&fKind,*/ pvUser);
2635 if (RT_FAILURE(rc))
2636 {
2637 /* weak reference? */
2638 if (!(paSyms[iSym].n_desc & N_WEAK_REF))
2639 break;
2640 Value = 0;
2641 }
2642
2643 /* Update the symbol. */
2644 paSyms[iSym].n_value = Value;
2645 if (paSyms[iSym].n_value != Value)
2646 {
2647 rc = VERR_LDR_ADDRESS_OVERFLOW;
2648 break;
2649 }
2650 }
2651 else if (paSyms[iSym].n_desc & N_WEAK_DEF)
2652 {
2653 /** @todo implement weak symbols. */
2654 /*return VERR_LDRMACHO_TODO; - ignored for now. */
2655 }
2656 }
2657 }
2658
2659 return rc;
2660}
2661
2662
2663/**
2664 * MH_OBJECT: Applies base relocations to a (unprotected) image mapping.
2665 *
2666 * @returns IPRT status code.
2667 * @param pThis The Mach-O module interpreter instance.
2668 * @param pvMapping The mapping to fixup.
2669 * @param NewBaseAddress The address to fixup the mapping to.
2670 */
2671static int kldrModMachOObjDoFixups(PKLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress)
2672{
2673 /*
2674 * Ensure that we've got the symbol table and section fixups handy.
2675 */
2676 int rc = kldrModMachOLoadObjSymTab(pThis);
2677 if (RT_FAILURE(rc))
2678 return rc;
2679
2680 /*
2681 * Iterate over the segments and their sections and apply fixups.
2682 */
2683 rc = VINF_SUCCESS;
2684 for (uint32_t iSeg = 0; RT_SUCCESS(rc) && iSeg < pThis->cSegments; iSeg++)
2685 {
2686 PKLDRMODMACHOSEG pSeg = &pThis->aSegments[iSeg];
2687 for (uint32_t iSect = 0; iSect < pSeg->cSections; iSect++)
2688 {
2689 PKLDRMODMACHOSECT pSect = &pSeg->paSections[iSect];
2690
2691 /* skip sections without fixups. */
2692 if (!pSect->cFixups)
2693 continue;
2694
2695 /* lazy load (and endian convert) the fixups. */
2696 if (!pSect->paFixups)
2697 {
2698 rc = kldrModMachOLoadFixups(pThis, pSect->offFixups, pSect->cFixups, &pSect->paFixups);
2699 if (RT_FAILURE(rc))
2700 break;
2701 }
2702
2703 /*
2704 * Apply the fixups.
2705 */
2706 uint8_t *pbSectBits = (uint8_t *)pvMapping + (uintptr_t)pSect->RVA;
2707 if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE) /** @todo this aint right. */
2708 rc = kldrModMachOFixupSectionGeneric32Bit(pThis, pbSectBits, pSect,
2709 (macho_nlist_32_t *)pThis->pvaSymbols,
2710 pThis->cSymbols, NewBaseAddress);
2711 else if ( pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
2712 && pThis->Hdr.cputype == CPU_TYPE_X86_64)
2713 rc = kldrModMachOFixupSectionAMD64(pThis, pbSectBits, pSect,
2714 (macho_nlist_64_t *)pThis->pvaSymbols,
2715 pThis->cSymbols, NewBaseAddress);
2716 else
2717 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2718 if (RT_FAILURE(rc))
2719 break;
2720 }
2721 }
2722
2723 return rc;
2724}
2725
2726
2727/**
2728 * Applies generic fixups to a section in an image of the same endian-ness
2729 * as the host CPU.
2730 *
2731 * @returns IPRT status code.
2732 * @param pThis The Mach-O module interpreter instance.
2733 * @param pbSectBits Pointer to the section bits.
2734 * @param pFixupSect The section being fixed up.
2735 * @param paSyms Pointer to the symbol table.
2736 * @param cSyms Number of symbols.
2737 * @param NewBaseAddress The new base image address.
2738 */
2739static int kldrModMachOFixupSectionGeneric32Bit(PKLDRMODMACHO pThis, uint8_t *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
2740 macho_nlist_32_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
2741{
2742 const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
2743 const uint32_t cFixups = pFixupSect->cFixups;
2744 size_t cbSectBits = (size_t)pFixupSect->cb;
2745 const uint8_t *pbSectVirginBits;
2746 uint32_t iFixup;
2747 RTLDRADDR SymAddr = ~(RTLDRADDR)0;
2748 int rc;
2749
2750 /*
2751 * Find the virgin bits.
2752 */
2753 if (pFixupSect->offFile != -1)
2754 {
2755 rc = kldrModMachOMapVirginBits(pThis);
2756 if (RT_FAILURE(rc))
2757 return rc;
2758 pbSectVirginBits = (const uint8_t *)pThis->pvBits + pFixupSect->offFile;
2759 }
2760 else
2761 pbSectVirginBits = NULL;
2762
2763 /*
2764 * Iterate the fixups and apply them.
2765 */
2766 for (iFixup = 0; iFixup < cFixups; iFixup++)
2767 {
2768 RTPTRUNION uFix;
2769 RTPTRUNION uFixVirgin;
2770 union
2771 {
2772 macho_relocation_info_t r;
2773 scattered_relocation_info_t s;
2774 } Fixup;
2775 Fixup.r = paFixups[iFixup];
2776
2777 if (!(Fixup.r.r_address & R_SCATTERED))
2778 {
2779 /* sanity */
2780 if ((uint32_t)Fixup.r.r_address >= cbSectBits)
2781 return VERR_LDR_BAD_FIXUP;
2782
2783 /* calc fixup addresses. */
2784 uFix.pv = pbSectBits + Fixup.r.r_address;
2785 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.r.r_address : 0;
2786
2787 /*
2788 * Calc the symbol value.
2789 */
2790 /* Calc the linked symbol address / addend. */
2791 switch (Fixup.r.r_length)
2792 {
2793 /** @todo Deal with unaligned accesses on non x86 platforms. */
2794 case 0: SymAddr = *uFixVirgin.pi8; break;
2795 case 1: SymAddr = *uFixVirgin.pi16; break;
2796 case 2: SymAddr = *uFixVirgin.pi32; break;
2797 case 3: SymAddr = *uFixVirgin.pi64; break;
2798 }
2799 if (Fixup.r.r_pcrel)
2800 SymAddr += Fixup.r.r_address + pFixupSect->LinkAddress;
2801
2802 /* Add symbol / section address. */
2803 if (Fixup.r.r_extern)
2804 {
2805 const macho_nlist_32_t *pSym;
2806 if (Fixup.r.r_symbolnum >= cSyms)
2807 return VERR_LDR_BAD_FIXUP;
2808 pSym = &paSyms[Fixup.r.r_symbolnum];
2809
2810 if (pSym->n_type & MACHO_N_STAB)
2811 return VERR_LDR_BAD_FIXUP;
2812
2813 switch (pSym->n_type & MACHO_N_TYPE)
2814 {
2815 case MACHO_N_SECT:
2816 {
2817 PKLDRMODMACHOSECT pSymSect;
2818 KLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2819 pSymSect = &pThis->paSections[pSym->n_sect - 1];
2820
2821 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
2822 break;
2823 }
2824
2825 case MACHO_N_UNDF:
2826 case MACHO_N_ABS:
2827 SymAddr += pSym->n_value;
2828 break;
2829
2830 case MACHO_N_INDR:
2831 case MACHO_N_PBUD:
2832 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2833 default:
2834 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
2835 }
2836 }
2837 else if (Fixup.r.r_symbolnum != R_ABS)
2838 {
2839 PKLDRMODMACHOSECT pSymSect;
2840 if (Fixup.r.r_symbolnum > pThis->cSections)
2841 return VERR_LDR_BAD_FIXUP;
2842 pSymSect = &pThis->paSections[Fixup.r.r_symbolnum - 1];
2843
2844 SymAddr -= pSymSect->LinkAddress;
2845 SymAddr += pSymSect->RVA + NewBaseAddress;
2846 }
2847
2848 /* adjust for PC relative */
2849 if (Fixup.r.r_pcrel)
2850 SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
2851 }
2852 else
2853 {
2854 PKLDRMODMACHOSECT pSymSect;
2855 uint32_t iSymSect;
2856 RTLDRADDR Value;
2857
2858 /* sanity */
2859 KLDRMODMACHO_ASSERT(Fixup.s.r_scattered);
2860 if ((uint32_t)Fixup.s.r_address >= cbSectBits)
2861 return VERR_LDR_BAD_FIXUP;
2862
2863 /* calc fixup addresses. */
2864 uFix.pv = pbSectBits + Fixup.s.r_address;
2865 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.s.r_address : 0;
2866
2867 /*
2868 * Calc the symbol value.
2869 */
2870 /* The addend is stored in the code. */
2871 switch (Fixup.s.r_length)
2872 {
2873 case 0: SymAddr = *uFixVirgin.pi8; break;
2874 case 1: SymAddr = *uFixVirgin.pi16; break;
2875 case 2: SymAddr = *uFixVirgin.pi32; break;
2876 case 3: SymAddr = *uFixVirgin.pi64; break;
2877 }
2878 if (Fixup.s.r_pcrel)
2879 SymAddr += Fixup.s.r_address;
2880 Value = Fixup.s.r_value;
2881 SymAddr -= Value; /* (-> addend only) */
2882
2883 /* Find the section number from the r_value. */
2884 pSymSect = NULL;
2885 for (iSymSect = 0; iSymSect < pThis->cSections; iSymSect++)
2886 {
2887 RTLDRADDR off = Value - pThis->paSections[iSymSect].LinkAddress;
2888 if (off < pThis->paSections[iSymSect].cb)
2889 {
2890 pSymSect = &pThis->paSections[iSymSect];
2891 break;
2892 }
2893 else if (off == pThis->paSections[iSymSect].cb) /* edge case */
2894 pSymSect = &pThis->paSections[iSymSect];
2895 }
2896 if (!pSymSect)
2897 return VERR_LDR_BAD_FIXUP;
2898
2899 /* Calc the symbol address. */
2900 SymAddr += Value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
2901 if (Fixup.s.r_pcrel)
2902 SymAddr -= Fixup.s.r_address + pFixupSect->RVA + NewBaseAddress;
2903
2904 Fixup.r.r_length = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_length;
2905 Fixup.r.r_type = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_type;
2906 }
2907
2908 /*
2909 * Write back the fixed up value.
2910 */
2911 if (Fixup.r.r_type == GENERIC_RELOC_VANILLA)
2912 {
2913 switch (Fixup.r.r_length)
2914 {
2915 case 0: *uFix.pu8 = (uint8_t)SymAddr; break;
2916 case 1: *uFix.pu16 = (uint16_t)SymAddr; break;
2917 case 2: *uFix.pu32 = (uint32_t)SymAddr; break;
2918 case 3: *uFix.pu64 = (uint64_t)SymAddr; break;
2919 }
2920 }
2921 else if (Fixup.r.r_type <= GENERIC_RELOC_LOCAL_SECTDIFF)
2922 return VERR_LDRMACHO_UNSUPPORTED_FIXUP_TYPE;
2923 else
2924 return VERR_LDR_BAD_FIXUP;
2925 }
2926
2927 return VINF_SUCCESS;
2928}
2929
2930
2931/**
2932 * Applies AMD64 fixups to a section.
2933 *
2934 * @returns IPRT status code.
2935 * @param pThis The Mach-O module interpreter instance.
2936 * @param pbSectBits Pointer to the section bits.
2937 * @param pFixupSect The section being fixed up.
2938 * @param paSyms Pointer to the symbol table.
2939 * @param cSyms Number of symbols.
2940 * @param NewBaseAddress The new base image address.
2941 */
2942static int kldrModMachOFixupSectionAMD64(PKLDRMODMACHO pThis, uint8_t *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
2943 macho_nlist_64_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
2944{
2945 const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
2946 const uint32_t cFixups = pFixupSect->cFixups;
2947 size_t cbSectBits = (size_t)pFixupSect->cb;
2948 const uint8_t *pbSectVirginBits;
2949 uint32_t iFixup;
2950 RTLDRADDR SymAddr;
2951 int rc;
2952
2953 /*
2954 * Find the virgin bits.
2955 */
2956 if (pFixupSect->offFile != -1)
2957 {
2958 rc = kldrModMachOMapVirginBits(pThis);
2959 if (RT_FAILURE(rc))
2960 return rc;
2961 pbSectVirginBits = (const uint8_t *)pThis->pvBits + pFixupSect->offFile;
2962 }
2963 else
2964 pbSectVirginBits = NULL;
2965
2966 /*
2967 * Iterate the fixups and apply them.
2968 */
2969 for (iFixup = 0; iFixup < cFixups; iFixup++)
2970 {
2971 union
2972 {
2973 macho_relocation_info_t r;
2974 scattered_relocation_info_t s;
2975 } Fixup;
2976 Fixup.r = paFixups[iFixup];
2977
2978 /* AMD64 doesn't use scattered fixups. */
2979 KLDRMODMACHO_CHECK_RETURN(!(Fixup.r.r_address & R_SCATTERED), VERR_LDR_BAD_FIXUP);
2980
2981 /* sanity */
2982 KLDRMODMACHO_CHECK_RETURN((uint32_t)Fixup.r.r_address < cbSectBits, VERR_LDR_BAD_FIXUP);
2983
2984 /* calc fixup addresses. */
2985 RTPTRUNION uFix;
2986 uFix.pv = pbSectBits + Fixup.r.r_address;
2987 RTPTRUNION uFixVirgin;
2988 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.r.r_address : 0;
2989
2990 /*
2991 * Calc the symbol value.
2992 */
2993 /* Calc the linked symbol address / addend. */
2994 switch (Fixup.r.r_length)
2995 {
2996 /** @todo Deal with unaligned accesses on non x86 platforms. */
2997 case 2: SymAddr = *uFixVirgin.pi32; break;
2998 case 3: SymAddr = *uFixVirgin.pi64; break;
2999 default:
3000 KLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3001 }
3002
3003 /* Add symbol / section address. */
3004 if (Fixup.r.r_extern)
3005 {
3006 const macho_nlist_64_t *pSym;
3007
3008 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
3009 pSym = &paSyms[Fixup.r.r_symbolnum];
3010 KLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_BAD_FIXUP);
3011
3012 switch (Fixup.r.r_type)
3013 {
3014 /* GOT references just needs to have their symbol verified.
3015 Later, we'll optimize GOT building here using a parallel sym->got array. */
3016 case X86_64_RELOC_GOT_LOAD:
3017 case X86_64_RELOC_GOT:
3018 switch (pSym->n_type & MACHO_N_TYPE)
3019 {
3020 case MACHO_N_SECT:
3021 case MACHO_N_UNDF:
3022 case MACHO_N_ABS:
3023 break;
3024 case MACHO_N_INDR:
3025 case MACHO_N_PBUD:
3026 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3027 default:
3028 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3029 }
3030 SymAddr = sizeof(uint64_t) * Fixup.r.r_symbolnum + pThis->GotRVA + NewBaseAddress;
3031 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_BAD_FIXUP);
3032 SymAddr -= 4;
3033 break;
3034
3035 /* Verify the r_pcrel field for signed fixups on the way into the default case. */
3036 case X86_64_RELOC_BRANCH:
3037 case X86_64_RELOC_SIGNED:
3038 case X86_64_RELOC_SIGNED_1:
3039 case X86_64_RELOC_SIGNED_2:
3040 case X86_64_RELOC_SIGNED_4:
3041 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3042 /* Falls through. */
3043 default:
3044 {
3045 /* Adjust with fixup specific addend and vierfy unsigned/r_pcrel. */
3046 switch (Fixup.r.r_type)
3047 {
3048 case X86_64_RELOC_UNSIGNED:
3049 KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3050 break;
3051 case X86_64_RELOC_BRANCH:
3052 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_BAD_FIXUP);
3053 SymAddr -= 4;
3054 break;
3055 case X86_64_RELOC_SIGNED:
3056 case X86_64_RELOC_SIGNED_1:
3057 case X86_64_RELOC_SIGNED_2:
3058 case X86_64_RELOC_SIGNED_4:
3059 SymAddr -= 4;
3060 break;
3061 default:
3062 KLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3063 }
3064
3065 switch (pSym->n_type & MACHO_N_TYPE)
3066 {
3067 case MACHO_N_SECT:
3068 {
3069 PKLDRMODMACHOSECT pSymSect;
3070 KLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3071 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3072 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3073 break;
3074 }
3075
3076 case MACHO_N_UNDF:
3077 /* branch to an external symbol may have to take a short detour. */
3078 if ( Fixup.r.r_type == X86_64_RELOC_BRANCH
3079 && SymAddr + Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress
3080 - pSym->n_value
3081 + UINT64_C(0x80000000)
3082 >= UINT64_C(0xffffff20))
3083 SymAddr += pThis->cbJmpStub * Fixup.r.r_symbolnum + pThis->JmpStubsRVA + NewBaseAddress;
3084 else
3085 SymAddr += pSym->n_value;
3086 break;
3087
3088 case MACHO_N_ABS:
3089 SymAddr += pSym->n_value;
3090 break;
3091
3092 case MACHO_N_INDR:
3093 case MACHO_N_PBUD:
3094 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3095 default:
3096 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3097 }
3098 break;
3099 }
3100
3101 /*
3102 * This is a weird customer, it will always be follows by an UNSIGNED fixup.
3103 */
3104 case X86_64_RELOC_SUBTRACTOR:
3105 {
3106 macho_relocation_info_t Fixup2;
3107
3108 /* Deal with the SUBTRACT symbol first, by subtracting it from SymAddr. */
3109 switch (pSym->n_type & MACHO_N_TYPE)
3110 {
3111 case MACHO_N_SECT:
3112 {
3113 PKLDRMODMACHOSECT pSymSect;
3114 KLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3115 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3116 SymAddr -= pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3117 break;
3118 }
3119
3120 case MACHO_N_UNDF:
3121 case MACHO_N_ABS:
3122 SymAddr -= pSym->n_value;
3123 break;
3124
3125 case MACHO_N_INDR:
3126 case MACHO_N_PBUD:
3127 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3128 default:
3129 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3130 }
3131
3132 /* Load the 2nd fixup, check sanity. */
3133 iFixup++;
3134 KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel && iFixup < cFixups, VERR_LDR_BAD_FIXUP);
3135 Fixup2 = paFixups[iFixup];
3136 KLDRMODMACHO_CHECK_RETURN( Fixup2.r_address == Fixup.r.r_address
3137 && Fixup2.r_length == Fixup.r.r_length
3138 && Fixup2.r_type == X86_64_RELOC_UNSIGNED
3139 && !Fixup2.r_pcrel
3140 && Fixup2.r_symbolnum < cSyms,
3141 VERR_LDR_BAD_FIXUP);
3142
3143 if (Fixup2.r_extern)
3144 {
3145 KLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
3146 pSym = &paSyms[Fixup2.r_symbolnum];
3147 KLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_BAD_FIXUP);
3148
3149 /* Add it's value to SymAddr. */
3150 switch (pSym->n_type & MACHO_N_TYPE)
3151 {
3152 case MACHO_N_SECT:
3153 {
3154 PKLDRMODMACHOSECT pSymSect;
3155 KLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3156 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3157 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3158 break;
3159 }
3160
3161 case MACHO_N_UNDF:
3162 case MACHO_N_ABS:
3163 SymAddr += pSym->n_value;
3164 break;
3165
3166 case MACHO_N_INDR:
3167 case MACHO_N_PBUD:
3168 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3169 default:
3170 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3171 }
3172 }
3173 else if (Fixup2.r_symbolnum != R_ABS)
3174 {
3175 PKLDRMODMACHOSECT pSymSect;
3176 KLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
3177 pSymSect = &pThis->paSections[Fixup2.r_symbolnum - 1];
3178 SymAddr += pSymSect->RVA + NewBaseAddress;
3179 }
3180 else
3181 KLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3182 }
3183 break;
3184 }
3185 }
3186 else
3187 {
3188 /* verify against fixup type and make adjustments */
3189 switch (Fixup.r.r_type)
3190 {
3191 case X86_64_RELOC_UNSIGNED:
3192 KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3193 break;
3194 case X86_64_RELOC_BRANCH:
3195 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3196 SymAddr += 4; /* dunno what the assmbler/linker really is doing here... */
3197 break;
3198 case X86_64_RELOC_SIGNED:
3199 case X86_64_RELOC_SIGNED_1:
3200 case X86_64_RELOC_SIGNED_2:
3201 case X86_64_RELOC_SIGNED_4:
3202 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3203 break;
3204 /*case X86_64_RELOC_GOT_LOAD:*/
3205 /*case X86_64_RELOC_GOT: */
3206 /*case X86_64_RELOC_SUBTRACTOR: - must be r_extern=1 says as. */
3207 default:
3208 KLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3209 }
3210 if (Fixup.r.r_symbolnum != R_ABS)
3211 {
3212 PKLDRMODMACHOSECT pSymSect;
3213 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
3214 pSymSect = &pThis->paSections[Fixup.r.r_symbolnum - 1];
3215
3216 SymAddr -= pSymSect->LinkAddress;
3217 SymAddr += pSymSect->RVA + NewBaseAddress;
3218 if (Fixup.r.r_pcrel)
3219 SymAddr += Fixup.r.r_address;
3220 }
3221 }
3222
3223 /* adjust for PC relative */
3224 if (Fixup.r.r_pcrel)
3225 SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
3226
3227 /*
3228 * Write back the fixed up value.
3229 */
3230 switch (Fixup.r.r_length)
3231 {
3232 case 3:
3233 *uFix.pu64 = (uint64_t)SymAddr;
3234 break;
3235 case 2:
3236 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel || Fixup.r.r_type == X86_64_RELOC_SUBTRACTOR, VERR_LDR_BAD_FIXUP);
3237 KLDRMODMACHO_CHECK_RETURN((int32_t)SymAddr == (int64_t)SymAddr, VERR_LDR_ADDRESS_OVERFLOW);
3238 *uFix.pu32 = (uint32_t)SymAddr;
3239 break;
3240 default:
3241 KLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3242 }
3243 }
3244
3245 return VINF_SUCCESS;
3246}
3247
3248
3249/**
3250 * Loads the symbol table for a MH_OBJECT file.
3251 *
3252 * The symbol table is pointed to by KLDRMODMACHO::pvaSymbols.
3253 *
3254 * @returns IPRT status code.
3255 * @param pThis The Mach-O module interpreter instance.
3256 */
3257static int kldrModMachOLoadObjSymTab(PKLDRMODMACHO pThis)
3258{
3259 int rc = VINF_SUCCESS;
3260
3261 if ( !pThis->pvaSymbols
3262 && pThis->cSymbols)
3263 {
3264 size_t cbSyms;
3265 size_t cbSym;
3266 void *pvSyms;
3267 void *pvStrings;
3268
3269 /* sanity */
3270 KLDRMODMACHO_CHECK_RETURN( pThis->offSymbols
3271 && (!pThis->cchStrings || pThis->offStrings),
3272 VERR_LDRMACHO_BAD_OBJECT_FILE);
3273
3274 /* allocate */
3275 cbSym = pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
3276 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
3277 ? sizeof(macho_nlist_32_t)
3278 : sizeof(macho_nlist_64_t);
3279 cbSyms = pThis->cSymbols * cbSym;
3280 KLDRMODMACHO_CHECK_RETURN(cbSyms / cbSym == pThis->cSymbols, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
3281 rc = VERR_NO_MEMORY;
3282 pvSyms = RTMemAlloc(cbSyms);
3283 if (pvSyms)
3284 {
3285 if (pThis->cchStrings)
3286 pvStrings = RTMemAlloc(pThis->cchStrings);
3287 else
3288 pvStrings = RTMemAllocZ(4);
3289 if (pvStrings)
3290 {
3291 /* read */
3292 rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvSyms, cbSyms, pThis->offSymbols);
3293 if (RT_SUCCESS(rc) && pThis->cchStrings)
3294 rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvStrings,
3295 pThis->cchStrings, pThis->offStrings);
3296 if (RT_SUCCESS(rc))
3297 {
3298 pThis->pvaSymbols = pvSyms;
3299 pThis->pchStrings = (char *)pvStrings;
3300
3301 /* perform endian conversion? */
3302 if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
3303 {
3304 uint32_t cLeft = pThis->cSymbols;
3305 macho_nlist_32_t *pSym = (macho_nlist_32_t *)pvSyms;
3306 while (cLeft-- > 0)
3307 {
3308 pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
3309 pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
3310 pSym->n_value = RT_BSWAP_U32(pSym->n_value);
3311 pSym++;
3312 }
3313 }
3314 else if (pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
3315 {
3316 uint32_t cLeft = pThis->cSymbols;
3317 macho_nlist_64_t *pSym = (macho_nlist_64_t *)pvSyms;
3318 while (cLeft-- > 0)
3319 {
3320 pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
3321 pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
3322 pSym->n_value = RT_BSWAP_U64(pSym->n_value);
3323 pSym++;
3324 }
3325 }
3326
3327 return VINF_SUCCESS;
3328 }
3329 RTMemFree(pvStrings);
3330 }
3331 RTMemFree(pvSyms);
3332 }
3333 }
3334 else
3335 KLDRMODMACHO_ASSERT(pThis->pchStrings || pThis->Hdr.filetype == MH_DSYM);
3336
3337 return rc;
3338}
3339
3340
3341/**
3342 * Loads the fixups at the given address and performs endian
3343 * conversion if necessary.
3344 *
3345 * @returns IPRT status code.
3346 * @param pThis The Mach-O module interpreter instance.
3347 * @param offFixups The file offset of the fixups.
3348 * @param cFixups The number of fixups to load.
3349 * @param ppaFixups Where to put the pointer to the allocated fixup array.
3350 */
3351static int kldrModMachOLoadFixups(PKLDRMODMACHO pThis, RTFOFF offFixups, uint32_t cFixups, macho_relocation_info_t **ppaFixups)
3352{
3353 macho_relocation_info_t *paFixups;
3354 size_t cbFixups;
3355
3356 /* allocate the memory. */
3357 cbFixups = cFixups * sizeof(*paFixups);
3358 KLDRMODMACHO_CHECK_RETURN(cbFixups / sizeof(*paFixups) == cFixups, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
3359 paFixups = (macho_relocation_info_t *)RTMemAlloc(cbFixups);
3360 if (!paFixups)
3361 return VERR_NO_MEMORY;
3362
3363 /* read the fixups. */
3364 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, paFixups, cbFixups, offFixups);
3365 if (RT_SUCCESS(rc))
3366 {
3367 *ppaFixups = paFixups;
3368
3369 /* do endian conversion if necessary. */
3370 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
3371 || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
3372 {
3373 uint32_t iFixup;
3374 for (iFixup = 0; iFixup < cFixups; iFixup++)
3375 {
3376 uint32_t *pu32 = (uint32_t *)&paFixups[iFixup];
3377 pu32[0] = RT_BSWAP_U32(pu32[0]);
3378 pu32[1] = RT_BSWAP_U32(pu32[1]);
3379 }
3380 }
3381 }
3382 else
3383 RTMemFree(paFixups);
3384 return rc;
3385}
3386
3387
3388/**
3389 * Maps the virgin file bits into memory if not already done.
3390 *
3391 * @returns IPRT status code.
3392 * @param pThis The Mach-O module interpreter instance.
3393 */
3394static int kldrModMachOMapVirginBits(PKLDRMODMACHO pThis)
3395{
3396 int rc = VINF_SUCCESS;
3397 if (!pThis->pvBits)
3398 rc = pThis->Core.pReader->pfnMap(pThis->Core.pReader, &pThis->pvBits);
3399 return rc;
3400}
3401
3402#if 0
3403
3404/** @copydoc kLdrModCallInit */
3405static int kldrModMachOCallInit(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
3406{
3407 /* later */
3408 RT_NOREF(pMod);
3409 RT_NOREF(pvMapping);
3410 RT_NOREF(uHandle);
3411 return VINF_SUCCESS;
3412}
3413
3414
3415/** @copydoc kLdrModCallTerm */
3416static int kldrModMachOCallTerm(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
3417{
3418 /* later */
3419 RT_NOREF(pMod);
3420 RT_NOREF(pvMapping);
3421 RT_NOREF(uHandle);
3422 return VINF_SUCCESS;
3423}
3424
3425
3426/** @copydoc kLdrModCallThread */
3427static int kldrModMachOCallThread(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle, unsigned fAttachingOrDetaching)
3428{
3429 /* Relevant for Mach-O? */
3430 RT_NOREF(pMod);
3431 RT_NOREF(pvMapping);
3432 RT_NOREF(uHandle);
3433 RT_NOREF(fAttachingOrDetaching);
3434 return VINF_SUCCESS;
3435}
3436
3437#endif
3438
3439
3440/**
3441 * @interface_method_impl{RTLDROPS,pfnGetImageSize}
3442 */
3443static DECLCALLBACK(size_t) rtldrMachO_GetImageSize(PRTLDRMODINTERNAL pMod)
3444{
3445 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3446 return pThis->cbImage;
3447}
3448
3449
3450/**
3451 * @interface_method_impl{RTLDROPS,pfnGetBits}
3452 */
3453static DECLCALLBACK(int) rtldrMachO_GetBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR BaseAddress,
3454 PFNRTLDRIMPORT pfnGetImport, void *pvUser)
3455{
3456 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3457
3458 if (!pThis->fCanLoad)
3459 return VERR_LDRMACHO_TODO;
3460
3461 /*
3462 * Zero the entire buffer first to simplify things.
3463 */
3464 memset(pvBits, 0, (size_t)pThis->cbImage);
3465
3466 /*
3467 * When possible use the segment table to load the data.
3468 */
3469 for (uint32_t i = 0; i < pThis->cSegments; i++)
3470 {
3471 /* skip it? */
3472 if ( pThis->aSegments[i].SegInfo.cbFile == -1
3473 || pThis->aSegments[i].SegInfo.offFile == -1
3474 || pThis->aSegments[i].SegInfo.LinkAddress == NIL_RTLDRADDR
3475 || !pThis->aSegments[i].SegInfo.Alignment)
3476 continue;
3477 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader,
3478 (uint8_t *)pvBits + pThis->aSegments[i].SegInfo.RVA,
3479 pThis->aSegments[i].SegInfo.cbFile,
3480 pThis->aSegments[i].SegInfo.offFile);
3481 if (RT_FAILURE(rc))
3482 return rc;
3483 }
3484
3485 /*
3486 * Perform relocations.
3487 */
3488 return rtldrMachO_RelocateBits(pMod, pvBits, BaseAddress, pThis->LinkAddress, pfnGetImport, pvUser);
3489}
3490
3491
3492/**
3493 * @interface_method_impl{RTLDROPS,pfnRelocate}
3494 */
3495static DECLCALLBACK(int) rtldrMachO_RelocateBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
3496 RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
3497{
3498 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3499 int rc;
3500 RT_NOREF(OldBaseAddress);
3501
3502 /*
3503 * Call workers to do the jobs.
3504 */
3505 if (pThis->Hdr.filetype == MH_OBJECT)
3506 {
3507 rc = kldrModMachOObjDoImports(pThis, NewBaseAddress, pfnGetImport, pvUser);
3508 if (RT_SUCCESS(rc))
3509 rc = kldrModMachOObjDoFixups(pThis, pvBits, NewBaseAddress);
3510
3511 }
3512 else
3513 rc = VERR_LDRMACHO_TODO;
3514 /*{
3515 rc = kldrModMachODoFixups(pThis, pvBits, NewBaseAddress, OldBaseAddress, pfnGetImport, pvUser);
3516 if (RT_SUCCESS(rc))
3517 rc = kldrModMachODoImports(pThis, pvBits, pfnGetImport, pvUser);
3518 }*/
3519
3520 /*
3521 * Construct the global offset table if necessary, it's always the last
3522 * segment when present.
3523 */
3524 if (RT_SUCCESS(rc) && pThis->fMakeGot)
3525 rc = kldrModMachOMakeGOT(pThis, pvBits, NewBaseAddress);
3526
3527 return rc;
3528}
3529
3530
3531/**
3532 * Builds the GOT.
3533 *
3534 * Assumes the symbol table has all external symbols resolved correctly and that
3535 * the bits has been cleared up front.
3536 */
3537static int kldrModMachOMakeGOT(PKLDRMODMACHO pThis, void *pvBits, RTLDRADDR NewBaseAddress)
3538{
3539 uint32_t iSym = pThis->cSymbols;
3540 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
3541 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
3542 {
3543 macho_nlist_32_t const *paSyms = (macho_nlist_32_t const *)pThis->pvaSymbols;
3544 uint32_t *paGOT = (uint32_t *)((uint8_t *)pvBits + pThis->GotRVA);
3545 while (iSym-- > 0)
3546 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
3547 {
3548 case MACHO_N_SECT:
3549 {
3550 PKLDRMODMACHOSECT pSymSect;
3551 KLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3552 pSymSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
3553 paGOT[iSym] = (uint32_t)(paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress);
3554 break;
3555 }
3556
3557 case MACHO_N_UNDF:
3558 case MACHO_N_ABS:
3559 paGOT[iSym] = paSyms[iSym].n_value;
3560 break;
3561 }
3562 }
3563 else
3564 {
3565 macho_nlist_64_t const *paSyms = (macho_nlist_64_t const *)pThis->pvaSymbols;
3566 uint64_t *paGOT = (uint64_t *)((uint8_t *)pvBits + pThis->GotRVA);
3567 while (iSym-- > 0)
3568 {
3569 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
3570 {
3571 case MACHO_N_SECT:
3572 {
3573 PKLDRMODMACHOSECT pSymSect;
3574 KLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3575 pSymSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
3576 paGOT[iSym] = paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3577 break;
3578 }
3579
3580 case MACHO_N_UNDF:
3581 case MACHO_N_ABS:
3582 paGOT[iSym] = paSyms[iSym].n_value;
3583 break;
3584 }
3585 }
3586
3587 if (pThis->JmpStubsRVA != NIL_RTLDRADDR)
3588 {
3589 iSym = pThis->cSymbols;
3590 switch (pThis->Hdr.cputype)
3591 {
3592 /*
3593 * AMD64 is simple since the GOT and the indirect jmps are parallel
3594 * arrays with entries of the same size. The relative offset will
3595 * be the the same for each entry, kind of nice. :-)
3596 */
3597 case CPU_TYPE_X86_64:
3598 {
3599 uint64_t *paJmps = (uint64_t *)((uint8_t *)pvBits + pThis->JmpStubsRVA);
3600 int32_t off;
3601 uint64_t u64Tmpl;
3602 union
3603 {
3604 uint8_t ab[8];
3605 uint64_t u64;
3606 } Tmpl;
3607
3608 /* create the template. */
3609 off = (int32_t)(pThis->GotRVA - (pThis->JmpStubsRVA + 6));
3610 Tmpl.ab[0] = 0xff; /* jmp [GOT-entry wrt RIP] */
3611 Tmpl.ab[1] = 0x25;
3612 Tmpl.ab[2] = off & 0xff;
3613 Tmpl.ab[3] = (off >> 8) & 0xff;
3614 Tmpl.ab[4] = (off >> 16) & 0xff;
3615 Tmpl.ab[5] = (off >> 24) & 0xff;
3616 Tmpl.ab[6] = 0xcc;
3617 Tmpl.ab[7] = 0xcc;
3618 u64Tmpl = Tmpl.u64;
3619
3620 /* copy the template to every jmp table entry. */
3621 while (iSym-- > 0)
3622 paJmps[iSym] = u64Tmpl;
3623 break;
3624 }
3625
3626 default:
3627 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3628 }
3629 }
3630 }
3631 return VINF_SUCCESS;
3632}
3633
3634
3635/**
3636 * @interface_method_impl{RTLDROPS,pfnEnumSegments}
3637 */
3638static DECLCALLBACK(int) rtldrMachO_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
3639{
3640 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3641 uint32_t const cSegments = pThis->cSegments;
3642 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3643 {
3644 int rc = pfnCallback(pMod, &pThis->aSegments[iSeg].SegInfo, pvUser);
3645 if (rc != VINF_SUCCESS)
3646 return rc;
3647 }
3648
3649 return VINF_SUCCESS;
3650}
3651
3652
3653/**
3654 * @interface_method_impl{RTLDROPS,pfnLinkAddressToSegOffset}
3655 */
3656static DECLCALLBACK(int) rtldrMachO_LinkAddressToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress,
3657 uint32_t *piSeg, PRTLDRADDR poffSeg)
3658{
3659 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3660 uint32_t const cSegments = pThis->cSegments;
3661 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3662 {
3663 RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
3664 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3665 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3666 {
3667 *piSeg = iSeg;
3668 *poffSeg = offSeg;
3669 return VINF_SUCCESS;
3670 }
3671 }
3672
3673 return VERR_LDR_INVALID_LINK_ADDRESS;
3674}
3675
3676
3677/**
3678 * @interface_method_impl{RTLDROPS,pfnLinkAddressToRva}
3679 */
3680static DECLCALLBACK(int) rtldrMachO_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
3681{
3682 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3683 uint32_t const cSegments = pThis->cSegments;
3684 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3685 {
3686 RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
3687 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3688 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3689 {
3690 *pRva = pThis->aSegments[iSeg].SegInfo.RVA + offSeg;
3691 return VINF_SUCCESS;
3692 }
3693 }
3694
3695 return VERR_LDR_INVALID_RVA;
3696}
3697
3698
3699/**
3700 * @interface_method_impl{RTLDROPS,pfnSegOffsetToRva}
3701 */
3702static DECLCALLBACK(int) rtldrMachO_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg, PRTLDRADDR pRva)
3703{
3704 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3705
3706 if (iSeg >= pThis->cSegments)
3707 return VERR_LDR_INVALID_SEG_OFFSET;
3708 KLDRMODMACHOSEG const *pSegment = &pThis->aSegments[iSeg];
3709
3710 if ( offSeg > pSegment->SegInfo.cbMapped
3711 && offSeg > pSegment->SegInfo.cb
3712 && ( pSegment->SegInfo.cbFile < 0
3713 || offSeg > (uint64_t)pSegment->SegInfo.cbFile))
3714 return VERR_LDR_INVALID_SEG_OFFSET;
3715
3716 *pRva = pSegment->SegInfo.RVA + offSeg;
3717 return VINF_SUCCESS;
3718}
3719
3720
3721/**
3722 * @interface_method_impl{RTLDROPS,pfnRvaToSegOffset}
3723 */
3724static DECLCALLBACK(int) rtldrMachO_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva, uint32_t *piSeg, PRTLDRADDR poffSeg)
3725{
3726 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3727 uint32_t const cSegments = pThis->cSegments;
3728 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3729 {
3730 RTLDRADDR offSeg = Rva - pThis->aSegments[iSeg].SegInfo.RVA;
3731 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3732 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3733 {
3734 *piSeg = iSeg;
3735 *poffSeg = offSeg;
3736 return VINF_SUCCESS;
3737 }
3738 }
3739
3740 return VERR_LDR_INVALID_RVA;
3741}
3742
3743
3744/**
3745 * @interface_method_impl{RTLDROPS,pfnReadDbgInfo}
3746 */
3747static DECLCALLBACK(int) rtldrMachO_ReadDbgInfo(PRTLDRMODINTERNAL pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void *pvBuf)
3748{
3749 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3750
3751 /** @todo May have to apply fixups here. */
3752 if (iDbgInfo < pThis->cSections)
3753 return pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvBuf, cb, off);
3754 return VERR_OUT_OF_RANGE;
3755}
3756
3757
3758/** @interface_method_impl{RTLDROPS,pfnQueryProp} */
3759static DECLCALLBACK(int) rtldrMachO_QueryProp(PRTLDRMODINTERNAL pMod, RTLDRPROP enmProp, void const *pvBits,
3760 void *pvBuf, size_t cbBuf, size_t *pcbRet)
3761{
3762 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3763 int rc;
3764 switch (enmProp)
3765 {
3766 case RTLDRPROP_UUID:
3767 Assert(cbBuf >= sizeof(pThis->abImageUuid));
3768 if (!ASMMemIsZero(pThis->abImageUuid, sizeof(pThis->abImageUuid)))
3769 {
3770 *pcbRet = sizeof(pThis->abImageUuid);
3771 memcpy(pvBuf, pThis->abImageUuid, sizeof(pThis->abImageUuid));
3772 rc = VINF_SUCCESS;
3773 }
3774 else
3775 rc = VERR_NOT_FOUND;
3776 break;
3777
3778#if 0 /** @todo return LC_ID_DYLIB */
3779 case RTLDRPROP_INTERNAL_NAME:
3780#endif
3781
3782 default:
3783 rc = VERR_NOT_FOUND;
3784 break;
3785 }
3786 RT_NOREF_PV(pvBits);
3787 return rc;
3788}
3789
3790
3791/**
3792 * Operations for a Mach-O module interpreter.
3793 */
3794static const RTLDROPS s_rtldrMachOOps=
3795{
3796 "mach-o",
3797 rtldrMachO_Close,
3798 NULL,
3799 NULL /*pfnDone*/,
3800 rtldrMachO_EnumSymbols,
3801 /* ext */
3802 rtldrMachO_GetImageSize,
3803 rtldrMachO_GetBits,
3804 rtldrMachO_RelocateBits,
3805 rtldrMachO_GetSymbolEx,
3806 NULL /*pfnQueryForwarderInfo*/,
3807 rtldrMachO_EnumDbgInfo,
3808 rtldrMachO_EnumSegments,
3809 rtldrMachO_LinkAddressToSegOffset,
3810 rtldrMachO_LinkAddressToRva,
3811 rtldrMachO_SegOffsetToRva,
3812 rtldrMachO_RvaToSegOffset,
3813 rtldrMachO_ReadDbgInfo,
3814 rtldrMachO_QueryProp,
3815 NULL /*pfnVerifySignature*/,
3816 NULL /*pfnHashImage*/,
3817 NULL /*pfnUnwindFrame*/,
3818 42
3819};
3820
3821
3822/**
3823 * Handles opening Mach-O images (non-fat).
3824 */
3825DECLHIDDEN(int) rtldrMachOOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offImage,
3826 PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
3827{
3828
3829 /*
3830 * Create the instance data and do a minimal header validation.
3831 */
3832 PKLDRMODMACHO pThis = NULL;
3833 int rc = kldrModMachODoCreate(pReader, offImage, fFlags, &pThis, pErrInfo);
3834 if (RT_SUCCESS(rc))
3835 {
3836 /*
3837 * Match up against the requested CPU architecture.
3838 */
3839 if ( enmArch == RTLDRARCH_WHATEVER
3840 || pThis->Core.enmArch == enmArch)
3841 {
3842 pThis->Core.pOps = &s_rtldrMachOOps;
3843 pThis->Core.u32Magic = RTLDRMOD_MAGIC;
3844 *phLdrMod = &pThis->Core;
3845 return VINF_SUCCESS;
3846 }
3847 rc = VERR_LDR_ARCH_MISMATCH;
3848 }
3849 if (pThis)
3850 {
3851 RTMemFree(pThis->pbLoadCommands);
3852 RTMemFree(pThis);
3853 }
3854 return rc;
3855
3856}
3857
3858
3859/**
3860 * Handles opening FAT Mach-O image.
3861 */
3862DECLHIDDEN(int) rtldrFatOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
3863{
3864 fat_header_t FatHdr;
3865 int rc = pReader->pfnRead(pReader, &FatHdr, sizeof(FatHdr), 0);
3866 if (RT_FAILURE(rc))
3867 return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
3868
3869 if (FatHdr.magic == IMAGE_FAT_SIGNATURE)
3870 { /* likely */ }
3871 else if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
3872 FatHdr.nfat_arch = RT_BSWAP_U32(FatHdr.nfat_arch);
3873 else
3874 return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "magic=%#x", FatHdr.magic);
3875 if (FatHdr.nfat_arch < 64)
3876 return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "Bad nfat_arch value: %#x", FatHdr.nfat_arch);
3877
3878 uint32_t offEntry = sizeof(FatHdr);
3879 for (uint32_t i = 0; i < FatHdr.nfat_arch; i++, offEntry += sizeof(fat_arch_t))
3880 {
3881 fat_arch_t FatEntry;
3882 rc = pReader->pfnRead(pReader, &FatEntry, sizeof(FatEntry), offEntry);
3883 if (RT_FAILURE(rc))
3884 return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
3885 if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
3886 {
3887 FatEntry.cputype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cputype);
3888 //FatEntry.cpusubtype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cpusubtype);
3889 FatEntry.offset = RT_BSWAP_U32(FatEntry.offset);
3890 //FatEntry.size = RT_BSWAP_U32(FatEntry.size);
3891 //FatEntry.align = RT_BSWAP_U32(FatEntry.align);
3892 }
3893
3894 /*
3895 * Match enmArch.
3896 */
3897 bool fMatch = false;
3898 switch (enmArch)
3899 {
3900 case RTLDRARCH_WHATEVER:
3901 fMatch = true;
3902 break;
3903
3904 case RTLDRARCH_X86_32:
3905 fMatch = FatEntry.cputype == CPU_TYPE_X86;
3906 break;
3907
3908 case RTLDRARCH_AMD64:
3909 fMatch = FatEntry.cputype == CPU_TYPE_X86_64;
3910 break;
3911
3912 case RTLDRARCH_ARM32:
3913 case RTLDRARCH_ARM64:
3914 case RTLDRARCH_X86_16:
3915 fMatch = false;
3916 break;
3917
3918 case RTLDRARCH_INVALID:
3919 case RTLDRARCH_HOST:
3920 case RTLDRARCH_END:
3921 case RTLDRARCH_32BIT_HACK:
3922 AssertFailedReturn(VERR_INVALID_PARAMETER);
3923 }
3924 if (fMatch)
3925 return rtldrMachOOpen(pReader, fFlags, enmArch, FatEntry.offset, phLdrMod, pErrInfo);
3926 }
3927
3928 return VERR_LDR_ARCH_MISMATCH;
3929
3930}
3931
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