VirtualBox

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

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

IPRT: Initial adaption of the kstuff loader code. bugref:9232

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