VirtualBox

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

Last change on this file since 74640 was 74640, 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: 150.7 KB
Line 
1/* $Id: ldrMachO.cpp 74640 2018-10-06 19:14:10Z 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 uint32_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);
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 if (RT_FAILURE(rc))
348 {
349 RTMemFree(pbLoadCommands);
350 return rc;
351 }
352 cSegments += fMakeGot;
353
354
355 /*
356 * Calc the instance size, allocate and initialize it.
357 */
358 size_t const cbModAndSegs = RT_ALIGN_Z(RT_UOFFSETOF_DYN(KLDRMODMACHO, aSegments[cSegments])
359 + sizeof(KLDRMODMACHOSECT) * cSections, 16);
360 PKLDRMODMACHO pThis = (PKLDRMODMACHO)RTMemAlloc(cbModAndSegs + cbStringPool);
361 if (!pThis)
362 return VERR_NO_MEMORY;
363 *ppModMachO = pThis;
364 pThis->pbLoadCommands = pbLoadCommands;
365 pThis->offImage = offImage;
366
367 /* Core & CPU.*/
368 pThis->Core.u32Magic = 0; /* set by caller */
369 pThis->Core.eState = LDR_STATE_OPENED;
370 pThis->Core.pOps = NULL; /* set by caller. */
371 pThis->Core.pReader = pRdr;
372 switch (s.Hdr32.cputype)
373 {
374 case CPU_TYPE_X86:
375 pThis->Core.enmArch = RTLDRARCH_X86_32;
376 pThis->Core.enmEndian = RTLDRENDIAN_LITTLE;
377 switch (s.Hdr32.cpusubtype)
378 {
379 case CPU_SUBTYPE_I386_ALL: /* == CPU_SUBTYPE_386 */
380 pThis->enmCpu = RTLDRCPU_X86_32_BLEND;
381 break;
382 case CPU_SUBTYPE_486:
383 pThis->enmCpu = RTLDRCPU_I486;
384 break;
385 case CPU_SUBTYPE_486SX:
386 pThis->enmCpu = RTLDRCPU_I486SX;
387 break;
388 case CPU_SUBTYPE_PENT: /* == CPU_SUBTYPE_586 */
389 pThis->enmCpu = RTLDRCPU_I586;
390 break;
391 case CPU_SUBTYPE_PENTPRO:
392 case CPU_SUBTYPE_PENTII_M3:
393 case CPU_SUBTYPE_PENTII_M5:
394 case CPU_SUBTYPE_CELERON:
395 case CPU_SUBTYPE_CELERON_MOBILE:
396 case CPU_SUBTYPE_PENTIUM_3:
397 case CPU_SUBTYPE_PENTIUM_3_M:
398 case CPU_SUBTYPE_PENTIUM_3_XEON:
399 pThis->enmCpu = RTLDRCPU_I686;
400 break;
401 case CPU_SUBTYPE_PENTIUM_M:
402 case CPU_SUBTYPE_PENTIUM_4:
403 case CPU_SUBTYPE_PENTIUM_4_M:
404 case CPU_SUBTYPE_XEON:
405 case CPU_SUBTYPE_XEON_MP:
406 pThis->enmCpu = RTLDRCPU_P4;
407 break;
408
409 default:
410 /* Hack for kextutil output. */
411 if ( s.Hdr32.cpusubtype == 0
412 && s.Hdr32.filetype == MH_OBJECT)
413 break;
414 return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
415 }
416 break;
417
418 case CPU_TYPE_X86_64:
419 pThis->Core.enmArch = RTLDRARCH_AMD64;
420 pThis->Core.enmEndian = RTLDRENDIAN_LITTLE;
421 switch (s.Hdr32.cpusubtype & ~CPU_SUBTYPE_MASK)
422 {
423 case CPU_SUBTYPE_X86_64_ALL: pThis->enmCpu = RTLDRCPU_AMD64_BLEND; break;
424 default:
425 return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
426 }
427 break;
428
429 default:
430 return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
431 }
432
433 pThis->Core.enmFormat = RTLDRFMT_MACHO;
434 switch (s.Hdr32.filetype)
435 {
436 case MH_OBJECT: pThis->Core.enmType = RTLDRTYPE_OBJECT; break;
437 case MH_EXECUTE: pThis->Core.enmType = RTLDRTYPE_EXECUTABLE_FIXED; break;
438 case MH_DYLIB: pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
439 case MH_BUNDLE: pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
440 case MH_KEXT_BUNDLE:pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
441 case MH_DSYM: pThis->Core.enmType = RTLDRTYPE_DEBUG_INFO; break;
442 default:
443 return VERR_LDRMACHO_UNSUPPORTED_FILE_TYPE;
444 }
445
446 /* KLDRMODMACHO */
447 pThis->cSegments = cSegments;
448 pThis->pvBits = NULL;
449 pThis->pvMapping = NULL;
450 pThis->fOpenFlags = fOpenFlags;
451 pThis->Hdr = s.Hdr64;
452 if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE
453 || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE)
454 pThis->Hdr.reserved = 0;
455 pThis->LinkAddress = LinkAddress;
456 pThis->cbImage = 0;
457 pThis->fCanLoad = fCanLoad;
458 pThis->fMakeGot = fMakeGot;
459 pThis->cbJmpStub = cbJmpStub;
460 pThis->uEffFileType = uEffFileType;
461 pThis->offSymbols = 0;
462 pThis->cSymbols = 0;
463 pThis->pvaSymbols = NULL;
464 pThis->offStrings = 0;
465 pThis->cchStrings = 0;
466 pThis->pchStrings = NULL;
467 memset(pThis->abImageUuid, 0, sizeof(pThis->abImageUuid));
468 pThis->GotRVA = NIL_RTLDRADDR;
469 pThis->JmpStubsRVA = NIL_RTLDRADDR;
470 pThis->cSections = cSections;
471 pThis->paSections = (PKLDRMODMACHOSECT)&pThis->aSegments[pThis->cSegments];
472
473 /*
474 * Setup the KLDRMOD segment array.
475 */
476 rc = kldrModMachOParseLoadCommands(pThis, (char *)pThis + cbModAndSegs, cbStringPool);
477
478 /*
479 * We're done.
480 */
481 return rc;
482}
483
484
485/**
486 * Converts, validates and preparses the load commands before we carve
487 * out the module instance.
488 *
489 * The conversion that's preformed is format endian to host endian. The
490 * preparsing has to do with segment counting, section counting and string pool
491 * sizing.
492 *
493 * Segment are created in two different ways, depending on the file type.
494 *
495 * For object files there is only one segment command without a given segment
496 * name. The sections inside that segment have different segment names and are
497 * not sorted by their segname attribute. We create one segment for each
498 * section, with the segment name being 'segname.sectname' in order to hopefully
499 * keep the names unique. Debug sections does not get segments.
500 *
501 * For non-object files, one kLdr segment is created for each Mach-O segment.
502 * Debug segments is not exposed by kLdr via the kLdr segment table, but via the
503 * debug enumeration callback API.
504 *
505 * @returns IPRT status code.
506 * @param pbLoadCommands The load commands to parse.
507 * @param pHdr The header.
508 * @param pRdr The file reader.
509 * @param offImage The image header (FAT fun).
510 * @param pcSegments Where to store the segment count.
511 * @param pcSegments Where to store the section count.
512 * @param pcbStringPool Where to store the string pool size.
513 * @param pfCanLoad Where to store the can-load-image indicator.
514 * @param pLinkAddress Where to store the image link address (i.e. the
515 * lowest segment address).
516 */
517static int kldrModMachOPreParseLoadCommands(uint8_t *pbLoadCommands, const mach_header_32_t *pHdr, PRTLDRREADER pRdr, RTFOFF offImage,
518 uint32_t fOpenFlags, uint32_t *pcSegments, uint32_t *pcSections, uint32_t *pcbStringPool,
519 bool *pfCanLoad, PRTLDRADDR pLinkAddress, uint8_t *puEffFileType)
520{
521 union
522 {
523 uint8_t *pb;
524 load_command_t *pLoadCmd;
525 segment_command_32_t *pSeg32;
526 segment_command_64_t *pSeg64;
527 thread_command_t *pThread;
528 symtab_command_t *pSymTab;
529 uuid_command_t *pUuid;
530 } u;
531 const uint64_t cbFile = pRdr->pfnSize(pRdr) - offImage;
532 uint32_t cSegments = 0;
533 uint32_t cSections = 0;
534 size_t cbStringPool = 0;
535 uint32_t cLeft = pHdr->ncmds;
536 uint32_t cbLeft = pHdr->sizeofcmds;
537 uint8_t *pb = pbLoadCommands;
538 int cSegmentCommands = 0;
539 int cSymbolTabs = 0;
540 int fConvertEndian = pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
541 || pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE;
542 uint8_t uEffFileType = *puEffFileType = pHdr->filetype;
543
544 *pcSegments = 0;
545 *pcSections = 0;
546 *pcbStringPool = 0;
547 *pfCanLoad = true;
548 *pLinkAddress = ~(RTLDRADDR)0;
549
550 while (cLeft-- > 0)
551 {
552 u.pb = pb;
553
554 /*
555 * Convert and validate command header.
556 */
557 KLDRMODMACHO_CHECK_RETURN(cbLeft >= sizeof(load_command_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
558 if (fConvertEndian)
559 {
560 u.pLoadCmd->cmd = RT_BSWAP_U32(u.pLoadCmd->cmd);
561 u.pLoadCmd->cmdsize = RT_BSWAP_U32(u.pLoadCmd->cmdsize);
562 }
563 KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize <= cbLeft, VERR_LDRMACHO_BAD_LOAD_COMMAND);
564 cbLeft -= u.pLoadCmd->cmdsize;
565 pb += u.pLoadCmd->cmdsize;
566
567 /*
568 * Convert endian if needed, parse and validate the command.
569 */
570 switch (u.pLoadCmd->cmd)
571 {
572 case LC_SEGMENT_32:
573 {
574 segment_command_32_t *pSrcSeg = (segment_command_32_t *)u.pLoadCmd;
575 section_32_t *pFirstSect = (section_32_t *)(pSrcSeg + 1);
576 section_32_t *pSect = pFirstSect;
577 uint32_t cSectionsLeft = pSrcSeg->nsects;
578 uint64_t offSect = 0;
579
580 /* Convert and verify the segment. */
581 KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_32_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
582 KLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
583 || pHdr->magic == IMAGE_MACHO32_SIGNATURE, VERR_LDRMACHO_BIT_MIX);
584 if (fConvertEndian)
585 {
586 pSrcSeg->vmaddr = RT_BSWAP_U32(pSrcSeg->vmaddr);
587 pSrcSeg->vmsize = RT_BSWAP_U32(pSrcSeg->vmsize);
588 pSrcSeg->fileoff = RT_BSWAP_U32(pSrcSeg->fileoff);
589 pSrcSeg->filesize = RT_BSWAP_U32(pSrcSeg->filesize);
590 pSrcSeg->maxprot = RT_BSWAP_U32(pSrcSeg->maxprot);
591 pSrcSeg->initprot = RT_BSWAP_U32(pSrcSeg->initprot);
592 pSrcSeg->nsects = RT_BSWAP_U32(pSrcSeg->nsects);
593 pSrcSeg->flags = RT_BSWAP_U32(pSrcSeg->flags);
594 }
595
596 /* Validation code shared with the 64-bit variant. */
597 #define VALIDATE_AND_ADD_SEGMENT(a_cBits) \
598 do { \
599 bool fSkipSeg = !strcmp(pSrcSeg->segname, "__DWARF") /* Note: Not for non-object files. */ \
600 || ( !strcmp(pSrcSeg->segname, "__CTF") /* Their CTF tool did/does weird things, */ \
601 && pSrcSeg->vmsize == 0) /* overlapping vmaddr and zero vmsize. */ \
602 || (cSectionsLeft > 0 && (pFirstSect->flags & S_ATTR_DEBUG)); \
603 \
604 /* MH_DSYM files for MH_OBJECT files must have MH_OBJECT segment translation. */ \
605 if ( uEffFileType == MH_DSYM \
606 && cSegmentCommands == 0 \
607 && pSrcSeg->segname[0] == '\0') \
608 *puEffFileType = uEffFileType = MH_OBJECT; \
609 \
610 KLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize == 0 \
611 || ( pSrcSeg->fileoff <= cbFile \
612 && (uint64_t)pSrcSeg->fileoff + pSrcSeg->filesize <= cbFile), \
613 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
614 KLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize <= pSrcSeg->vmsize \
615 || (fSkipSeg && !strcmp(pSrcSeg->segname, "__CTF") /* see above */), \
616 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
617 KLDRMODMACHO_CHECK_RETURN(!(~pSrcSeg->maxprot & pSrcSeg->initprot), \
618 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
619 KLDRMODMACHO_CHECK_RETURN(!(pSrcSeg->flags & ~(SG_HIGHVM | SG_FVMLIB | SG_NORELOC | SG_PROTECTED_VERSION_1)), \
620 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
621 KLDRMODMACHO_CHECK_RETURN( pSrcSeg->nsects * sizeof(section_##a_cBits##_t) \
622 <= u.pLoadCmd->cmdsize - sizeof(segment_command_##a_cBits##_t), \
623 VERR_LDRMACHO_BAD_LOAD_COMMAND); \
624 KLDRMODMACHO_CHECK_RETURN( uEffFileType != MH_OBJECT \
625 || cSegmentCommands == 0 \
626 || ( cSegmentCommands == 1 \
627 && uEffFileType == MH_OBJECT \
628 && pHdr->filetype == MH_DSYM \
629 && fSkipSeg), \
630 VERR_LDRMACHO_BAD_OBJECT_FILE); \
631 cSegmentCommands++; \
632 \
633 /* Add the segment, if not object file. */ \
634 if (!fSkipSeg && uEffFileType != MH_OBJECT) \
635 { \
636 cbStringPool += RTStrNLen(&pSrcSeg->segname[0], sizeof(pSrcSeg->segname)) + 1; \
637 cSegments++; \
638 if (cSegments == 1) /* The link address is set by the first segment. */ \
639 *pLinkAddress = pSrcSeg->vmaddr; \
640 } \
641 } while (0)
642
643 VALIDATE_AND_ADD_SEGMENT(32);
644
645 /*
646 * Convert, validate and parse the sections.
647 */
648 cSectionsLeft = pSrcSeg->nsects;
649 pFirstSect = pSect = (section_32_t *)(pSrcSeg + 1);
650 while (cSectionsLeft-- > 0)
651 {
652 if (fConvertEndian)
653 {
654 pSect->addr = RT_BSWAP_U32(pSect->addr);
655 pSect->size = RT_BSWAP_U32(pSect->size);
656 pSect->offset = RT_BSWAP_U32(pSect->offset);
657 pSect->align = RT_BSWAP_U32(pSect->align);
658 pSect->reloff = RT_BSWAP_U32(pSect->reloff);
659 pSect->nreloc = RT_BSWAP_U32(pSect->nreloc);
660 pSect->flags = RT_BSWAP_U32(pSect->flags);
661 pSect->reserved1 = RT_BSWAP_U32(pSect->reserved1);
662 pSect->reserved2 = RT_BSWAP_U32(pSect->reserved2);
663 }
664
665 /* Validation code shared with the 64-bit variant. */
666 #define VALIDATE_AND_ADD_SECTION(a_cBits) \
667 do { \
668 int fFileBits; \
669 \
670 /* validate */ \
671 if (uEffFileType != MH_OBJECT) \
672 KLDRMODMACHO_CHECK_RETURN(!strcmp(pSect->segname, pSrcSeg->segname),\
673 VERR_LDRMACHO_BAD_SECTION); \
674 \
675 switch (pSect->flags & SECTION_TYPE) \
676 { \
677 case S_ZEROFILL: \
678 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
679 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
680 fFileBits = 0; \
681 break; \
682 case S_REGULAR: \
683 case S_CSTRING_LITERALS: \
684 case S_COALESCED: \
685 case S_4BYTE_LITERALS: \
686 case S_8BYTE_LITERALS: \
687 case S_16BYTE_LITERALS: \
688 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
689 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
690 fFileBits = 1; \
691 break; \
692 \
693 case S_SYMBOL_STUBS: \
694 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
695 /* reserved2 == stub size. 0 has been seen (corecrypto.kext) */ \
696 KLDRMODMACHO_CHECK_RETURN(pSect->reserved2 < 64, VERR_LDRMACHO_BAD_SECTION); \
697 fFileBits = 1; \
698 break; \
699 \
700 case S_NON_LAZY_SYMBOL_POINTERS: \
701 case S_LAZY_SYMBOL_POINTERS: \
702 case S_LAZY_DYLIB_SYMBOL_POINTERS: \
703 /* (reserved 1 = is indirect symbol table index) */ \
704 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
705 *pfCanLoad = false; \
706 fFileBits = -1; /* __DATA.__got in the 64-bit mach_kernel has bits, any things without bits? */ \
707 break; \
708 \
709 case S_MOD_INIT_FUNC_POINTERS: \
710 /** @todo this requires a query API or flag... (e.g. C++ constructors) */ \
711 KLDRMODMACHO_CHECK_RETURN(fOpenFlags & RTLDR_O_FOR_DEBUG, \
712 VERR_LDRMACHO_UNSUPPORTED_INIT_SECTION); \
713 /* Falls through. */ \
714 case S_MOD_TERM_FUNC_POINTERS: \
715 /** @todo this requires a query API or flag... (e.g. C++ destructors) */ \
716 KLDRMODMACHO_CHECK_RETURN(fOpenFlags & RTLDR_O_FOR_DEBUG, \
717 VERR_LDRMACHO_UNSUPPORTED_TERM_SECTION); \
718 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
719 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
720 fFileBits = 1; \
721 break; /* ignored */ \
722 \
723 case S_LITERAL_POINTERS: \
724 case S_DTRACE_DOF: \
725 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
726 KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
727 fFileBits = 1; \
728 break; \
729 \
730 case S_INTERPOSING: \
731 case S_GB_ZEROFILL: \
732 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNSUPPORTED_SECTION); \
733 \
734 default: \
735 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNKNOWN_SECTION); \
736 } \
737 KLDRMODMACHO_CHECK_RETURN(!(pSect->flags & ~( S_ATTR_PURE_INSTRUCTIONS | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS \
738 | S_ATTR_NO_DEAD_STRIP | S_ATTR_LIVE_SUPPORT | S_ATTR_SELF_MODIFYING_CODE \
739 | S_ATTR_DEBUG | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_EXT_RELOC \
740 | S_ATTR_LOC_RELOC | SECTION_TYPE)), \
741 VERR_LDRMACHO_BAD_SECTION); \
742 KLDRMODMACHO_CHECK_RETURN((pSect->flags & S_ATTR_DEBUG) == (pFirstSect->flags & S_ATTR_DEBUG), \
743 VERR_LDRMACHO_MIXED_DEBUG_SECTION_FLAGS); \
744 \
745 KLDRMODMACHO_CHECK_RETURN(pSect->addr - pSrcSeg->vmaddr <= pSrcSeg->vmsize, \
746 VERR_LDRMACHO_BAD_SECTION); \
747 KLDRMODMACHO_CHECK_RETURN( pSect->addr - pSrcSeg->vmaddr + pSect->size <= pSrcSeg->vmsize \
748 || !strcmp(pSrcSeg->segname, "__CTF") /* see above */, \
749 VERR_LDRMACHO_BAD_SECTION); \
750 KLDRMODMACHO_CHECK_RETURN(pSect->align < 31, \
751 VERR_LDRMACHO_BAD_SECTION); \
752 /* Workaround for buggy ld64 (or as, llvm, ++) that produces a misaligned __TEXT.__unwind_info. */ \
753 /* Seen: pSect->align = 4, pSect->addr = 0x5ebe14. Just adjust the alignment down. */ \
754 if ( ((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSect->addr) \
755 && pSect->align == 4 \
756 && strcmp(pSect->sectname, "__unwind_info") == 0) \
757 pSect->align = 2; \
758 KLDRMODMACHO_CHECK_RETURN(!((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSect->addr), \
759 VERR_LDRMACHO_BAD_SECTION); \
760 KLDRMODMACHO_CHECK_RETURN(!((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSrcSeg->vmaddr), \
761 VERR_LDRMACHO_BAD_SECTION); \
762 \
763 /* Adjust the section offset before we check file offset. */ \
764 offSect = (offSect + RT_BIT_64(pSect->align) - UINT64_C(1)) & ~(RT_BIT_64(pSect->align) - UINT64_C(1)); \
765 if (pSect->addr) \
766 { \
767 KLDRMODMACHO_CHECK_RETURN(offSect <= pSect->addr - pSrcSeg->vmaddr, VERR_LDRMACHO_BAD_SECTION); \
768 if (offSect < pSect->addr - pSrcSeg->vmaddr) \
769 offSect = pSect->addr - pSrcSeg->vmaddr; \
770 } \
771 \
772 if (fFileBits && pSect->offset == 0 && pSrcSeg->fileoff == 0 && pHdr->filetype == MH_DSYM) \
773 fFileBits = 0; \
774 if (fFileBits) \
775 { \
776 if (uEffFileType != MH_OBJECT) \
777 { \
778 KLDRMODMACHO_CHECK_RETURN(pSect->offset == pSrcSeg->fileoff + offSect, \
779 VERR_LDRMACHO_NON_CONT_SEG_BITS); \
780 KLDRMODMACHO_CHECK_RETURN(pSect->offset - pSrcSeg->fileoff <= pSrcSeg->filesize, \
781 VERR_LDRMACHO_BAD_SECTION); \
782 } \
783 KLDRMODMACHO_CHECK_RETURN(pSect->offset <= cbFile, \
784 VERR_LDRMACHO_BAD_SECTION); \
785 KLDRMODMACHO_CHECK_RETURN((uint64_t)pSect->offset + pSect->size <= cbFile, \
786 VERR_LDRMACHO_BAD_SECTION); \
787 } \
788 else \
789 KLDRMODMACHO_CHECK_RETURN(pSect->offset == 0, VERR_LDRMACHO_BAD_SECTION); \
790 \
791 if (!pSect->nreloc) \
792 KLDRMODMACHO_CHECK_RETURN(!pSect->reloff, \
793 VERR_LDRMACHO_BAD_SECTION); \
794 else \
795 { \
796 KLDRMODMACHO_CHECK_RETURN(pSect->reloff <= cbFile, \
797 VERR_LDRMACHO_BAD_SECTION); \
798 KLDRMODMACHO_CHECK_RETURN( (uint64_t)pSect->reloff \
799 + (RTFOFF)pSect->nreloc * sizeof(macho_relocation_info_t) \
800 <= cbFile, \
801 VERR_LDRMACHO_BAD_SECTION); \
802 } \
803 \
804 /* Validate against file type (pointless?) and count the section, for object files add segment. */ \
805 switch (uEffFileType) \
806 { \
807 case MH_OBJECT: \
808 if ( !(pSect->flags & S_ATTR_DEBUG) \
809 && strcmp(pSect->segname, "__DWARF")) \
810 { \
811 cbStringPool += RTStrNLen(&pSect->segname[0], sizeof(pSect->segname)) + 1; \
812 cbStringPool += RTStrNLen(&pSect->sectname[0], sizeof(pSect->sectname)) + 1; \
813 cSegments++; \
814 if (cSegments == 1) /* The link address is set by the first segment. */ \
815 *pLinkAddress = pSect->addr; \
816 } \
817 /* Falls through. */ \
818 case MH_EXECUTE: \
819 case MH_DYLIB: \
820 case MH_BUNDLE: \
821 case MH_DSYM: \
822 case MH_KEXT_BUNDLE: \
823 cSections++; \
824 break; \
825 default: \
826 KLDRMODMACHO_FAILED_RETURN(VERR_INVALID_PARAMETER); \
827 } \
828 \
829 /* Advance the section offset, since we're also aligning it. */ \
830 offSect += pSect->size; \
831 } while (0) /* VALIDATE_AND_ADD_SECTION */
832
833 VALIDATE_AND_ADD_SECTION(32);
834
835 /* next */
836 pSect++;
837 }
838 break;
839 }
840
841 case LC_SEGMENT_64:
842 {
843 segment_command_64_t *pSrcSeg = (segment_command_64_t *)u.pLoadCmd;
844 section_64_t *pFirstSect = (section_64_t *)(pSrcSeg + 1);
845 section_64_t *pSect = pFirstSect;
846 uint32_t cSectionsLeft = pSrcSeg->nsects;
847 uint64_t offSect = 0;
848
849 /* Convert and verify the segment. */
850 KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_64_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
851 KLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE
852 || pHdr->magic == IMAGE_MACHO64_SIGNATURE, VERR_LDRMACHO_BIT_MIX);
853 if (fConvertEndian)
854 {
855 pSrcSeg->vmaddr = RT_BSWAP_U64(pSrcSeg->vmaddr);
856 pSrcSeg->vmsize = RT_BSWAP_U64(pSrcSeg->vmsize);
857 pSrcSeg->fileoff = RT_BSWAP_U64(pSrcSeg->fileoff);
858 pSrcSeg->filesize = RT_BSWAP_U64(pSrcSeg->filesize);
859 pSrcSeg->maxprot = RT_BSWAP_U32(pSrcSeg->maxprot);
860 pSrcSeg->initprot = RT_BSWAP_U32(pSrcSeg->initprot);
861 pSrcSeg->nsects = RT_BSWAP_U32(pSrcSeg->nsects);
862 pSrcSeg->flags = RT_BSWAP_U32(pSrcSeg->flags);
863 }
864
865 VALIDATE_AND_ADD_SEGMENT(64);
866
867 /*
868 * Convert, validate and parse the sections.
869 */
870 while (cSectionsLeft-- > 0)
871 {
872 if (fConvertEndian)
873 {
874 pSect->addr = RT_BSWAP_U64(pSect->addr);
875 pSect->size = RT_BSWAP_U64(pSect->size);
876 pSect->offset = RT_BSWAP_U32(pSect->offset);
877 pSect->align = RT_BSWAP_U32(pSect->align);
878 pSect->reloff = RT_BSWAP_U32(pSect->reloff);
879 pSect->nreloc = RT_BSWAP_U32(pSect->nreloc);
880 pSect->flags = RT_BSWAP_U32(pSect->flags);
881 pSect->reserved1 = RT_BSWAP_U32(pSect->reserved1);
882 pSect->reserved2 = RT_BSWAP_U32(pSect->reserved2);
883 }
884
885 VALIDATE_AND_ADD_SECTION(64);
886
887 /* next */
888 pSect++;
889 }
890 break;
891 } /* LC_SEGMENT_64 */
892
893
894 case LC_SYMTAB:
895 {
896 size_t cbSym;
897 if (fConvertEndian)
898 {
899 u.pSymTab->symoff = RT_BSWAP_U32(u.pSymTab->symoff);
900 u.pSymTab->nsyms = RT_BSWAP_U32(u.pSymTab->nsyms);
901 u.pSymTab->stroff = RT_BSWAP_U32(u.pSymTab->stroff);
902 u.pSymTab->strsize = RT_BSWAP_U32(u.pSymTab->strsize);
903 }
904
905 /* verify */
906 cbSym = pHdr->magic == IMAGE_MACHO32_SIGNATURE
907 || pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
908 ? sizeof(macho_nlist_32_t)
909 : sizeof(macho_nlist_64_t);
910 if ( u.pSymTab->symoff >= cbFile
911 || (uint64_t)u.pSymTab->symoff + u.pSymTab->nsyms * cbSym > cbFile)
912 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
913 if ( u.pSymTab->stroff >= cbFile
914 || (uint64_t)u.pSymTab->stroff + u.pSymTab->strsize > cbFile)
915 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
916
917 /* only one string in objects, please. */
918 cSymbolTabs++;
919 if ( uEffFileType == MH_OBJECT
920 && cSymbolTabs != 1)
921 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_OBJECT_FILE);
922 break;
923 }
924
925 case LC_DYSYMTAB:
926 /** @todo deal with this! */
927 break;
928
929 case LC_THREAD:
930 case LC_UNIXTHREAD:
931 {
932 uint32_t *pu32 = (uint32_t *)(u.pb + sizeof(load_command_t));
933 uint32_t cItemsLeft = (u.pThread->cmdsize - sizeof(load_command_t)) / sizeof(uint32_t);
934 while (cItemsLeft)
935 {
936 /* convert & verify header items ([0] == flavor, [1] == uint32_t count). */
937 if (cItemsLeft < 2)
938 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
939 if (fConvertEndian)
940 {
941 pu32[0] = RT_BSWAP_U32(pu32[0]);
942 pu32[1] = RT_BSWAP_U32(pu32[1]);
943 }
944 if (pu32[1] + 2 > cItemsLeft)
945 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
946
947 /* convert & verify according to flavor. */
948 switch (pu32[0])
949 {
950 /** @todo */
951 default:
952 break;
953 }
954
955 /* next */
956 cItemsLeft -= pu32[1] + 2;
957 pu32 += pu32[1] + 2;
958 }
959 break;
960 }
961
962 case LC_UUID:
963 if (u.pUuid->cmdsize != sizeof(uuid_command_t))
964 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
965 /** @todo Check anything here need converting? */
966 break;
967
968 case LC_CODE_SIGNATURE:
969 if (u.pUuid->cmdsize != sizeof(linkedit_data_command_t))
970 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
971 break;
972
973 case LC_VERSION_MIN_MACOSX:
974 case LC_VERSION_MIN_IPHONEOS:
975 if (u.pUuid->cmdsize != sizeof(version_min_command_t))
976 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
977 break;
978
979 case LC_SOURCE_VERSION: /* Harmless. It just gives a clue regarding the source code revision/version. */
980 case LC_DATA_IN_CODE: /* Ignore */
981 case LC_DYLIB_CODE_SIGN_DRS:/* Ignore */
982 /** @todo valid command size. */
983 break;
984
985 case LC_FUNCTION_STARTS: /** @todo dylib++ */
986 /* Ignore for now. */
987 break;
988 case LC_ID_DYLIB: /** @todo dylib */
989 case LC_LOAD_DYLIB: /** @todo dylib */
990 case LC_LOAD_DYLINKER: /** @todo dylib */
991 case LC_TWOLEVEL_HINTS: /** @todo dylib */
992 case LC_LOAD_WEAK_DYLIB: /** @todo dylib */
993 case LC_ID_DYLINKER: /** @todo dylib */
994 case LC_RPATH: /** @todo dylib */
995 case LC_SEGMENT_SPLIT_INFO: /** @todo dylib++ */
996 case LC_REEXPORT_DYLIB: /** @todo dylib */
997 case LC_DYLD_INFO: /** @todo dylib */
998 case LC_DYLD_INFO_ONLY: /** @todo dylib */
999 case LC_LOAD_UPWARD_DYLIB: /** @todo dylib */
1000 case LC_DYLD_ENVIRONMENT: /** @todo dylib */
1001 case LC_MAIN: /** @todo parse this and find and entry point or smth. */
1002 /** @todo valid command size. */
1003 if (!(fOpenFlags & RTLDR_O_FOR_DEBUG))
1004 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNSUPPORTED_LOAD_COMMAND);
1005 *pfCanLoad = false;
1006 break;
1007
1008 case LC_LOADFVMLIB:
1009 case LC_IDFVMLIB:
1010 case LC_IDENT:
1011 case LC_FVMFILE:
1012 case LC_PREPAGE:
1013 case LC_PREBOUND_DYLIB:
1014 case LC_ROUTINES:
1015 case LC_ROUTINES_64:
1016 case LC_SUB_FRAMEWORK:
1017 case LC_SUB_UMBRELLA:
1018 case LC_SUB_CLIENT:
1019 case LC_SUB_LIBRARY:
1020 case LC_PREBIND_CKSUM:
1021 case LC_SYMSEG:
1022 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNSUPPORTED_LOAD_COMMAND);
1023
1024 default:
1025 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNKNOWN_LOAD_COMMAND);
1026 }
1027 }
1028
1029 /* be strict. */
1030 if (cbLeft)
1031 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
1032
1033 switch (uEffFileType)
1034 {
1035 case MH_OBJECT:
1036 case MH_EXECUTE:
1037 case MH_DYLIB:
1038 case MH_BUNDLE:
1039 case MH_DSYM:
1040 case MH_KEXT_BUNDLE:
1041 if (!cSegments)
1042 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_OBJECT_FILE);
1043 break;
1044 }
1045
1046 *pcSegments = cSegments;
1047 *pcSections = cSections;
1048 *pcbStringPool = (uint32_t)cbStringPool;
1049
1050 return VINF_SUCCESS;
1051}
1052
1053
1054/**
1055 * Parses the load commands after we've carved out the module instance.
1056 *
1057 * This fills in the segment table and perhaps some other properties.
1058 *
1059 * @returns IPRT status code.
1060 * @param pThis The module.
1061 * @param pbStringPool The string pool
1062 * @param cbStringPool The size of the string pool.
1063 */
1064static int kldrModMachOParseLoadCommands(PKLDRMODMACHO pThis, char *pbStringPool, uint32_t cbStringPool)
1065{
1066 union
1067 {
1068 const uint8_t *pb;
1069 const load_command_t *pLoadCmd;
1070 const segment_command_32_t *pSeg32;
1071 const segment_command_64_t *pSeg64;
1072 const symtab_command_t *pSymTab;
1073 const uuid_command_t *pUuid;
1074 } u;
1075 uint32_t cLeft = pThis->Hdr.ncmds;
1076 uint32_t cbLeft = pThis->Hdr.sizeofcmds;
1077 const uint8_t *pb = pThis->pbLoadCommands;
1078 PKLDRMODMACHOSEG pDstSeg = &pThis->aSegments[0];
1079 PKLDRMODMACHOSECT pSectExtra = pThis->paSections;
1080 const uint32_t cSegments = pThis->cSegments;
1081 PKLDRMODMACHOSEG pSegItr;
1082 RT_NOREF(cbStringPool);
1083
1084 while (cLeft-- > 0)
1085 {
1086 u.pb = pb;
1087 cbLeft -= u.pLoadCmd->cmdsize;
1088 pb += u.pLoadCmd->cmdsize;
1089
1090 /*
1091 * Convert endian if needed, parse and validate the command.
1092 */
1093 switch (u.pLoadCmd->cmd)
1094 {
1095 case LC_SEGMENT_32:
1096 {
1097 const segment_command_32_t *pSrcSeg = (const segment_command_32_t *)u.pLoadCmd;
1098 section_32_t *pFirstSect = (section_32_t *)(pSrcSeg + 1);
1099 section_32_t *pSect = pFirstSect;
1100 uint32_t cSectionsLeft = pSrcSeg->nsects;
1101
1102 /* Adds a segment, used by the macro below and thus shared with the 64-bit segment variant. */
1103#define NEW_SEGMENT(a_cBits, a_achName1, a_fObjFile, a_achName2, a_SegAddr, a_cbSeg, a_fFileBits, a_offFile, a_cbFile) \
1104 do { \
1105 pDstSeg->SegInfo.pszName = pbStringPool; \
1106 pDstSeg->SegInfo.cchName = (uint32_t)RTStrNLen(a_achName1, sizeof(a_achName1)); \
1107 memcpy(pbStringPool, a_achName1, pDstSeg->SegInfo.cchName); \
1108 pbStringPool += pDstSeg->SegInfo.cchName; \
1109 if (a_fObjFile) \
1110 { /* MH_OBJECT: Add '.sectname' - sections aren't sorted by segments. */ \
1111 size_t cchName2 = RTStrNLen(a_achName2, sizeof(a_achName2)); \
1112 *pbStringPool++ = '.'; \
1113 memcpy(pbStringPool, a_achName2, cchName2); \
1114 pbStringPool += cchName2; \
1115 pDstSeg->SegInfo.cchName += (uint32_t)cchName2; \
1116 } \
1117 *pbStringPool++ = '\0'; \
1118 pDstSeg->SegInfo.SelFlat = 0; \
1119 pDstSeg->SegInfo.Sel16bit = 0; \
1120 pDstSeg->SegInfo.fFlags = 0; \
1121 pDstSeg->SegInfo.fProt = RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC; /** @todo fixme! */ \
1122 pDstSeg->SegInfo.cb = (a_cbSeg); \
1123 pDstSeg->SegInfo.Alignment = 1; /* updated while parsing sections. */ \
1124 pDstSeg->SegInfo.LinkAddress = (a_SegAddr); \
1125 if (a_fFileBits) \
1126 { \
1127 pDstSeg->SegInfo.offFile = (RTFOFF)((a_offFile) + pThis->offImage); \
1128 pDstSeg->SegInfo.cbFile = (RTFOFF)(a_cbFile); \
1129 } \
1130 else \
1131 { \
1132 pDstSeg->SegInfo.offFile = -1; \
1133 pDstSeg->SegInfo.cbFile = -1; \
1134 } \
1135 pDstSeg->SegInfo.RVA = (a_SegAddr) - pThis->LinkAddress; \
1136 pDstSeg->SegInfo.cbMapped = 0; \
1137 \
1138 pDstSeg->iOrgSegNo = (uint32_t)(pDstSeg - &pThis->aSegments[0]); \
1139 pDstSeg->cSections = 0; \
1140 pDstSeg->paSections = pSectExtra; \
1141 } while (0)
1142
1143 /* Closes the new segment - part of NEW_SEGMENT. */
1144#define CLOSE_SEGMENT() \
1145 do { \
1146 pDstSeg->cSections = (uint32_t)(pSectExtra - pDstSeg->paSections); \
1147 pDstSeg++; \
1148 } while (0)
1149
1150
1151 /* Shared with the 64-bit variant. */
1152#define ADD_SEGMENT_AND_ITS_SECTIONS(a_cBits) \
1153 do { \
1154 bool fAddSegOuter = false; \
1155 \
1156 /* \
1157 * Check that the segment name is unique. We couldn't do that \
1158 * in the preparsing stage. \
1159 */ \
1160 if (pThis->uEffFileType != MH_OBJECT) \
1161 for (pSegItr = &pThis->aSegments[0]; pSegItr != pDstSeg; pSegItr++) \
1162 if (!strncmp(pSegItr->SegInfo.pszName, pSrcSeg->segname, sizeof(pSrcSeg->segname))) \
1163 KLDRMODMACHO_FAILED_RETURN(VERR_LDR_DUPLICATE_SEGMENT_NAME); \
1164 \
1165 /* \
1166 * Create a new segment, unless we're supposed to skip this one. \
1167 */ \
1168 if ( pThis->uEffFileType != MH_OBJECT \
1169 && (cSectionsLeft == 0 || !(pFirstSect->flags & S_ATTR_DEBUG)) \
1170 && strcmp(pSrcSeg->segname, "__DWARF") \
1171 && strcmp(pSrcSeg->segname, "__CTF") ) \
1172 { \
1173 NEW_SEGMENT(a_cBits, pSrcSeg->segname, false /*a_fObjFile*/, "" /*a_achName2*/, \
1174 pSrcSeg->vmaddr, pSrcSeg->vmsize, \
1175 pSrcSeg->filesize != 0, pSrcSeg->fileoff, pSrcSeg->filesize); \
1176 fAddSegOuter = true; \
1177 } \
1178 \
1179 /* \
1180 * Convert and parse the sections. \
1181 */ \
1182 while (cSectionsLeft-- > 0) \
1183 { \
1184 /* New segment if object file. */ \
1185 bool fAddSegInner = false; \
1186 if ( pThis->uEffFileType == MH_OBJECT \
1187 && !(pSect->flags & S_ATTR_DEBUG) \
1188 && strcmp(pSrcSeg->segname, "__DWARF") \
1189 && strcmp(pSrcSeg->segname, "__CTF") ) \
1190 { \
1191 Assert(!fAddSegOuter); \
1192 NEW_SEGMENT(a_cBits, pSect->segname, true /*a_fObjFile*/, pSect->sectname, \
1193 pSect->addr, pSect->size, \
1194 pSect->offset != 0, pSect->offset, pSect->size); \
1195 fAddSegInner = true; \
1196 } \
1197 \
1198 /* Section data extract. */ \
1199 pSectExtra->cb = pSect->size; \
1200 pSectExtra->RVA = pSect->addr - pDstSeg->SegInfo.LinkAddress; \
1201 pSectExtra->LinkAddress = pSect->addr; \
1202 if (pSect->offset) \
1203 pSectExtra->offFile = pSect->offset + pThis->offImage; \
1204 else \
1205 pSectExtra->offFile = -1; \
1206 pSectExtra->cFixups = pSect->nreloc; \
1207 pSectExtra->paFixups = NULL; \
1208 if (pSect->nreloc) \
1209 pSectExtra->offFixups = pSect->reloff + pThis->offImage; \
1210 else \
1211 pSectExtra->offFixups = -1; \
1212 pSectExtra->fFlags = pSect->flags; \
1213 pSectExtra->iSegment = (uint32_t)(pDstSeg - &pThis->aSegments[0]); \
1214 pSectExtra->pvMachoSection = pSect; \
1215 \
1216 /* Update the segment alignment, if we're not skipping it. */ \
1217 if ( (fAddSegOuter || fAddSegInner) \
1218 && pDstSeg->SegInfo.Alignment < ((RTLDRADDR)1 << pSect->align)) \
1219 pDstSeg->SegInfo.Alignment = (RTLDRADDR)1 << pSect->align; \
1220 \
1221 /* Next section, and if object file next segment. */ \
1222 pSectExtra++; \
1223 pSect++; \
1224 if (fAddSegInner) \
1225 CLOSE_SEGMENT(); \
1226 } \
1227 \
1228 /* Close the segment and advance. */ \
1229 if (fAddSegOuter) \
1230 CLOSE_SEGMENT(); \
1231 } while (0) /* ADD_SEGMENT_AND_ITS_SECTIONS */
1232
1233 ADD_SEGMENT_AND_ITS_SECTIONS(32);
1234 break;
1235 }
1236
1237 case LC_SEGMENT_64:
1238 {
1239 const segment_command_64_t *pSrcSeg = (const segment_command_64_t *)u.pLoadCmd;
1240 section_64_t *pFirstSect = (section_64_t *)(pSrcSeg + 1);
1241 section_64_t *pSect = pFirstSect;
1242 uint32_t cSectionsLeft = pSrcSeg->nsects;
1243
1244 ADD_SEGMENT_AND_ITS_SECTIONS(64);
1245 break;
1246 }
1247
1248 case LC_SYMTAB:
1249 switch (pThis->uEffFileType)
1250 {
1251 case MH_OBJECT:
1252 case MH_EXECUTE:
1253 case MH_DYLIB: /** @todo ??? */
1254 case MH_BUNDLE: /** @todo ??? */
1255 case MH_DSYM:
1256 case MH_KEXT_BUNDLE:
1257 pThis->offSymbols = u.pSymTab->symoff + pThis->offImage;
1258 pThis->cSymbols = u.pSymTab->nsyms;
1259 pThis->offStrings = u.pSymTab->stroff + pThis->offImage;
1260 pThis->cchStrings = u.pSymTab->strsize;
1261 break;
1262 }
1263 break;
1264
1265 case LC_UUID:
1266 memcpy(pThis->abImageUuid, u.pUuid->uuid, sizeof(pThis->abImageUuid));
1267 break;
1268
1269 default:
1270 break;
1271 } /* command switch */
1272 } /* while more commands */
1273
1274 Assert(pDstSeg == &pThis->aSegments[cSegments - pThis->fMakeGot]);
1275
1276 /*
1277 * Adjust mapping addresses calculating the image size.
1278 */
1279 {
1280 bool fLoadLinkEdit = false;
1281 PKLDRMODMACHOSECT pSectExtraItr;
1282 RTLDRADDR uNextRVA = 0;
1283 RTLDRADDR cb;
1284 uint32_t cSegmentsToAdjust = cSegments - pThis->fMakeGot;
1285 uint32_t c;
1286
1287 for (;;)
1288 {
1289 /* Check if there is __DWARF segment at the end and make sure it's left
1290 out of the RVA negotiations and image loading. */
1291 if ( cSegmentsToAdjust > 0
1292 && !strcmp(pThis->aSegments[cSegmentsToAdjust - 1].SegInfo.pszName, "__DWARF"))
1293 {
1294 cSegmentsToAdjust--;
1295 pThis->aSegments[cSegmentsToAdjust].SegInfo.RVA = NIL_RTLDRADDR;
1296 pThis->aSegments[cSegmentsToAdjust].SegInfo.cbMapped = 0;
1297 continue;
1298 }
1299
1300 /* If we're skipping the __LINKEDIT segment, check for it and adjust
1301 the number of segments we'll be messing with here. ASSUMES it's
1302 last (by now anyway). */
1303 if ( !fLoadLinkEdit
1304 && cSegmentsToAdjust > 0
1305 && !strcmp(pThis->aSegments[cSegmentsToAdjust - 1].SegInfo.pszName, "__LINKEDIT"))
1306 {
1307 cSegmentsToAdjust--;
1308 pThis->aSegments[cSegmentsToAdjust].SegInfo.RVA = NIL_RTLDRADDR;
1309 pThis->aSegments[cSegmentsToAdjust].SegInfo.cbMapped = 0;
1310 continue;
1311 }
1312 break;
1313 }
1314
1315 /* Adjust RVAs. */
1316 c = cSegmentsToAdjust;
1317 for (pDstSeg = &pThis->aSegments[0]; c-- > 0; pDstSeg++)
1318 {
1319 cb = pDstSeg->SegInfo.RVA - uNextRVA;
1320 if (cb >= 0x00100000) /* 1MB */
1321 {
1322 pDstSeg->SegInfo.RVA = uNextRVA;
1323 //pThis->pMod->fFlags |= KLDRMOD_FLAGS_NON_CONTIGUOUS_LINK_ADDRS;
1324 }
1325 uNextRVA = pDstSeg->SegInfo.RVA + RTLDR_ALIGN_ADDR(pDstSeg->SegInfo.cb, pDstSeg->SegInfo.Alignment);
1326 }
1327
1328 /* Calculate the cbMapping members. */
1329 c = cSegmentsToAdjust;
1330 for (pDstSeg = &pThis->aSegments[0]; c-- > 1; pDstSeg++)
1331 {
1332
1333 cb = pDstSeg[1].SegInfo.RVA - pDstSeg->SegInfo.RVA;
1334 pDstSeg->SegInfo.cbMapped = (size_t)cb == cb ? (size_t)cb : ~(size_t)0;
1335 }
1336
1337 cb = RTLDR_ALIGN_ADDR(pDstSeg->SegInfo.cb, pDstSeg->SegInfo.Alignment);
1338 pDstSeg->SegInfo.cbMapped = (size_t)cb == cb ? (size_t)cb : ~(size_t)0;
1339
1340 /* Set the image size. */
1341 pThis->cbImage = pDstSeg->SegInfo.RVA + cb;
1342
1343 /* Fixup the section RVAs (internal). */
1344 c = cSegmentsToAdjust;
1345 uNextRVA = pThis->cbImage;
1346 pDstSeg = &pThis->aSegments[0];
1347 for (pSectExtraItr = pThis->paSections; pSectExtraItr != pSectExtra; pSectExtraItr++)
1348 {
1349 if (pSectExtraItr->iSegment < c)
1350 pSectExtraItr->RVA += pDstSeg[pSectExtraItr->iSegment].SegInfo.RVA;
1351 else
1352 {
1353 pSectExtraItr->RVA = uNextRVA;
1354 uNextRVA += RTLDR_ALIGN_ADDR(pSectExtraItr->cb, 64);
1355 }
1356 }
1357 }
1358
1359 /*
1360 * Make the GOT segment if necessary.
1361 */
1362 if (pThis->fMakeGot)
1363 {
1364 uint32_t cbPtr = ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
1365 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
1366 ? sizeof(uint32_t)
1367 : sizeof(uint64_t);
1368 uint32_t cbGot = pThis->cSymbols * cbPtr;
1369 uint32_t cbJmpStubs;
1370
1371 pThis->GotRVA = pThis->cbImage;
1372
1373 if (pThis->cbJmpStub)
1374 {
1375 cbGot = RT_ALIGN_Z(cbGot, 64);
1376 pThis->JmpStubsRVA = pThis->GotRVA + cbGot;
1377 cbJmpStubs = pThis->cbJmpStub * pThis->cSymbols;
1378 }
1379 else
1380 {
1381 pThis->JmpStubsRVA = NIL_RTLDRADDR;
1382 cbJmpStubs = 0;
1383 }
1384
1385 pDstSeg = &pThis->aSegments[cSegments - 1];
1386 pDstSeg->SegInfo.pszName = "GOT";
1387 pDstSeg->SegInfo.cchName = 3;
1388 pDstSeg->SegInfo.SelFlat = 0;
1389 pDstSeg->SegInfo.Sel16bit = 0;
1390 pDstSeg->SegInfo.fFlags = 0;
1391 pDstSeg->SegInfo.fProt = RTMEM_PROT_READ;
1392 pDstSeg->SegInfo.cb = cbGot + cbJmpStubs;
1393 pDstSeg->SegInfo.Alignment = 64;
1394 pDstSeg->SegInfo.LinkAddress = pThis->LinkAddress + pThis->GotRVA;
1395 pDstSeg->SegInfo.offFile = -1;
1396 pDstSeg->SegInfo.cbFile = -1;
1397 pDstSeg->SegInfo.RVA = pThis->GotRVA;
1398 pDstSeg->SegInfo.cbMapped = (size_t)RTLDR_ALIGN_ADDR(cbGot + cbJmpStubs, pDstSeg->SegInfo.Alignment);
1399
1400 pDstSeg->iOrgSegNo = UINT32_MAX;
1401 pDstSeg->cSections = 0;
1402 pDstSeg->paSections = NULL;
1403
1404 pThis->cbImage += pDstSeg->SegInfo.cbMapped;
1405 }
1406
1407 return VINF_SUCCESS;
1408}
1409
1410
1411/**
1412 * @interface_method_impl{RTLDROPS,pfnClose}
1413 */
1414static DECLCALLBACK(int) rtldrMachO_Close(PRTLDRMODINTERNAL pMod)
1415{
1416 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
1417 int rc = VINF_SUCCESS;
1418 KLDRMODMACHO_ASSERT(!pThis->pvMapping);
1419
1420 uint32_t i = pThis->cSegments;
1421 while (i-- > 0)
1422 {
1423 uint32_t j = pThis->aSegments[i].cSections;
1424 while (j-- > 0)
1425 {
1426 RTMemFree(pThis->aSegments[i].paSections[j].paFixups);
1427 pThis->aSegments[i].paSections[j].paFixups = NULL;
1428 }
1429 }
1430
1431 if (pThis->Core.pReader)
1432 {
1433 rc = pThis->Core.pReader->pfnDestroy(pThis->Core.pReader);
1434 pThis->Core.pReader = NULL;
1435 }
1436 pThis->Core.u32Magic = 0;
1437 pThis->Core.pOps = NULL;
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 RTMemFree(pThis);
1445 return rc;
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/** @copydoc kLdrModQueryImageUuid */
2295static int kldrModMachOQueryImageUuid(PKLDRMODMACHO pThis, const void *pvBits, void *pvUuid, size_t cbUuid)
2296{
2297 RT_NOREF(pvBits);
2298
2299 memset(pvUuid, 0, cbUuid);
2300 if (memcmp(pvUuid, pThis->abImageUuid, sizeof(pThis->abImageUuid)) == 0)
2301 return VERR_NOT_FOUND;
2302
2303 memcpy(pvUuid, pThis->abImageUuid, sizeof(pThis->abImageUuid));
2304 return VINF_SUCCESS;
2305}
2306
2307
2308/**
2309 * @interface_method_impl{RTLDROPS,pfnEnumDbgInfo}
2310 */
2311static DECLCALLBACK(int) rtldrMachO_EnumDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, PFNRTLDRENUMDBG pfnCallback, void *pvUser)
2312{
2313 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2314 int rc = VINF_SUCCESS;
2315 uint32_t iSect;
2316 RT_NOREF(pvBits);
2317
2318 for (iSect = 0; iSect < pThis->cSections; iSect++)
2319 {
2320 /* (32-bit & 64-bit starts the same way) */
2321 section_32_t *pMachOSect = (section_32_t *)pThis->paSections[iSect].pvMachoSection;
2322 char szTmp[sizeof(pMachOSect->sectname) + 1];
2323
2324 if (strcmp(pMachOSect->segname, "__DWARF"))
2325 continue;
2326
2327 memcpy(szTmp, pMachOSect->sectname, sizeof(pMachOSect->sectname));
2328 szTmp[sizeof(pMachOSect->sectname)] = '\0';
2329
2330 RTLDRDBGINFO DbgInfo;
2331 DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF;
2332 DbgInfo.iDbgInfo = iSect;
2333 DbgInfo.LinkAddress = pThis->paSections[iSect].LinkAddress;
2334 DbgInfo.cb = pThis->paSections[iSect].cb;
2335 DbgInfo.pszExtFile = NULL;
2336 DbgInfo.u.Dwarf.pszSection = szTmp;
2337 rc = pfnCallback(&pThis->Core, &DbgInfo, pvUser);
2338 if (rc != VINF_SUCCESS)
2339 break;
2340 }
2341
2342 return rc;
2343}
2344
2345#if 0
2346
2347/** @copydoc kLdrModHasDbgInfo */
2348static int kldrModMachOHasDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits)
2349{
2350 /*PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);*/
2351
2352#if 0
2353 /*
2354 * Base this entirely on the presence of a debug directory.
2355 */
2356 if ( pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size
2357 < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */
2358 || !pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)
2359 return KLDR_ERR_NO_DEBUG_INFO;
2360 return VINF_SUCCESS;
2361#else
2362 RT_NOREF(pMod);
2363 RT_NOREF(pvBits);
2364 return VERR_LDR_NO_DEBUG_INFO;
2365#endif
2366}
2367
2368
2369/** @copydoc kLdrModMap */
2370static int kldrModMachOMap(PRTLDRMODINTERNAL pMod)
2371{
2372 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2373 unsigned fFixed;
2374 uint32_t i;
2375 void *pvBase;
2376 int rc;
2377
2378 if (!pThis->fCanLoad)
2379 return VERR_LDRMACHO_TODO;
2380
2381 /*
2382 * Already mapped?
2383 */
2384 if (pThis->pvMapping)
2385 return KLDR_ERR_ALREADY_MAPPED;
2386
2387 /*
2388 * Map it.
2389 */
2390 /* fixed image? */
2391 fFixed = pMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2392 || pMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2393 if (!fFixed)
2394 pvBase = NULL;
2395 else
2396 {
2397 pvBase = (void *)(uintptr_t)pMod->aSegments[0].LinkAddress;
2398 if ((uintptr_t)pvBase != pMod->aSegments[0].LinkAddress)
2399 return VERR_LDR_ADDRESS_OVERFLOW;
2400 }
2401
2402 /* try do the prepare */
2403 rc = kRdrMap(pMod->pRdr, &pvBase, pMod->cSegments, pMod->aSegments, fFixed);
2404 if (RT_FAILURE(rc))
2405 return rc;
2406
2407 /*
2408 * Update the segments with their map addresses.
2409 */
2410 for (i = 0; i < pMod->cSegments; i++)
2411 {
2412 if (pMod->aSegments[i].RVA != NIL_RTLDRADDR)
2413 pMod->aSegments[i].MapAddress = (uintptr_t)pvBase + (uintptr_t)pMod->aSegments[i].RVA;
2414 }
2415 pThis->pvMapping = pvBase;
2416
2417 return VINF_SUCCESS;
2418}
2419
2420
2421/** @copydoc kLdrModUnmap */
2422static int kldrModMachOUnmap(PRTLDRMODINTERNAL pMod)
2423{
2424 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2425 uint32_t i;
2426 int rc;
2427
2428 /*
2429 * Mapped?
2430 */
2431 if (!pThis->pvMapping)
2432 return KLDR_ERR_NOT_MAPPED;
2433
2434 /*
2435 * Try unmap the image.
2436 */
2437 rc = kRdrUnmap(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
2438 if (RT_FAILURE(rc))
2439 return rc;
2440
2441 /*
2442 * Update the segments to reflect that they aren't mapped any longer.
2443 */
2444 pThis->pvMapping = NULL;
2445 for (i = 0; i < pMod->cSegments; i++)
2446 pMod->aSegments[i].MapAddress = 0;
2447
2448 return VINF_SUCCESS;
2449}
2450
2451
2452/** @copydoc kLdrModAllocTLS */
2453static int kldrModMachOAllocTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
2454{
2455 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2456
2457 /*
2458 * Mapped?
2459 */
2460 if ( pvMapping == KLDRMOD_INT_MAP
2461 && !pThis->pvMapping )
2462 return KLDR_ERR_NOT_MAPPED;
2463 return VINF_SUCCESS;
2464}
2465
2466
2467/** @copydoc kLdrModFreeTLS */
2468static void kldrModMachOFreeTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
2469{
2470 RT_NOREF(pMod);
2471 RT_NOREF(pvMapping);
2472}
2473
2474
2475
2476/** @copydoc kLdrModReload */
2477static int kldrModMachOReload(PRTLDRMODINTERNAL pMod)
2478{
2479 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2480
2481 /*
2482 * Mapped?
2483 */
2484 if (!pThis->pvMapping)
2485 return KLDR_ERR_NOT_MAPPED;
2486
2487 /* the file provider does it all */
2488 return kRdrRefresh(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
2489}
2490
2491
2492/** @copydoc kLdrModFixupMapping */
2493static int kldrModMachOFixupMapping(PRTLDRMODINTERNAL pMod, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
2494{
2495 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
2496 int rc, rc2;
2497
2498 /*
2499 * Mapped?
2500 */
2501 if (!pThis->pvMapping)
2502 return KLDR_ERR_NOT_MAPPED;
2503
2504 /*
2505 * Before doing anything we'll have to make all pages writable.
2506 */
2507 rc = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */);
2508 if (RT_FAILURE(rc))
2509 return rc;
2510
2511 /*
2512 * Resolve imports and apply base relocations.
2513 */
2514 rc = rtldrMachO_RelocateBits(pMod, pThis->pvMapping, (uintptr_t)pThis->pvMapping, pThis->LinkAddress,
2515 pfnGetImport, pvUser);
2516
2517 /*
2518 * Restore protection.
2519 */
2520 rc2 = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */);
2521 if (RT_SUCCESS(rc) && RT_FAILURE(rc2)
2522 rc = rc2;
2523 return rc;
2524}
2525
2526#endif
2527
2528/**
2529 * MH_OBJECT: Resolves undefined symbols (imports).
2530 *
2531 * @returns IPRT status code.
2532 * @param pThis The Mach-O module interpreter instance.
2533 * @param pfnGetImport The callback for resolving an imported symbol.
2534 * @param pvUser User argument to the callback.
2535 */
2536static int kldrModMachOObjDoImports(PKLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
2537{
2538 const uint32_t cSyms = pThis->cSymbols;
2539 uint32_t iSym;
2540
2541 /*
2542 * Ensure that we've got the symbol table and section fixups handy.
2543 */
2544 int rc = kldrModMachOLoadObjSymTab(pThis);
2545 if (RT_FAILURE(rc))
2546 return rc;
2547
2548 /*
2549 * Iterate the symbol table and resolve undefined symbols.
2550 * We currently ignore REFERENCE_TYPE.
2551 */
2552 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
2553 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
2554 {
2555 macho_nlist_32_t *paSyms = (macho_nlist_32_t *)pThis->pvaSymbols;
2556 for (iSym = 0; iSym < cSyms; iSym++)
2557 {
2558 /* skip stabs */
2559 if (paSyms[iSym].n_type & MACHO_N_STAB)
2560 continue;
2561
2562 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2563 {
2564 const char *pszSymbol;
2565 size_t cchSymbol;
2566 //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
2567 RTLDRADDR Value = NIL_RTLDRADDR;
2568
2569 /** @todo Implement N_REF_TO_WEAK. */
2570 KLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
2571
2572 /* Get the symbol name. */
2573 KLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2574 pszSymbol = &pThis->pchStrings[paSyms[iSym].n_un.n_strx];
2575 cchSymbol = strlen(pszSymbol);
2576
2577 /* Check for linker defined symbols relating to sections and segments. */
2578 if ( cchSymbol > sizeof("section$end$") - 1
2579 && *pszSymbol == 's'
2580 && memchr(pszSymbol, '$', cchSymbol))
2581 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
2582 else
2583 rc = VERR_SYMBOL_NOT_FOUND;
2584
2585 /* Ask the user for an address to the symbol. */
2586 if (RT_FAILURE_NP(rc))
2587 rc = pfnGetImport(&pThis->Core, NULL /*pszModule*/, pszSymbol, iSym, &Value/*, &fKind*/, pvUser);
2588 if (RT_FAILURE(rc))
2589 {
2590 /* weak reference? */
2591 if (!(paSyms[iSym].n_desc & N_WEAK_REF))
2592 break;
2593 Value = 0;
2594 }
2595
2596 /* Update the symbol. */
2597 paSyms[iSym].n_value = (uint32_t)Value;
2598 if (paSyms[iSym].n_value != Value)
2599 {
2600 rc = VERR_LDR_ADDRESS_OVERFLOW;
2601 break;
2602 }
2603 }
2604 else if (paSyms[iSym].n_desc & N_WEAK_DEF)
2605 {
2606 /** @todo implement weak symbols. */
2607 /*return VERR_LDRMACHO_TODO; - ignored for now. */
2608 }
2609 }
2610 }
2611 else
2612 {
2613 /* (Identical to the 32-bit code, just different paSym type. (and n_strx is unsigned)) */
2614 macho_nlist_64_t *paSyms = (macho_nlist_64_t *)pThis->pvaSymbols;
2615 for (iSym = 0; iSym < cSyms; iSym++)
2616 {
2617 /* skip stabs */
2618 if (paSyms[iSym].n_type & MACHO_N_STAB)
2619 continue;
2620
2621 if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
2622 {
2623 const char *pszSymbol;
2624 size_t cchSymbol;
2625 //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
2626 RTLDRADDR Value = NIL_RTLDRADDR;
2627
2628 /** @todo Implement N_REF_TO_WEAK. */
2629 KLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
2630
2631 /* Get the symbol name. */
2632 KLDRMODMACHO_CHECK_RETURN(paSyms[iSym].n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
2633 pszSymbol = &pThis->pchStrings[paSyms[iSym].n_un.n_strx];
2634 cchSymbol = strlen(pszSymbol);
2635
2636 /* Check for linker defined symbols relating to sections and segments. */
2637 if ( cchSymbol > sizeof("section$end$") - 1
2638 && *pszSymbol == 's'
2639 && memchr(pszSymbol, '$', cchSymbol))
2640 rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
2641 else
2642 rc = VERR_SYMBOL_NOT_FOUND;
2643
2644 /* Ask the user for an address to the symbol. */
2645 if (RT_FAILURE_NP(rc))
2646 rc = pfnGetImport(&pThis->Core, NULL, pszSymbol, iSym, &Value, /*&fKind,*/ pvUser);
2647 if (RT_FAILURE(rc))
2648 {
2649 /* weak reference? */
2650 if (!(paSyms[iSym].n_desc & N_WEAK_REF))
2651 break;
2652 Value = 0;
2653 }
2654
2655 /* Update the symbol. */
2656 paSyms[iSym].n_value = Value;
2657 if (paSyms[iSym].n_value != Value)
2658 {
2659 rc = VERR_LDR_ADDRESS_OVERFLOW;
2660 break;
2661 }
2662 }
2663 else if (paSyms[iSym].n_desc & N_WEAK_DEF)
2664 {
2665 /** @todo implement weak symbols. */
2666 /*return VERR_LDRMACHO_TODO; - ignored for now. */
2667 }
2668 }
2669 }
2670
2671 return rc;
2672}
2673
2674
2675/**
2676 * MH_OBJECT: Applies base relocations to a (unprotected) image mapping.
2677 *
2678 * @returns IPRT status code.
2679 * @param pThis The Mach-O module interpreter instance.
2680 * @param pvMapping The mapping to fixup.
2681 * @param NewBaseAddress The address to fixup the mapping to.
2682 * @param OldBaseAddress The address the mapping is currently fixed up to.
2683 */
2684static int kldrModMachOObjDoFixups(PKLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress)
2685{
2686 /*
2687 * Ensure that we've got the symbol table and section fixups handy.
2688 */
2689 int rc = kldrModMachOLoadObjSymTab(pThis);
2690 if (RT_FAILURE(rc))
2691 return rc;
2692
2693 /*
2694 * Iterate over the segments and their sections and apply fixups.
2695 */
2696 rc = VINF_SUCCESS;
2697 for (uint32_t iSeg = 0; RT_SUCCESS(rc) && iSeg < pThis->cSegments; iSeg++)
2698 {
2699 PKLDRMODMACHOSEG pSeg = &pThis->aSegments[iSeg];
2700 for (uint32_t iSect = 0; iSect < pSeg->cSections; iSect++)
2701 {
2702 PKLDRMODMACHOSECT pSect = &pSeg->paSections[iSect];
2703
2704 /* skip sections without fixups. */
2705 if (!pSect->cFixups)
2706 continue;
2707
2708 /* lazy load (and endian convert) the fixups. */
2709 if (!pSect->paFixups)
2710 {
2711 rc = kldrModMachOLoadFixups(pThis, pSect->offFixups, pSect->cFixups, &pSect->paFixups);
2712 if (RT_FAILURE(rc))
2713 break;
2714 }
2715
2716 /*
2717 * Apply the fixups.
2718 */
2719 uint8_t *pbSectBits = (uint8_t *)pvMapping + (uintptr_t)pSect->RVA;
2720 if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE) /** @todo this aint right. */
2721 rc = kldrModMachOFixupSectionGeneric32Bit(pThis, pbSectBits, pSect,
2722 (macho_nlist_32_t *)pThis->pvaSymbols,
2723 pThis->cSymbols, NewBaseAddress);
2724 else if ( pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
2725 && pThis->Hdr.cputype == CPU_TYPE_X86_64)
2726 rc = kldrModMachOFixupSectionAMD64(pThis, pbSectBits, pSect,
2727 (macho_nlist_64_t *)pThis->pvaSymbols,
2728 pThis->cSymbols, NewBaseAddress);
2729 else
2730 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2731 if (RT_FAILURE(rc))
2732 break;
2733 }
2734 }
2735
2736 return rc;
2737}
2738
2739
2740/**
2741 * Applies generic fixups to a section in an image of the same endian-ness
2742 * as the host CPU.
2743 *
2744 * @returns IPRT status code.
2745 * @param pThis The Mach-O module interpreter instance.
2746 * @param pbSectBits Pointer to the section bits.
2747 * @param pFixupSect The section being fixed up.
2748 * @param NewBaseAddress The new base image address.
2749 */
2750static int kldrModMachOFixupSectionGeneric32Bit(PKLDRMODMACHO pThis, uint8_t *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
2751 macho_nlist_32_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
2752{
2753 const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
2754 const uint32_t cFixups = pFixupSect->cFixups;
2755 size_t cbSectBits = (size_t)pFixupSect->cb;
2756 const uint8_t *pbSectVirginBits;
2757 uint32_t iFixup;
2758 RTLDRADDR SymAddr = ~(RTLDRADDR)0;
2759 int rc;
2760
2761 /*
2762 * Find the virgin bits.
2763 */
2764 if (pFixupSect->offFile != -1)
2765 {
2766 rc = kldrModMachOMapVirginBits(pThis);
2767 if (RT_FAILURE(rc))
2768 return rc;
2769 pbSectVirginBits = (const uint8_t *)pThis->pvBits + pFixupSect->offFile;
2770 }
2771 else
2772 pbSectVirginBits = NULL;
2773
2774 /*
2775 * Iterate the fixups and apply them.
2776 */
2777 for (iFixup = 0; iFixup < cFixups; iFixup++)
2778 {
2779 RTPTRUNION uFix;
2780 RTPTRUNION uFixVirgin;
2781 union
2782 {
2783 macho_relocation_info_t r;
2784 scattered_relocation_info_t s;
2785 } Fixup;
2786 Fixup.r = paFixups[iFixup];
2787
2788 if (!(Fixup.r.r_address & R_SCATTERED))
2789 {
2790 /* sanity */
2791 if ((uint32_t)Fixup.r.r_address >= cbSectBits)
2792 return VERR_LDR_BAD_FIXUP;
2793
2794 /* calc fixup addresses. */
2795 uFix.pv = pbSectBits + Fixup.r.r_address;
2796 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.r.r_address : 0;
2797
2798 /*
2799 * Calc the symbol value.
2800 */
2801 /* Calc the linked symbol address / addend. */
2802 switch (Fixup.r.r_length)
2803 {
2804 /** @todo Deal with unaligned accesses on non x86 platforms. */
2805 case 0: SymAddr = *uFixVirgin.pi8; break;
2806 case 1: SymAddr = *uFixVirgin.pi16; break;
2807 case 2: SymAddr = *uFixVirgin.pi32; break;
2808 case 3: SymAddr = *uFixVirgin.pi64; break;
2809 }
2810 if (Fixup.r.r_pcrel)
2811 SymAddr += Fixup.r.r_address + pFixupSect->LinkAddress;
2812
2813 /* Add symbol / section address. */
2814 if (Fixup.r.r_extern)
2815 {
2816 const macho_nlist_32_t *pSym;
2817 if (Fixup.r.r_symbolnum >= cSyms)
2818 return VERR_LDR_BAD_FIXUP;
2819 pSym = &paSyms[Fixup.r.r_symbolnum];
2820
2821 if (pSym->n_type & MACHO_N_STAB)
2822 return VERR_LDR_BAD_FIXUP;
2823
2824 switch (pSym->n_type & MACHO_N_TYPE)
2825 {
2826 case MACHO_N_SECT:
2827 {
2828 PKLDRMODMACHOSECT pSymSect;
2829 KLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
2830 pSymSect = &pThis->paSections[pSym->n_sect - 1];
2831
2832 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
2833 break;
2834 }
2835
2836 case MACHO_N_UNDF:
2837 case MACHO_N_ABS:
2838 SymAddr += pSym->n_value;
2839 break;
2840
2841 case MACHO_N_INDR:
2842 case MACHO_N_PBUD:
2843 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
2844 default:
2845 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
2846 }
2847 }
2848 else if (Fixup.r.r_symbolnum != R_ABS)
2849 {
2850 PKLDRMODMACHOSECT pSymSect;
2851 if (Fixup.r.r_symbolnum > pThis->cSections)
2852 return VERR_LDR_BAD_FIXUP;
2853 pSymSect = &pThis->paSections[Fixup.r.r_symbolnum - 1];
2854
2855 SymAddr -= pSymSect->LinkAddress;
2856 SymAddr += pSymSect->RVA + NewBaseAddress;
2857 }
2858
2859 /* adjust for PC relative */
2860 if (Fixup.r.r_pcrel)
2861 SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
2862 }
2863 else
2864 {
2865 PKLDRMODMACHOSECT pSymSect;
2866 uint32_t iSymSect;
2867 RTLDRADDR Value;
2868
2869 /* sanity */
2870 KLDRMODMACHO_ASSERT(Fixup.s.r_scattered);
2871 if ((uint32_t)Fixup.s.r_address >= cbSectBits)
2872 return VERR_LDR_BAD_FIXUP;
2873
2874 /* calc fixup addresses. */
2875 uFix.pv = pbSectBits + Fixup.s.r_address;
2876 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.s.r_address : 0;
2877
2878 /*
2879 * Calc the symbol value.
2880 */
2881 /* The addend is stored in the code. */
2882 switch (Fixup.s.r_length)
2883 {
2884 case 0: SymAddr = *uFixVirgin.pi8; break;
2885 case 1: SymAddr = *uFixVirgin.pi16; break;
2886 case 2: SymAddr = *uFixVirgin.pi32; break;
2887 case 3: SymAddr = *uFixVirgin.pi64; break;
2888 }
2889 if (Fixup.s.r_pcrel)
2890 SymAddr += Fixup.s.r_address;
2891 Value = Fixup.s.r_value;
2892 SymAddr -= Value; /* (-> addend only) */
2893
2894 /* Find the section number from the r_value. */
2895 pSymSect = NULL;
2896 for (iSymSect = 0; iSymSect < pThis->cSections; iSymSect++)
2897 {
2898 RTLDRADDR off = Value - pThis->paSections[iSymSect].LinkAddress;
2899 if (off < pThis->paSections[iSymSect].cb)
2900 {
2901 pSymSect = &pThis->paSections[iSymSect];
2902 break;
2903 }
2904 else if (off == pThis->paSections[iSymSect].cb) /* edge case */
2905 pSymSect = &pThis->paSections[iSymSect];
2906 }
2907 if (!pSymSect)
2908 return VERR_LDR_BAD_FIXUP;
2909
2910 /* Calc the symbol address. */
2911 SymAddr += Value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
2912 if (Fixup.s.r_pcrel)
2913 SymAddr -= Fixup.s.r_address + pFixupSect->RVA + NewBaseAddress;
2914
2915 Fixup.r.r_length = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_length;
2916 Fixup.r.r_type = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_type;
2917 }
2918
2919 /*
2920 * Write back the fixed up value.
2921 */
2922 if (Fixup.r.r_type == GENERIC_RELOC_VANILLA)
2923 {
2924 switch (Fixup.r.r_length)
2925 {
2926 case 0: *uFix.pu8 = (uint8_t)SymAddr; break;
2927 case 1: *uFix.pu16 = (uint16_t)SymAddr; break;
2928 case 2: *uFix.pu32 = (uint32_t)SymAddr; break;
2929 case 3: *uFix.pu64 = (uint64_t)SymAddr; break;
2930 }
2931 }
2932 else if (Fixup.r.r_type <= GENERIC_RELOC_LOCAL_SECTDIFF)
2933 return VERR_LDRMACHO_UNSUPPORTED_FIXUP_TYPE;
2934 else
2935 return VERR_LDR_BAD_FIXUP;
2936 }
2937
2938 return VINF_SUCCESS;
2939}
2940
2941
2942/**
2943 * Applies AMD64 fixups to a section.
2944 *
2945 * @returns IPRT status code.
2946 * @param pThis The Mach-O module interpreter instance.
2947 * @param pbSectBits Pointer to the section bits.
2948 * @param pFixupSect The section being fixed up.
2949 * @param NewBaseAddress The new base image address.
2950 */
2951static int kldrModMachOFixupSectionAMD64(PKLDRMODMACHO pThis, uint8_t *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
2952 macho_nlist_64_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
2953{
2954 const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
2955 const uint32_t cFixups = pFixupSect->cFixups;
2956 size_t cbSectBits = (size_t)pFixupSect->cb;
2957 const uint8_t *pbSectVirginBits;
2958 uint32_t iFixup;
2959 RTLDRADDR SymAddr;
2960 int rc;
2961
2962 /*
2963 * Find the virgin bits.
2964 */
2965 if (pFixupSect->offFile != -1)
2966 {
2967 rc = kldrModMachOMapVirginBits(pThis);
2968 if (RT_FAILURE(rc))
2969 return rc;
2970 pbSectVirginBits = (const uint8_t *)pThis->pvBits + pFixupSect->offFile;
2971 }
2972 else
2973 pbSectVirginBits = NULL;
2974
2975 /*
2976 * Iterate the fixups and apply them.
2977 */
2978 for (iFixup = 0; iFixup < cFixups; iFixup++)
2979 {
2980 union
2981 {
2982 macho_relocation_info_t r;
2983 scattered_relocation_info_t s;
2984 } Fixup;
2985 Fixup.r = paFixups[iFixup];
2986
2987 /* AMD64 doesn't use scattered fixups. */
2988 KLDRMODMACHO_CHECK_RETURN(!(Fixup.r.r_address & R_SCATTERED), VERR_LDR_BAD_FIXUP);
2989
2990 /* sanity */
2991 KLDRMODMACHO_CHECK_RETURN((uint32_t)Fixup.r.r_address < cbSectBits, VERR_LDR_BAD_FIXUP);
2992
2993 /* calc fixup addresses. */
2994 RTPTRUNION uFix;
2995 uFix.pv = pbSectBits + Fixup.r.r_address;
2996 RTPTRUNION uFixVirgin;
2997 uFixVirgin.pv = pbSectVirginBits ? (uint8_t *)pbSectVirginBits + Fixup.r.r_address : 0;
2998
2999 /*
3000 * Calc the symbol value.
3001 */
3002 /* Calc the linked symbol address / addend. */
3003 switch (Fixup.r.r_length)
3004 {
3005 /** @todo Deal with unaligned accesses on non x86 platforms. */
3006 case 2: SymAddr = *uFixVirgin.pi32; break;
3007 case 3: SymAddr = *uFixVirgin.pi64; break;
3008 default:
3009 KLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3010 }
3011
3012 /* Add symbol / section address. */
3013 if (Fixup.r.r_extern)
3014 {
3015 const macho_nlist_64_t *pSym;
3016
3017 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
3018 pSym = &paSyms[Fixup.r.r_symbolnum];
3019 KLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_BAD_FIXUP);
3020
3021 switch (Fixup.r.r_type)
3022 {
3023 /* GOT references just needs to have their symbol verified.
3024 Later, we'll optimize GOT building here using a parallel sym->got array. */
3025 case X86_64_RELOC_GOT_LOAD:
3026 case X86_64_RELOC_GOT:
3027 switch (pSym->n_type & MACHO_N_TYPE)
3028 {
3029 case MACHO_N_SECT:
3030 case MACHO_N_UNDF:
3031 case MACHO_N_ABS:
3032 break;
3033 case MACHO_N_INDR:
3034 case MACHO_N_PBUD:
3035 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3036 default:
3037 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3038 }
3039 SymAddr = sizeof(uint64_t) * Fixup.r.r_symbolnum + pThis->GotRVA + NewBaseAddress;
3040 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_BAD_FIXUP);
3041 SymAddr -= 4;
3042 break;
3043
3044 /* Verify the r_pcrel field for signed fixups on the way into the default case. */
3045 case X86_64_RELOC_BRANCH:
3046 case X86_64_RELOC_SIGNED:
3047 case X86_64_RELOC_SIGNED_1:
3048 case X86_64_RELOC_SIGNED_2:
3049 case X86_64_RELOC_SIGNED_4:
3050 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3051 /* Falls through. */
3052 default:
3053 {
3054 /* Adjust with fixup specific addend and vierfy unsigned/r_pcrel. */
3055 switch (Fixup.r.r_type)
3056 {
3057 case X86_64_RELOC_UNSIGNED:
3058 KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3059 break;
3060 case X86_64_RELOC_BRANCH:
3061 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_BAD_FIXUP);
3062 SymAddr -= 4;
3063 break;
3064 case X86_64_RELOC_SIGNED:
3065 case X86_64_RELOC_SIGNED_1:
3066 case X86_64_RELOC_SIGNED_2:
3067 case X86_64_RELOC_SIGNED_4:
3068 SymAddr -= 4;
3069 break;
3070 default:
3071 KLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3072 }
3073
3074 switch (pSym->n_type & MACHO_N_TYPE)
3075 {
3076 case MACHO_N_SECT:
3077 {
3078 PKLDRMODMACHOSECT pSymSect;
3079 KLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3080 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3081 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3082 break;
3083 }
3084
3085 case MACHO_N_UNDF:
3086 /* branch to an external symbol may have to take a short detour. */
3087 if ( Fixup.r.r_type == X86_64_RELOC_BRANCH
3088 && SymAddr + Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress
3089 - pSym->n_value
3090 + UINT64_C(0x80000000)
3091 >= UINT64_C(0xffffff20))
3092 SymAddr += pThis->cbJmpStub * Fixup.r.r_symbolnum + pThis->JmpStubsRVA + NewBaseAddress;
3093 else
3094 SymAddr += pSym->n_value;
3095 break;
3096
3097 case MACHO_N_ABS:
3098 SymAddr += pSym->n_value;
3099 break;
3100
3101 case MACHO_N_INDR:
3102 case MACHO_N_PBUD:
3103 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3104 default:
3105 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3106 }
3107 break;
3108 }
3109
3110 /*
3111 * This is a weird customer, it will always be follows by an UNSIGNED fixup.
3112 */
3113 case X86_64_RELOC_SUBTRACTOR:
3114 {
3115 macho_relocation_info_t Fixup2;
3116
3117 /* Deal with the SUBTRACT symbol first, by subtracting it from SymAddr. */
3118 switch (pSym->n_type & MACHO_N_TYPE)
3119 {
3120 case MACHO_N_SECT:
3121 {
3122 PKLDRMODMACHOSECT pSymSect;
3123 KLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3124 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3125 SymAddr -= pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3126 break;
3127 }
3128
3129 case MACHO_N_UNDF:
3130 case MACHO_N_ABS:
3131 SymAddr -= pSym->n_value;
3132 break;
3133
3134 case MACHO_N_INDR:
3135 case MACHO_N_PBUD:
3136 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3137 default:
3138 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3139 }
3140
3141 /* Load the 2nd fixup, check sanity. */
3142 iFixup++;
3143 KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel && iFixup < cFixups, VERR_LDR_BAD_FIXUP);
3144 Fixup2 = paFixups[iFixup];
3145 KLDRMODMACHO_CHECK_RETURN( Fixup2.r_address == Fixup.r.r_address
3146 && Fixup2.r_length == Fixup.r.r_length
3147 && Fixup2.r_type == X86_64_RELOC_UNSIGNED
3148 && !Fixup2.r_pcrel
3149 && Fixup2.r_symbolnum < cSyms,
3150 VERR_LDR_BAD_FIXUP);
3151
3152 if (Fixup2.r_extern)
3153 {
3154 KLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
3155 pSym = &paSyms[Fixup2.r_symbolnum];
3156 KLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_BAD_FIXUP);
3157
3158 /* Add it's value to SymAddr. */
3159 switch (pSym->n_type & MACHO_N_TYPE)
3160 {
3161 case MACHO_N_SECT:
3162 {
3163 PKLDRMODMACHOSECT pSymSect;
3164 KLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3165 pSymSect = &pThis->paSections[pSym->n_sect - 1];
3166 SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3167 break;
3168 }
3169
3170 case MACHO_N_UNDF:
3171 case MACHO_N_ABS:
3172 SymAddr += pSym->n_value;
3173 break;
3174
3175 case MACHO_N_INDR:
3176 case MACHO_N_PBUD:
3177 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3178 default:
3179 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
3180 }
3181 }
3182 else if (Fixup2.r_symbolnum != R_ABS)
3183 {
3184 PKLDRMODMACHOSECT pSymSect;
3185 KLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
3186 pSymSect = &pThis->paSections[Fixup2.r_symbolnum - 1];
3187 SymAddr += pSymSect->RVA + NewBaseAddress;
3188 }
3189 else
3190 KLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3191 }
3192 break;
3193 }
3194 }
3195 else
3196 {
3197 /* verify against fixup type and make adjustments */
3198 switch (Fixup.r.r_type)
3199 {
3200 case X86_64_RELOC_UNSIGNED:
3201 KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3202 break;
3203 case X86_64_RELOC_BRANCH:
3204 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3205 SymAddr += 4; /* dunno what the assmbler/linker really is doing here... */
3206 break;
3207 case X86_64_RELOC_SIGNED:
3208 case X86_64_RELOC_SIGNED_1:
3209 case X86_64_RELOC_SIGNED_2:
3210 case X86_64_RELOC_SIGNED_4:
3211 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
3212 break;
3213 /*case X86_64_RELOC_GOT_LOAD:*/
3214 /*case X86_64_RELOC_GOT: */
3215 /*case X86_64_RELOC_SUBTRACTOR: - must be r_extern=1 says as. */
3216 default:
3217 KLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3218 }
3219 if (Fixup.r.r_symbolnum != R_ABS)
3220 {
3221 PKLDRMODMACHOSECT pSymSect;
3222 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
3223 pSymSect = &pThis->paSections[Fixup.r.r_symbolnum - 1];
3224
3225 SymAddr -= pSymSect->LinkAddress;
3226 SymAddr += pSymSect->RVA + NewBaseAddress;
3227 if (Fixup.r.r_pcrel)
3228 SymAddr += Fixup.r.r_address;
3229 }
3230 }
3231
3232 /* adjust for PC relative */
3233 if (Fixup.r.r_pcrel)
3234 SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
3235
3236 /*
3237 * Write back the fixed up value.
3238 */
3239 switch (Fixup.r.r_length)
3240 {
3241 case 3:
3242 *uFix.pu64 = (uint64_t)SymAddr;
3243 break;
3244 case 2:
3245 KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel || Fixup.r.r_type == X86_64_RELOC_SUBTRACTOR, VERR_LDR_BAD_FIXUP);
3246 KLDRMODMACHO_CHECK_RETURN((int32_t)SymAddr == (int64_t)SymAddr, VERR_LDR_ADDRESS_OVERFLOW);
3247 *uFix.pu32 = (uint32_t)SymAddr;
3248 break;
3249 default:
3250 KLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
3251 }
3252 }
3253
3254 return VINF_SUCCESS;
3255}
3256
3257
3258/**
3259 * Loads the symbol table for a MH_OBJECT file.
3260 *
3261 * The symbol table is pointed to by KLDRMODMACHO::pvaSymbols.
3262 *
3263 * @returns IPRT status code.
3264 * @param pThis The Mach-O module interpreter instance.
3265 */
3266static int kldrModMachOLoadObjSymTab(PKLDRMODMACHO pThis)
3267{
3268 int rc = VINF_SUCCESS;
3269
3270 if ( !pThis->pvaSymbols
3271 && pThis->cSymbols)
3272 {
3273 size_t cbSyms;
3274 size_t cbSym;
3275 void *pvSyms;
3276 void *pvStrings;
3277
3278 /* sanity */
3279 KLDRMODMACHO_CHECK_RETURN( pThis->offSymbols
3280 && (!pThis->cchStrings || pThis->offStrings),
3281 VERR_LDRMACHO_BAD_OBJECT_FILE);
3282
3283 /* allocate */
3284 cbSym = pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
3285 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
3286 ? sizeof(macho_nlist_32_t)
3287 : sizeof(macho_nlist_64_t);
3288 cbSyms = pThis->cSymbols * cbSym;
3289 KLDRMODMACHO_CHECK_RETURN(cbSyms / cbSym == pThis->cSymbols, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
3290 rc = VERR_NO_MEMORY;
3291 pvSyms = RTMemAlloc(cbSyms);
3292 if (pvSyms)
3293 {
3294 if (pThis->cchStrings)
3295 pvStrings = RTMemAlloc(pThis->cchStrings);
3296 else
3297 pvStrings = RTMemAllocZ(4);
3298 if (pvStrings)
3299 {
3300 /* read */
3301 rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvSyms, cbSyms, pThis->offSymbols);
3302 if (RT_SUCCESS(rc) && pThis->cchStrings)
3303 rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvStrings,
3304 pThis->cchStrings, pThis->offStrings);
3305 if (RT_SUCCESS(rc))
3306 {
3307 pThis->pvaSymbols = pvSyms;
3308 pThis->pchStrings = (char *)pvStrings;
3309
3310 /* perform endian conversion? */
3311 if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
3312 {
3313 uint32_t cLeft = pThis->cSymbols;
3314 macho_nlist_32_t *pSym = (macho_nlist_32_t *)pvSyms;
3315 while (cLeft-- > 0)
3316 {
3317 pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
3318 pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
3319 pSym->n_value = RT_BSWAP_U32(pSym->n_value);
3320 pSym++;
3321 }
3322 }
3323 else if (pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
3324 {
3325 uint32_t cLeft = pThis->cSymbols;
3326 macho_nlist_64_t *pSym = (macho_nlist_64_t *)pvSyms;
3327 while (cLeft-- > 0)
3328 {
3329 pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
3330 pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
3331 pSym->n_value = RT_BSWAP_U64(pSym->n_value);
3332 pSym++;
3333 }
3334 }
3335
3336 return VINF_SUCCESS;
3337 }
3338 RTMemFree(pvStrings);
3339 }
3340 RTMemFree(pvSyms);
3341 }
3342 }
3343 else
3344 KLDRMODMACHO_ASSERT(pThis->pchStrings || pThis->Hdr.filetype == MH_DSYM);
3345
3346 return rc;
3347}
3348
3349
3350/**
3351 * Loads the fixups at the given address and performs endian
3352 * conversion if necessary.
3353 *
3354 * @returns IPRT status code.
3355 * @param pThis The Mach-O module interpreter instance.
3356 * @param offFixups The file offset of the fixups.
3357 * @param cFixups The number of fixups to load.
3358 * @param ppaFixups Where to put the pointer to the allocated fixup array.
3359 */
3360static int kldrModMachOLoadFixups(PKLDRMODMACHO pThis, RTFOFF offFixups, uint32_t cFixups, macho_relocation_info_t **ppaFixups)
3361{
3362 macho_relocation_info_t *paFixups;
3363 size_t cbFixups;
3364
3365 /* allocate the memory. */
3366 cbFixups = cFixups * sizeof(*paFixups);
3367 KLDRMODMACHO_CHECK_RETURN(cbFixups / sizeof(*paFixups) == cFixups, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
3368 paFixups = (macho_relocation_info_t *)RTMemAlloc(cbFixups);
3369 if (!paFixups)
3370 return VERR_NO_MEMORY;
3371
3372 /* read the fixups. */
3373 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, paFixups, cbFixups, offFixups);
3374 if (RT_SUCCESS(rc))
3375 {
3376 *ppaFixups = paFixups;
3377
3378 /* do endian conversion if necessary. */
3379 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
3380 || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
3381 {
3382 uint32_t iFixup;
3383 for (iFixup = 0; iFixup < cFixups; iFixup++)
3384 {
3385 uint32_t *pu32 = (uint32_t *)&paFixups[iFixup];
3386 pu32[0] = RT_BSWAP_U32(pu32[0]);
3387 pu32[1] = RT_BSWAP_U32(pu32[1]);
3388 }
3389 }
3390 }
3391 else
3392 RTMemFree(paFixups);
3393 return rc;
3394}
3395
3396
3397/**
3398 * Maps the virgin file bits into memory if not already done.
3399 *
3400 * @returns IPRT status code.
3401 * @param pThis The Mach-O module interpreter instance.
3402 */
3403static int kldrModMachOMapVirginBits(PKLDRMODMACHO pThis)
3404{
3405 int rc = VINF_SUCCESS;
3406 if (!pThis->pvBits)
3407 rc = pThis->Core.pReader->pfnMap(pThis->Core.pReader, &pThis->pvBits);
3408 return rc;
3409}
3410
3411#if 0
3412
3413/** @copydoc kLdrModCallInit */
3414static int kldrModMachOCallInit(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
3415{
3416 /* later */
3417 RT_NOREF(pMod);
3418 RT_NOREF(pvMapping);
3419 RT_NOREF(uHandle);
3420 return VINF_SUCCESS;
3421}
3422
3423
3424/** @copydoc kLdrModCallTerm */
3425static int kldrModMachOCallTerm(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
3426{
3427 /* later */
3428 RT_NOREF(pMod);
3429 RT_NOREF(pvMapping);
3430 RT_NOREF(uHandle);
3431 return VINF_SUCCESS;
3432}
3433
3434
3435/** @copydoc kLdrModCallThread */
3436static int kldrModMachOCallThread(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle, unsigned fAttachingOrDetaching)
3437{
3438 /* Relevant for Mach-O? */
3439 RT_NOREF(pMod);
3440 RT_NOREF(pvMapping);
3441 RT_NOREF(uHandle);
3442 RT_NOREF(fAttachingOrDetaching);
3443 return VINF_SUCCESS;
3444}
3445
3446#endif
3447
3448
3449/**
3450 * @interface_method_impl{RTLDROPS,pfnGetImageSize}
3451 */
3452static DECLCALLBACK(size_t) rtldrMachO_GetImageSize(PRTLDRMODINTERNAL pMod)
3453{
3454 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3455 return pThis->cbImage;
3456}
3457
3458
3459/**
3460 * @interface_method_impl{RTLDROPS,pfnGetBits}
3461 */
3462static DECLCALLBACK(int) rtldrMachO_GetBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR BaseAddress,
3463 PFNRTLDRIMPORT pfnGetImport, void *pvUser)
3464{
3465 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3466
3467 if (!pThis->fCanLoad)
3468 return VERR_LDRMACHO_TODO;
3469
3470 /*
3471 * Zero the entire buffer first to simplify things.
3472 */
3473 memset(pvBits, 0, (size_t)pThis->cbImage);
3474
3475 /*
3476 * When possible use the segment table to load the data.
3477 */
3478 for (uint32_t i = 0; i < pThis->cSegments; i++)
3479 {
3480 /* skip it? */
3481 if ( pThis->aSegments[i].SegInfo.cbFile == -1
3482 || pThis->aSegments[i].SegInfo.offFile == -1
3483 || pThis->aSegments[i].SegInfo.LinkAddress == NIL_RTLDRADDR
3484 || !pThis->aSegments[i].SegInfo.Alignment)
3485 continue;
3486 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader,
3487 (uint8_t *)pvBits + pThis->aSegments[i].SegInfo.RVA,
3488 pThis->aSegments[i].SegInfo.cbFile,
3489 pThis->aSegments[i].SegInfo.offFile);
3490 if (RT_FAILURE(rc))
3491 return rc;
3492 }
3493
3494 /*
3495 * Perform relocations.
3496 */
3497 return rtldrMachO_RelocateBits(pMod, pvBits, BaseAddress, pThis->LinkAddress, pfnGetImport, pvUser);
3498}
3499
3500
3501/**
3502 * @interface_method_impl{RTLDROPS,pfnRelocate}
3503 */
3504static DECLCALLBACK(int) rtldrMachO_RelocateBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
3505 RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
3506{
3507 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3508 int rc;
3509 RT_NOREF(OldBaseAddress);
3510
3511 /*
3512 * Call workers to do the jobs.
3513 */
3514 if (pThis->Hdr.filetype == MH_OBJECT)
3515 {
3516 rc = kldrModMachOObjDoImports(pThis, NewBaseAddress, pfnGetImport, pvUser);
3517 if (RT_SUCCESS(rc))
3518 rc = kldrModMachOObjDoFixups(pThis, pvBits, NewBaseAddress);
3519
3520 }
3521 else
3522 rc = VERR_LDRMACHO_TODO;
3523 /*{
3524 rc = kldrModMachODoFixups(pThis, pvBits, NewBaseAddress, OldBaseAddress, pfnGetImport, pvUser);
3525 if (RT_SUCCESS(rc))
3526 rc = kldrModMachODoImports(pThis, pvBits, pfnGetImport, pvUser);
3527 }*/
3528
3529 /*
3530 * Construct the global offset table if necessary, it's always the last
3531 * segment when present.
3532 */
3533 if (RT_SUCCESS(rc) && pThis->fMakeGot)
3534 rc = kldrModMachOMakeGOT(pThis, pvBits, NewBaseAddress);
3535
3536 return rc;
3537}
3538
3539
3540/**
3541 * Builds the GOT.
3542 *
3543 * Assumes the symbol table has all external symbols resolved correctly and that
3544 * the bits has been cleared up front.
3545 */
3546static int kldrModMachOMakeGOT(PKLDRMODMACHO pThis, void *pvBits, RTLDRADDR NewBaseAddress)
3547{
3548 uint32_t iSym = pThis->cSymbols;
3549 if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
3550 || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
3551 {
3552 macho_nlist_32_t const *paSyms = (macho_nlist_32_t const *)pThis->pvaSymbols;
3553 uint32_t *paGOT = (uint32_t *)((uint8_t *)pvBits + pThis->GotRVA);
3554 while (iSym-- > 0)
3555 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
3556 {
3557 case MACHO_N_SECT:
3558 {
3559 PKLDRMODMACHOSECT pSymSect;
3560 KLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3561 pSymSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
3562 paGOT[iSym] = (uint32_t)(paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress);
3563 break;
3564 }
3565
3566 case MACHO_N_UNDF:
3567 case MACHO_N_ABS:
3568 paGOT[iSym] = paSyms[iSym].n_value;
3569 break;
3570 }
3571 }
3572 else
3573 {
3574 macho_nlist_64_t const *paSyms = (macho_nlist_64_t const *)pThis->pvaSymbols;
3575 uint64_t *paGOT = (uint64_t *)((uint8_t *)pvBits + pThis->GotRVA);
3576 while (iSym-- > 0)
3577 {
3578 switch (paSyms[iSym].n_type & MACHO_N_TYPE)
3579 {
3580 case MACHO_N_SECT:
3581 {
3582 PKLDRMODMACHOSECT pSymSect;
3583 KLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
3584 pSymSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
3585 paGOT[iSym] = paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
3586 break;
3587 }
3588
3589 case MACHO_N_UNDF:
3590 case MACHO_N_ABS:
3591 paGOT[iSym] = paSyms[iSym].n_value;
3592 break;
3593 }
3594 }
3595
3596 if (pThis->JmpStubsRVA != NIL_RTLDRADDR)
3597 {
3598 iSym = pThis->cSymbols;
3599 switch (pThis->Hdr.cputype)
3600 {
3601 /*
3602 * AMD64 is simple since the GOT and the indirect jmps are parallel
3603 * arrays with entries of the same size. The relative offset will
3604 * be the the same for each entry, kind of nice. :-)
3605 */
3606 case CPU_TYPE_X86_64:
3607 {
3608 uint64_t *paJmps = (uint64_t *)((uint8_t *)pvBits + pThis->JmpStubsRVA);
3609 int32_t off;
3610 uint64_t u64Tmpl;
3611 union
3612 {
3613 uint8_t ab[8];
3614 uint64_t u64;
3615 } Tmpl;
3616
3617 /* create the template. */
3618 off = (int32_t)(pThis->GotRVA - (pThis->JmpStubsRVA + 6));
3619 Tmpl.ab[0] = 0xff; /* jmp [GOT-entry wrt RIP] */
3620 Tmpl.ab[1] = 0x25;
3621 Tmpl.ab[2] = off & 0xff;
3622 Tmpl.ab[3] = (off >> 8) & 0xff;
3623 Tmpl.ab[4] = (off >> 16) & 0xff;
3624 Tmpl.ab[5] = (off >> 24) & 0xff;
3625 Tmpl.ab[6] = 0xcc;
3626 Tmpl.ab[7] = 0xcc;
3627 u64Tmpl = Tmpl.u64;
3628
3629 /* copy the template to every jmp table entry. */
3630 while (iSym-- > 0)
3631 paJmps[iSym] = u64Tmpl;
3632 break;
3633 }
3634
3635 default:
3636 KLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
3637 }
3638 }
3639 }
3640 return VINF_SUCCESS;
3641}
3642
3643
3644/**
3645 * @interface_method_impl{RTLDROPS,pfnEnumSegments}
3646 */
3647static DECLCALLBACK(int) rtldrMachO_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
3648{
3649 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3650 uint32_t const cSegments = pThis->cSegments;
3651 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3652 {
3653 int rc = pfnCallback(pMod, &pThis->aSegments[iSeg].SegInfo, pvUser);
3654 if (rc != VINF_SUCCESS)
3655 return rc;
3656 }
3657
3658 return VINF_SUCCESS;
3659}
3660
3661
3662/**
3663 * @interface_method_impl{RTLDROPS,pfnLinkAddressToSegOffset}
3664 */
3665static DECLCALLBACK(int) rtldrMachO_LinkAddressToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress,
3666 uint32_t *piSeg, PRTLDRADDR poffSeg)
3667{
3668 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3669 uint32_t const cSegments = pThis->cSegments;
3670 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3671 {
3672 RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
3673 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3674 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3675 {
3676 *piSeg = iSeg;
3677 *poffSeg = offSeg;
3678 return VINF_SUCCESS;
3679 }
3680 }
3681
3682 return VERR_LDR_INVALID_LINK_ADDRESS;
3683}
3684
3685
3686/**
3687 * @interface_method_impl{RTLDROPS,pfnLinkAddressToRva}.
3688 */
3689static DECLCALLBACK(int) rtldrMachO_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
3690{
3691 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3692 uint32_t const cSegments = pThis->cSegments;
3693 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3694 {
3695 RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
3696 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3697 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3698 {
3699 *pRva = pThis->aSegments[iSeg].SegInfo.RVA + offSeg;
3700 return VINF_SUCCESS;
3701 }
3702 }
3703
3704 return VERR_LDR_INVALID_RVA;
3705}
3706
3707
3708/**
3709 * @interface_method_impl{RTLDROPS,pfnSegOffsetToRva}
3710 */
3711static DECLCALLBACK(int) rtldrMachO_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg, PRTLDRADDR pRva)
3712{
3713 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3714
3715 if (iSeg >= pThis->cSegments)
3716 return VERR_LDR_INVALID_SEG_OFFSET;
3717 KLDRMODMACHOSEG const *pSegment = &pThis->aSegments[iSeg];
3718
3719 if ( offSeg > pSegment->SegInfo.cbMapped
3720 && offSeg > pSegment->SegInfo.cb
3721 && ( pSegment->SegInfo.cbFile < 0
3722 || offSeg > (uint64_t)pSegment->SegInfo.cbFile))
3723 return VERR_LDR_INVALID_SEG_OFFSET;
3724
3725 *pRva = pSegment->SegInfo.RVA + offSeg;
3726 return VINF_SUCCESS;
3727}
3728
3729
3730/**
3731 * @interface_method_impl{RTLDROPS,pfnRvaToSegOffset}
3732 */
3733static DECLCALLBACK(int) rtldrMachO_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva, uint32_t *piSeg, PRTLDRADDR poffSeg)
3734{
3735 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3736 uint32_t const cSegments = pThis->cSegments;
3737 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
3738 {
3739 RTLDRADDR offSeg = Rva - pThis->aSegments[iSeg].SegInfo.RVA;
3740 if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
3741 || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
3742 {
3743 *piSeg = iSeg;
3744 *poffSeg = offSeg;
3745 return VINF_SUCCESS;
3746 }
3747 }
3748
3749 return VERR_LDR_INVALID_RVA;
3750}
3751
3752
3753/**
3754 * @interface_method_impl{RTLDROPS,pfnReadDbgInfo}
3755 */
3756static DECLCALLBACK(int) rtldrMachO_ReadDbgInfo(PRTLDRMODINTERNAL pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void *pvBuf)
3757{
3758 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3759
3760 /** @todo May have to apply fixups here. */
3761 if (iDbgInfo < pThis->cSections)
3762 return pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvBuf, cb, off);
3763 return VERR_OUT_OF_RANGE;
3764}
3765
3766
3767/** @interface_method_impl{RTLDROPS,pfnQueryProp} */
3768static DECLCALLBACK(int) rtldrMachO_QueryProp(PRTLDRMODINTERNAL pMod, RTLDRPROP enmProp, void const *pvBits,
3769 void *pvBuf, size_t cbBuf, size_t *pcbRet)
3770{
3771 PKLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, KLDRMODMACHO, Core);
3772 int rc;
3773 switch (enmProp)
3774 {
3775 case RTLDRPROP_UUID:
3776 rc = kldrModMachOQueryImageUuid(pThis, pvBits, (uint8_t *)pvBuf, cbBuf);
3777 if (RT_FAILURE(rc))
3778 return rc;
3779 cbBuf = RT_MIN(cbBuf, sizeof(RTUUID));
3780 break;
3781
3782#if 0 /** @todo return LC_ID_DYLIB */
3783 case RTLDRPROP_INTERNAL_NAME:
3784#endif
3785
3786 default:
3787 return VERR_NOT_FOUND;
3788 }
3789 if (pcbRet)
3790 *pcbRet = cbBuf;
3791 RT_NOREF_PV(pvBits);
3792 return VINF_SUCCESS;
3793}
3794
3795
3796/**
3797 * Operations for a Mach-O module interpreter.
3798 */
3799static const RTLDROPS s_rtldrMachOOps=
3800{
3801 "mach-o",
3802 rtldrMachO_Close,
3803 NULL,
3804 NULL /*pfnDone*/,
3805 rtldrMachO_EnumSymbols,
3806 /* ext */
3807 rtldrMachO_GetImageSize,
3808 rtldrMachO_GetBits,
3809 rtldrMachO_RelocateBits,
3810 rtldrMachO_GetSymbolEx,
3811 NULL /*pfnQueryForwarderInfo*/,
3812 rtldrMachO_EnumDbgInfo,
3813 rtldrMachO_EnumSegments,
3814 rtldrMachO_LinkAddressToSegOffset,
3815 rtldrMachO_LinkAddressToRva,
3816 rtldrMachO_SegOffsetToRva,
3817 rtldrMachO_RvaToSegOffset,
3818 rtldrMachO_ReadDbgInfo,
3819 rtldrMachO_QueryProp,
3820 NULL /*pfnVerifySignature*/,
3821 NULL /*pfnHashImage*/,
3822 NULL /*pfnUnwindFrame*/,
3823 42
3824};
3825
3826
3827/**
3828 * Handles opening Mach-O images (non-fat).
3829 */
3830DECLHIDDEN(int) rtldrMachOOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offImage,
3831 PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
3832{
3833
3834 /*
3835 * Create the instance data and do a minimal header validation.
3836 */
3837 PKLDRMODMACHO pThis = NULL;
3838 int rc = kldrModMachODoCreate(pReader, offImage, fFlags, &pThis, pErrInfo);
3839 if (RT_SUCCESS(rc))
3840 {
3841 /*
3842 * Match up against the requested CPU architecture.
3843 */
3844 if ( enmArch == RTLDRARCH_WHATEVER
3845 || pThis->Core.enmArch == enmArch)
3846 {
3847 pThis->Core.pOps = &s_rtldrMachOOps;
3848 pThis->Core.u32Magic = RTLDRMOD_MAGIC;
3849 *phLdrMod = &pThis->Core;
3850 return VINF_SUCCESS;
3851 }
3852 rc = VERR_LDR_ARCH_MISMATCH;
3853 }
3854 if (pThis)
3855 {
3856 RTMemFree(pThis->pbLoadCommands);
3857 RTMemFree(pThis);
3858 }
3859 return rc;
3860
3861}
3862
3863
3864/**
3865 * Handles opening FAT Mach-O image.
3866 */
3867DECLHIDDEN(int) rtldrFatOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
3868{
3869 fat_header_t FatHdr;
3870 int rc = pReader->pfnRead(pReader, &FatHdr, sizeof(FatHdr), 0);
3871 if (RT_FAILURE(rc))
3872 return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
3873
3874 if (FatHdr.magic == IMAGE_FAT_SIGNATURE)
3875 { /* likely */ }
3876 else if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
3877 FatHdr.nfat_arch = RT_BSWAP_U32(FatHdr.nfat_arch);
3878 else
3879 return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "magic=%#x", FatHdr.magic);
3880 if (FatHdr.nfat_arch < 64)
3881 return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "Bad nfat_arch value: %#x", FatHdr.nfat_arch);
3882
3883 uint32_t offEntry = sizeof(FatHdr);
3884 for (uint32_t i = 0; i < FatHdr.nfat_arch; i++, offEntry += sizeof(fat_arch_t))
3885 {
3886 fat_arch_t FatEntry;
3887 rc = pReader->pfnRead(pReader, &FatEntry, sizeof(FatEntry), offEntry);
3888 if (RT_FAILURE(rc))
3889 return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
3890 if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
3891 {
3892 FatEntry.cputype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cputype);
3893 //FatEntry.cpusubtype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cpusubtype);
3894 FatEntry.offset = RT_BSWAP_U32(FatEntry.offset);
3895 //FatEntry.size = RT_BSWAP_U32(FatEntry.size);
3896 //FatEntry.align = RT_BSWAP_U32(FatEntry.align);
3897 }
3898
3899 /*
3900 * Match enmArch.
3901 */
3902 bool fMatch = false;
3903 switch (enmArch)
3904 {
3905 case RTLDRARCH_WHATEVER:
3906 fMatch = true;
3907 break;
3908
3909 case RTLDRARCH_X86_32:
3910 fMatch = FatEntry.cputype == CPU_TYPE_X86;
3911 break;
3912
3913 case RTLDRARCH_AMD64:
3914 fMatch = FatEntry.cputype == CPU_TYPE_X86_64;
3915 break;
3916
3917 case RTLDRARCH_ARM32:
3918 case RTLDRARCH_ARM64:
3919 case RTLDRARCH_X86_16:
3920 fMatch = false;
3921 break;
3922
3923 case RTLDRARCH_INVALID:
3924 case RTLDRARCH_HOST:
3925 case RTLDRARCH_END:
3926 case RTLDRARCH_32BIT_HACK:
3927 AssertFailedReturn(VERR_INVALID_PARAMETER);
3928 }
3929 if (fMatch)
3930 return rtldrMachOOpen(pReader, fFlags, enmArch, FatEntry.offset, phLdrMod, pErrInfo);
3931 }
3932
3933 return VERR_LDR_ARCH_MISMATCH;
3934
3935}
3936
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