OpenCore  1.0.4
OpenCore Bootloader
1.0.4
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
KernelCollection.c
Go to the documentation of this file.
1
15#include <Uefi.h>
16
19
20#include <Library/BaseLib.h>
21#include <Library/BaseMemoryLib.h>
22#include <Library/BaseOverflowLib.h>
23#include <Library/DebugLib.h>
24#include <Library/MemoryAllocationLib.h>
27#include <Library/OcFileLib.h>
28
29#include "PrelinkedInternal.h"
30
31STATIC
32UINTN
34 IN OUT PRELINKED_CONTEXT *Context
35 )
36{
37 PRELINKED_KEXT *PrelinkedKext;
38 LIST_ENTRY *Kext;
39 UINTN Size;
40 UINTN CommandSize;
41
42 Size = 0;
43
44 Kext = GetFirstNode (&Context->InjectedKexts);
45 while (!IsNull (&Context->InjectedKexts, Kext)) {
46 PrelinkedKext = GET_INJECTED_KEXT_FROM_LINK (Kext);
47
48 //
49 // Each command must be NUL-terminated and 8-byte aligned.
50 //
51 CommandSize = sizeof (MACH_FILESET_ENTRY_COMMAND) + AsciiStrSize (PrelinkedKext->Identifier);
52 Size += ALIGN_VALUE (CommandSize, 8);
53
54 Kext = GetNextNode (&Context->InjectedKexts, Kext);
55 }
56
57 return Size;
58}
59
60STATIC
61VOID
63 IN OUT PRELINKED_CONTEXT *Context,
64 IN OUT MACH_HEADER_64 *MachHeader
65 )
66{
67 PRELINKED_KEXT *PrelinkedKext;
68 LIST_ENTRY *Kext;
70 MACH_HEADER_64 *KextHeader;
71 UINTN StringSize;
72
73 Command.Address = (UINTN)MachHeader->Commands + MachHeader->CommandsSize;
74
75 Kext = GetFirstNode (&Context->InjectedKexts);
76
77 while (!IsNull (&Context->InjectedKexts, Kext)) {
78 PrelinkedKext = GET_INJECTED_KEXT_FROM_LINK (Kext);
79
80 StringSize = AsciiStrSize (PrelinkedKext->Identifier);
81
82 //
83 // Write 8-byte aligned fileset command.
84 //
85 Command.FilesetEntry->CommandType = MACH_LOAD_COMMAND_FILESET_ENTRY;
86 Command.FilesetEntry->CommandSize = (UINT32)ALIGN_VALUE (sizeof (MACH_FILESET_ENTRY_COMMAND) + StringSize, 8);
87 Command.FilesetEntry->VirtualAddress = PrelinkedKext->Context.VirtualBase;
88 KextHeader = MachoGetMachHeader64 (&PrelinkedKext->Context.MachContext);
89 ASSERT (KextHeader != NULL);
90 ASSERT (
91 (UINTN)KextHeader > (UINTN)Context->Prelinked
92 && (UINTN)KextHeader < (UINTN)Context->Prelinked + Context->PrelinkedSize
93 );
94 Command.FilesetEntry->FileOffset = (UINTN)KextHeader - (UINTN)Context->Prelinked;
95 Command.FilesetEntry->EntryId.Offset = OFFSET_OF (MACH_FILESET_ENTRY_COMMAND, Payload);
96 Command.FilesetEntry->Reserved = 0;
97 CopyMem (Command.FilesetEntry->Payload, PrelinkedKext->Identifier, StringSize);
98 ZeroMem (
99 &Command.FilesetEntry->Payload[StringSize],
100 Command.FilesetEntry->CommandSize - Command.FilesetEntry->EntryId.Offset - StringSize
101 );
102
103 //
104 // Refresh Mach-O header constants to include the new command.
105 //
106 MachHeader->NumCommands++;
107 MachHeader->CommandsSize += Command.FilesetEntry->CommandSize;
108
109 //
110 // Proceed to the next command.
111 //
112 Command.Address += Command.FilesetEntry->CommandSize;
113
114 Kext = GetNextNode (&Context->InjectedKexts, Kext);
115 }
116
117 //
118 // Write a segment covering all the kexts.
119 //
120 Command.Segment64->CommandType = MACH_LOAD_COMMAND_SEGMENT_64;
121 Command.Segment64->CommandSize = sizeof (MACH_SEGMENT_COMMAND_64);
122 CopyMem (Command.Segment64->SegmentName, KC_MOSCOW_SEGMENT, sizeof (KC_MOSCOW_SEGMENT));
123 ZeroMem (
124 &Command.Segment64->SegmentName[sizeof (KC_MOSCOW_SEGMENT)],
125 sizeof (Command.Segment64->SegmentName) - sizeof (KC_MOSCOW_SEGMENT)
126 );
127 Command.Segment64->VirtualAddress = Context->KextsVmAddress;
128 Command.Segment64->FileOffset = Context->KextsFileOffset;
129 //
130 // In KC mode, PrelinkedLastAddress and PrelinkedLastLoadAddress are equal
131 // before KEXTs are injected.
132 //
133 Command.Segment64->Size = Context->PrelinkedLastAddress - Context->KextsVmAddress;
134 Command.Segment64->FileSize = Context->PrelinkedSize - Context->KextsFileOffset;
135 Command.Segment64->MaximumProtection = MACH_SEGMENT_VM_PROT_READ
137 Command.Segment64->InitialProtection = MACH_SEGMENT_VM_PROT_READ
139 Command.Segment64->NumSections = 0;
140 Command.Segment64->Flags = 0;
141
142 //
143 // Refresh Mach-O header constants to include the new segment command.
144 //
145 MachHeader->NumCommands++;
146 MachHeader->CommandsSize += sizeof (MACH_SEGMENT_COMMAND_64);
147
148 //
149 // Proceed to the next command.
150 //
151 Command.Address += Command.FilesetEntry->CommandSize;
152
153 //
154 // Write new __PRELINKED_INFO segment, as the first writeable segment is used by EfiBoot
155 // as a relocation base. Relocation in KC mode is very simplified, and this segment PADDR
156 // is simply used for all the relocations in DYSYMTAB. Unless we leave it untouched, sliding
157 // will "relocate" invalid memory, as new plist is normally put at the end of KC memory.
158 //
159 // Note, that we might need to resolve Context->PrelinkedInfoSegment if regions move upper
160 // and current __PRELINKED_INFO gets shifted.
161 //
162 CopyMem (
163 (VOID *)Command.Address,
164 &Context->PrelinkedInfoSegment->Segment64,
165 Context->PrelinkedInfoSegment->Segment64.CommandSize
166 );
167
168 //
169 // Must choose a different name to avoid collisions.
170 //
171 CopyMem (Context->PrelinkedInfoSegment->Segment64.SegmentName, "__KREMLIN_START", sizeof ("__KREMLIN_START"));
172 CopyMem (Context->PrelinkedInfoSection->Section64.SegmentName, "__KREMLIN_START", sizeof ("__KREMLIN_START"));
173 CopyMem (Context->PrelinkedInfoSection->Section64.SectionName, "__kremlin_start", sizeof ("__kremlin_start"));
174
175 //
176 // Refresh Mach-O header constants to include the new segment command.
177 //
178 MachHeader->NumCommands++;
179 MachHeader->CommandsSize += Context->PrelinkedInfoSegment->Segment64.CommandSize;
180
181 //
182 // Update __PRELINKED_INFO pointers to get them updated at a later stage.
183 //
184 Context->PrelinkedInfoSegment = (MACH_SEGMENT_COMMAND_ANY *)Command.Address;
185 Context->PrelinkedInfoSection = (MACH_SECTION_ANY *)&Context->PrelinkedInfoSegment->Segment64.Sections[0];
186}
187
188EFI_STATUS
190 IN OUT PRELINKED_CONTEXT *Context
191 )
192{
193 MACH_HEADER_64 *MachHeader;
194 MACH_SEGMENT_COMMAND_64 *TextSegment;
195 UINTN CurrentSize;
196 UINTN FilesetSize;
197 UINTN RequiredSize;
198
199 MachHeader = MachoGetMachHeader64 (
200 &Context->PrelinkedMachContext
201 );
202
203 CurrentSize = MachHeader->CommandsSize + sizeof (*MachHeader);
204 FilesetSize = InternalKcGetKextFilesetSize (Context);
205 RequiredSize = FilesetSize + sizeof (MACH_SEGMENT_COMMAND_64) + Context->PrelinkedInfoSegment->Segment64.CommandSize;
206
207 TextSegment = MachoGetSegmentByName64 (
208 &Context->PrelinkedMachContext,
210 );
211
212 DEBUG ((
213 DEBUG_INFO,
214 "OCAK: KC TEXT is %u bytes with %u Mach-O headers need %u\n",
215 (UINT32)(TextSegment != NULL ? TextSegment->FileSize : 0),
216 (UINT32)CurrentSize,
217 (UINT32)RequiredSize
218 ));
219
220 if ( (TextSegment == NULL)
221 || (TextSegment->FileOffset != 0)
222 || (TextSegment->FileSize != TextSegment->Size)
223 || (TextSegment->FileSize < CurrentSize))
224 {
225 return EFI_INVALID_PARAMETER;
226 }
227
228 if (CurrentSize + RequiredSize > TextSegment->FileSize) {
229 //
230 // We do not have enough memory in the header, free some memory by merging
231 // kext segments (__REGION###) into a single RWX segment.
232 // - This is not bad for security as it is actually how it is done before
233 // 11.0, where OSKext::setVMAttributes sets the proper memory permissions
234 // on every kext soon after the kernel loads.
235 // - This is not bad for compatibility as the only thing referencing these
236 // segments is dyld fixups command, and it does not matter whether it
237 // references the base segment or points to the middle of it.
238 // The actual reason this was added might actually be because of the ARM
239 // transition, where you can restrict the memory permissions till the next
240 // hardware reset (like on iOS) and they now really try to require W^X:
241 // https://developer.apple.com/videos/play/wwdc2020/10686/
242 //
243 if (!MachoMergeSegments (&Context->PrelinkedMachContext, KC_REGION_SEGMENT_PREFIX)) {
244 DEBUG ((DEBUG_INFO, "OCAK: Segment expansion failure\n"));
245 return EFI_UNSUPPORTED;
246 }
247
248 CurrentSize = MachHeader->CommandsSize + sizeof (*MachHeader);
249 }
250
251 //
252 // If we do not fit even after the expansion, we are really doomed.
253 // This should never happen.
254 //
255 if (CurrentSize + RequiredSize > TextSegment->FileSize) {
256 DEBUG ((DEBUG_INFO, "OCAK: Used header %u is still too large\n", (UINT32)CurrentSize));
257 return EFI_UNSUPPORTED;
258 }
259
260 //
261 // At this step we have memory for all the new commands.
262 // Just write the here.
263 //
264 InternalKcWriteCommandHeaders (Context, MachHeader);
265
266 return EFI_SUCCESS;
267}
268
269UINT32
271 IN UINT32 SegmentSize
272 )
273{
275
276 ASSERT (SegmentSize % MACHO_PAGE_SIZE == 0);
277
278 if (SegmentSize > PRELINKED_KEXTS_MAX_SIZE) {
279 return 0;
280 }
281
282 return (UINT32)(sizeof (*Dummy)
283 + (SegmentSize / MACHO_PAGE_SIZE) * sizeof (Dummy->PageStart[0]));
284}
285
286EFI_STATUS
288 IN OUT PRELINKED_CONTEXT *Context,
289 IN UINT32 SegChainSize,
290 IN UINT32 ReservedSize
291 )
292{
293 BOOLEAN Result;
294 CONST MACH_LINKEDIT_DATA_COMMAND *DyldChainedFixups;
295 CONST MACHO_DYLD_CHAINED_FIXUPS_HEADER *DyldChainedFixupsHdr;
296 MACH_DYLD_CHAINED_STARTS_IN_IMAGE *DyldChainedStarts;
297 UINT32 DyldChainedStartsSize;
298 UINT32 SegIndex;
299
300 ASSERT (Context != NULL);
301 ASSERT (ReservedSize % MACHO_PAGE_SIZE == 0);
302
303 ASSERT (Context->KextsFixupChains != NULL);
304
305 ASSERT (SegChainSize != 0);
306 ASSERT (
307 SegChainSize >= KcGetSegmentFixupChainsSize (ReservedSize)
308 );
309 //
310 // Context initialisation guarantees the command size is a multiple of 8.
311 //
313 BASE_ALIGNOF (MACH_LINKEDIT_DATA_COMMAND) <= sizeof (UINT64),
314 "Alignment is not guaranteed."
315 );
316
317 DyldChainedFixups = (CONST MACH_LINKEDIT_DATA_COMMAND *)MachoGetNextCommand (
318 &Context->PrelinkedMachContext,
320 NULL
321 );
322 //
323 // Show that StartsInImage is aligned relative to __LINKEDIT start so we only
324 // need to check DataOffset below.
325 //
326 ASSERT ((Context->LinkEditSegment->Segment64.FileOffset % MACHO_PAGE_SIZE) == 0);
329 "Alignment is not guaranteed."
330 );
331
332 if ( (DyldChainedFixups == NULL)
333 || (DyldChainedFixups->CommandSize != sizeof (*DyldChainedFixups))
334 || (DyldChainedFixups->DataSize < sizeof (MACHO_DYLD_CHAINED_FIXUPS_HEADER))
335 || (DyldChainedFixups->DataOffset < Context->LinkEditSegment->Segment64.FileOffset)
336 || ((Context->LinkEditSegment->Segment64.FileOffset + Context->LinkEditSegment->Segment64.FileSize)
337 - DyldChainedFixups->DataOffset < DyldChainedFixups->DataSize)
338 || !BASE_TYPE_ALIGNED (MACHO_DYLD_CHAINED_FIXUPS_HEADER, DyldChainedFixups->DataOffset))
339 {
340 DEBUG ((DEBUG_WARN, "ChainedFixups insane\n"));
341 return EFI_UNSUPPORTED;
342 }
343
344 DyldChainedFixupsHdr = (CONST MACHO_DYLD_CHAINED_FIXUPS_HEADER *)(VOID *)(
345 Context->Prelinked + DyldChainedFixups->DataOffset
346 );
347 if (DyldChainedFixupsHdr->FixupsVersion != 0) {
348 DEBUG ((DEBUG_WARN, "Unrecognised version\n"));
349 }
350
353 "Alignment is not guaranteed."
354 );
355
358 "The subtraction below is unsafe."
359 );
360
361 if ( (DyldChainedFixupsHdr->StartsOffset < sizeof (MACHO_DYLD_CHAINED_FIXUPS_HEADER))
362 || (DyldChainedFixupsHdr->StartsOffset > DyldChainedFixups->DataSize - sizeof (MACH_DYLD_CHAINED_STARTS_IN_IMAGE))
363 || !BASE_TYPE_ALIGNED (MACH_DYLD_CHAINED_STARTS_IN_IMAGE, DyldChainedFixupsHdr->StartsOffset))
364 {
365 DEBUG ((DEBUG_WARN, "ChainedFixupsHdr insane\n"));
366 return EFI_UNSUPPORTED;
367 }
368
369 DyldChainedStarts = (MACH_DYLD_CHAINED_STARTS_IN_IMAGE *)(VOID *)(
370 (UINTN)DyldChainedFixupsHdr + DyldChainedFixupsHdr->StartsOffset
371 );
372
373 Result = BaseOverflowMulAddU32 (
374 DyldChainedStarts->NumSegments,
375 sizeof (*DyldChainedStarts->SegInfoOffset),
376 sizeof (*DyldChainedStarts),
377 &DyldChainedStartsSize
378 );
379 if (Result || (DyldChainedStartsSize > DyldChainedFixups->DataSize - DyldChainedFixupsHdr->StartsOffset)) {
380 DEBUG ((DEBUG_WARN, "DyldChainedFixups insane\n"));
381 return EFI_UNSUPPORTED;
382 }
383
384 for (SegIndex = 0; SegIndex < DyldChainedStarts->NumSegments; ++SegIndex) {
385 if (DyldChainedStarts->SegInfoOffset[SegIndex] == 0) {
386 break;
387 }
388 }
389
390 if (SegIndex == DyldChainedStarts->NumSegments) {
391 DEBUG ((DEBUG_WARN, "No free start\n"));
392 return EFI_UNSUPPORTED;
393 }
394
395 ASSERT ((UINTN)Context->KextsFixupChains > (UINTN)DyldChainedStarts);
396 DyldChainedStarts->SegInfoOffset[SegIndex] = (UINT32)(
397 (UINTN)Context->KextsFixupChains - (UINTN)DyldChainedStarts
398 );
399
400 Context->KextsFixupChains->Size = SegChainSize;
401 Context->KextsFixupChains->PageSize = MACHO_PAGE_SIZE;
402 Context->KextsFixupChains->PointerFormat = MACH_DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE;
403 Context->KextsFixupChains->SegmentOffset = Context->KextsVmAddress - Context->VirtualBase;
404 Context->KextsFixupChains->MaxValidPointer = 0;
405 Context->KextsFixupChains->PageCount = (UINT16)(ReservedSize / MACHO_PAGE_SIZE);
406 //
407 // Initialise all pages with no associated fixups.
408 // Sets all PageStarts to MACH_DYLD_CHAINED_PTR_START_NONE.
409 //
410 SetMem (
411 Context->KextsFixupChains->PageStart,
412 (UINT32)Context->KextsFixupChains->PageCount
413 * sizeof (Context->KextsFixupChains->PageStart[0]),
414 0xFF
415 );
416 return EFI_SUCCESS;
417}
418
419/*
420 Indexes RelocInfo into the fixup chains SegChain of Segment.
421
422 @param[in,out] Context Prelinked context.
423 @param[in] MachContext The context of the Mach-O RelocInfo belongs to. It
424 must have been prelinked by OcAppleKernelLib. The
425 image must reside in Segment.
426 @param[in] RelocInfo The relocation to add a fixup of.
427 @param[in] RelocBase The relocation base address.
428*/
429STATIC
430VOID
432 IN OUT PRELINKED_CONTEXT *Context,
433 IN OC_MACHO_CONTEXT *MachContext,
434 IN CONST MACH_RELOCATION_INFO *RelocInfo,
435 IN UINT64 RelocBase
436 )
437{
438 UINT8 *SegmentData;
439 UINT8 *SegmentPageData;
440
441 UINT64 RelocAddress;
442 UINT32 RelocOffsetInSeg;
443 VOID *RelocDest;
444
445 UINT16 NewFixupPage;
446 UINT16 NewFixupPageOffset;
448
449 UINT16 IterFixupPageOffset;
450 VOID *IterFixupData;
452 UINT16 NextIterFixupPageOffset;
453
454 UINT16 FixupDelta;
455
456 ASSERT (Context != NULL);
457 ASSERT (MachContext != NULL);
458 ASSERT (RelocInfo != NULL);
459
460 ASSERT (Context->KextsFixupChains != NULL);
461 ASSERT (Context->KextsFixupChains->PageSize == MACHO_PAGE_SIZE);
462 //
463 // The entire KEXT and thus its relocations must be in Segment.
464 // Mach-O images are limited to 4 GB size by OcMachoLib, so the cast is safe.
465 //
466 RelocAddress = RelocBase + (UINT32)RelocInfo->Address;
467 RelocOffsetInSeg = (UINT32)(RelocAddress - Context->KextsVmAddress);
468 //
469 // For now we assume we prelinked already and the relocations are sane.
470 //
471 ASSERT (RelocInfo->Extern == 0);
472 ASSERT (RelocInfo->Type == MachX8664RelocUnsigned);
473 ASSERT (RelocAddress >= Context->KextsVmAddress);
474 ASSERT (RelocOffsetInSeg <= Context->PrelinkedSize - Context->KextsFileOffset);
475 ASSERT (Context->KextsFileOffset - RelocOffsetInSeg >= 8);
476 //
477 // Create a new fixup based on the data of RelocInfo.
478 //
479 SegmentData = Context->Prelinked + Context->KextsFileOffset;
480 RelocDest = SegmentData + RelocOffsetInSeg;
481 //
482 // It has been observed all fields but target and next are 0 for the kernel
483 // KC. For isAuth, this is because x86 does not support Pointer
484 // Authentication.
485 //
486 ZeroMem (&NewFixup, sizeof (NewFixup));
487 //
488 // This 1MB here is a bit of a hack. I think it is just the same thing
489 // as KERNEL_BASE_PADDR in OcAfterBootCompatLib.
490 //
491 NewFixup.Target = ReadUnaligned64 (RelocDest) - KERNEL_FIXUP_OFFSET;
492
493 NewFixupPage = (UINT16)(RelocOffsetInSeg / MACHO_PAGE_SIZE);
494 NewFixupPageOffset = (UINT16)(RelocOffsetInSeg % MACHO_PAGE_SIZE);
495
496 IterFixupPageOffset = Context->KextsFixupChains->PageStart[NewFixupPage];
497
498 if (IterFixupPageOffset == MACH_DYLD_CHAINED_PTR_START_NONE) {
499 //
500 // The current page has no fixups, just assign and terminate this one.
501 //
502 Context->KextsFixupChains->PageStart[NewFixupPage] = NewFixupPageOffset;
503 //
504 // Fixup.next is 0 (i.e. the chain is terminated) already.
505 //
506 } else if (NewFixupPageOffset < IterFixupPageOffset) {
507 //
508 // RelocInfo preceeds the first fixup of the page - prepend the new fixup.
509 //
510 NewFixup.Next = IterFixupPageOffset - NewFixupPageOffset;
511 Context->KextsFixupChains->PageStart[NewFixupPage] = NewFixupPageOffset;
512 } else {
513 SegmentPageData = SegmentData + NewFixupPage * MACHO_PAGE_SIZE;
514 //
515 // Find the last fixup of this page that preceeds RelocInfo.
516 // FIXME: Consider sorting the relocations in descending order first to
517 // then always prepend the new fixup.
518 //
519 NextIterFixupPageOffset = IterFixupPageOffset;
520 do {
521 IterFixupPageOffset = NextIterFixupPageOffset;
522 IterFixupData = SegmentPageData + IterFixupPageOffset;
523
524 CopyMem (&IterFixup, IterFixupData, sizeof (IterFixup));
525 NextIterFixupPageOffset = (UINT16)(IterFixupPageOffset + IterFixup.Next);
526 } while (NextIterFixupPageOffset < NewFixupPageOffset && IterFixup.Next != 0);
527
528 FixupDelta = NewFixupPageOffset - IterFixupPageOffset;
529 //
530 // Our new fixup needs to point to the first fixup following it or terminate
531 // if the previous one was the last.
532 //
533 if (IterFixup.Next != 0) {
534 ASSERT (IterFixup.Next >= FixupDelta);
535 NewFixup.Next = IterFixup.Next - FixupDelta;
536 } else {
537 NewFixup.Next = 0;
538 }
539
540 //
541 // The last fixup preceeding RelocInfo must point to our new fixup.
542 //
543 IterFixup.Next = FixupDelta;
544 CopyMem (IterFixupData, &IterFixup, sizeof (IterFixup));
545 }
546
547 CopyMem (RelocDest, &NewFixup, sizeof (NewFixup));
548}
549
550/*
551 Indexes all relocations of MachContext into the kernel described by Context.
552
553 @param[in,out] Context Prelinked context.
554 @param[in] MachContext The context of the Mach-O to index. It must have
555 been prelinked by OcAppleKernelLib. The image
556 must reside in Segment.
557*/
558VOID
560 IN OUT PRELINKED_CONTEXT *Context,
561 IN OC_MACHO_CONTEXT *MachContext
562 )
563{
564 CONST MACH_DYSYMTAB_COMMAND *DySymtab;
565 CONST MACH_SEGMENT_COMMAND_64 *FirstSegment;
566 MACH_HEADER_64 *MachHeader;
567 CONST MACH_RELOCATION_INFO *Relocations;
568 VOID *FileData;
569 UINT32 RelocIndex;
570
571 ASSERT (Context != NULL);
572 ASSERT (MachContext != NULL);
573
574 MachHeader = MachoGetMachHeader64 (MachContext);
575
576 //
577 // Kexts with fixups are now dylibs in cache.
578 // This is required for OSKext_protect to work properly
579 // as the kernel map that operates on vm_map no longer has kext addresses.
580 //
582
583 //
584 // FIXME: The current OcMachoLib was not written with post-linking
585 // re-initialisation in mind. We really don't want to sanitise everything
586 // again, so avoid the dedicated API for now.
587 //
589 MachContext,
591 NULL
592 );
593 //
594 // If DYSYMTAB does not exist, the KEXT must have already not been flagged for
595 // DYLD linkage as otherwise prelinking would have failed.
596 //
597 if (DySymtab == NULL) {
598 return;
599 }
600
601 FirstSegment = MachoGetNextSegment64 (MachContext, NULL);
602 //
603 // DYSYMTAB and at least one segment must exist, otherwise prelinking would
604 // have failed.
605 //
606 ASSERT (DySymtab != NULL);
607 ASSERT (FirstSegment != NULL);
608 //
609 // The Mach-O file to index must be included in Segment.
610 //
611 ASSERT (FirstSegment->VirtualAddress >= Context->KextsVmAddress);
612 ASSERT (MachoGetLastAddress (MachContext) <= Context->PrelinkedLastAddress);
613 //
614 // Prelinking must have eliminated all external relocations.
615 //
616 ASSERT (DySymtab->NumExternalRelocations == 0);
617 //
618 // Convert all relocations to fixups.
619 //
620 FileData = MachoGetFileData (MachContext);
621 Relocations = (MACH_RELOCATION_INFO *)(
622 (UINTN)FileData + DySymtab->LocalRelocationsOffset
623 );
624
625 DEBUG ((
626 DEBUG_INFO,
627 "OCAK: Local relocs %u on %LX\n",
628 DySymtab->NumOfLocalRelocations,
629 FirstSegment->VirtualAddress
630 ));
631
632 for (RelocIndex = 0; RelocIndex < DySymtab->NumOfLocalRelocations; ++RelocIndex) {
634 Context,
635 MachContext,
636 &Relocations[RelocIndex],
637 FirstSegment->VirtualAddress
638 );
639 }
640}
641
642UINT32
644 IN PRELINKED_CONTEXT *Context,
645 IN UINT64 SourceAddress
646 )
647{
649
650 ASSERT (Context != NULL);
651 ASSERT (Context->IsKernelCollection);
652
653 //
654 // Find the KC segment that contains the KEXT at SourceAddress.
655 //
656 for (
657 Segment = MachoGetNextSegment64 (&Context->PrelinkedMachContext, NULL);
658 Segment != NULL;
659 Segment = MachoGetNextSegment64 (&Context->PrelinkedMachContext, Segment)
660 )
661 {
662 if ( (SourceAddress >= Segment->VirtualAddress)
663 && (SourceAddress - Segment->VirtualAddress < Segment->Size))
664 {
665 //
666 // Ensure SourceAddress lies in file memory.
667 //
668 if (SourceAddress - Segment->VirtualAddress > Segment->FileSize) {
669 return 0;
670 }
671
672 //
673 // All kexts in KC use joint __LINKEDIT with the kernel.
674 //
675 return (UINT32)(Context->LinkEditSegment->Segment64.VirtualAddress
676 - SourceAddress + Context->LinkEditSegment->Segment64.Size);
677 }
678 }
679
680 return 0;
681}
682
683EFI_STATUS
685 IN PRELINKED_CONTEXT *PrelinkedContext,
686 IN OUT OC_MACHO_CONTEXT *Context,
687 IN UINT32 Delta
688 )
689{
690 MACH_HEADER_64 *KextHeader;
692 UINTN TopOfCommands;
694 MACH_SYMTAB_COMMAND *Symtab;
695 MACH_DYSYMTAB_COMMAND *DySymtab;
696 UINT32 SectIndex;
697
698 ASSERT (PrelinkedContext != NULL);
699 ASSERT (Context != NULL);
700 ASSERT (Delta > 0);
701
702 KextHeader = MachoGetMachHeader64 (Context);
703 ASSERT (KextHeader != NULL);
704
705 TopOfCommands = ((UINTN)KextHeader->Commands + KextHeader->CommandsSize);
706 //
707 // Iterate over all Load Commands to rebase them based on type.
708 //
709 for (
710 Command = KextHeader->Commands;
711 (UINTN)Command < TopOfCommands;
713 )
714 {
715 switch (Command->CommandType) {
717 if (Command->CommandSize < sizeof (MACH_SEGMENT_COMMAND_64)) {
718 return EFI_UNSUPPORTED;
719 }
720
721 Segment = (MACH_SEGMENT_COMMAND_64 *)(VOID *)Command;
722 //
723 // Rebase the segment's sections.
724 //
725 for (SectIndex = 0; SectIndex < Segment->NumSections; ++SectIndex) {
726 if (Segment->Sections[SectIndex].Offset != 0) {
727 Segment->Sections[SectIndex].Offset += Delta;
728 }
729 }
730
731 //
732 // Rebase the segment itself.
733 //
734 if ((Segment->FileOffset != 0) || (Segment->FileSize != 0)) {
735 Segment->FileOffset += Delta;
736 }
737
738 break;
739
741 if (Command->CommandSize != sizeof (MACH_SYMTAB_COMMAND)) {
742 return EFI_UNSUPPORTED;
743 }
744
745 Symtab = (MACH_SYMTAB_COMMAND *)(VOID *)Command;
746 //
747 // Rebase all SYMTAB offsets.
748 //
749 if (Symtab->SymbolsOffset != 0) {
750 Symtab->SymbolsOffset += Delta;
751 }
752
753 if (Symtab->StringsOffset != 0) {
754 Symtab->StringsOffset += Delta;
755 }
756
757 break;
758
760 if (Command->CommandSize != sizeof (MACH_DYSYMTAB_COMMAND)) {
761 return EFI_UNSUPPORTED;
762 }
763
764 DySymtab = (MACH_DYSYMTAB_COMMAND *)(VOID *)Command;
765 //
766 // Rebase DYSYMTAB fields that make sense in a prelinked context.
767 //
768 if (DySymtab->IndirectSymbolsOffset != 0) {
769 DySymtab->IndirectSymbolsOffset += Delta;
770 }
771
772 //
773 // KC KEXTs use chained fixups indexed by the KC header.
774 //
775 DySymtab->NumOfLocalRelocations = 0;
776 DySymtab->LocalRelocationsOffset = 0;
777
778 break;
779
780 default:
781 //
782 // Other common segments do not contain offsets or they are unused.
783 //
784 break;
785 }
786 }
787
788 //
789 // Update the container offset to make sure we can link against this
790 // kext later as well. Context->InnerSize remains unchanged and is the actual
791 // size of the kext.
792 //
793 Context->FileData = PrelinkedContext->Prelinked;
794 Context->FileSize = PrelinkedContext->PrelinkedSize;
795
796 return EFI_SUCCESS;
797}
798
799UINT64
801 IN UINT64 Value,
802 IN CONST CHAR8 *Name OPTIONAL
803 )
804{
806
807 //
808 // For all non-kernel (which uses own relocation) all virtual tables
809 // and several other symbols will contain fixups exclusively.
810 // For now we will just detect them by the kernel address
811 // as it is faster than compare Kext->Identifier and Context->IsKernelCollection.
812 //
813 if ( ((Value & KERNEL_ADDRESS_MASK) != KERNEL_ADDRESS_BASE)
815 {
816 //
817 // FIXME: This needs a bit more love with aliasing and alignment.
818 // Some day, when Intel rewrites EDK II.
819 //
820 Rebase = (MACH_DYLD_CHAINED_PTR_64_KERNEL_CACHE_REBASE *)(UINTN)&Value;
821 DEBUG_CODE_BEGIN ();
822 if ( (Rebase->CacheLevel != 0)
823 || (Rebase->Diversity != 0)
824 || (Rebase->AddrDiv != 0)
825 || (Rebase->Key != 0)
826 || (Rebase->IsAuth != 0))
827 {
828 DEBUG ((DEBUG_INFO, "OCAK: Invalid fixup %Lx for %a\n", Value, Name != NULL ? Name : "<none>"));
829 }
830
831 DEBUG_CODE_END ();
832
834 }
835
836 return Value;
837}
#define MACH_LOAD_COMMAND_SEGMENT_64
#define MACH_LOAD_COMMAND_DYLD_CHAINED_FIXUPS
#define MACH_LOAD_COMMAND_FILESET_ENTRY
#define NEXT_MACH_LOAD_COMMAND(Command)
#define MACH_LOAD_COMMAND_DYSYMTAB
#define MACH_SEGMENT_VM_PROT_READ
#define MACH_LOAD_COMMAND_SYMTAB
#define MACH_SEGMENT_VM_PROT_EXECUTE
@ MACH_DYLD_CHAINED_PTR_START_NONE
used in page_start[] to denote a page with no fixups
@ MachX8664RelocUnsigned
for absolute addresses
@ MACH_DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE
stride 1, x86_64 kernel caches
#define MACH_HEADER_FLAG_DYLIB_IN_CACHE
#define MACH_SEGMENT_VM_PROT_WRITE
UINT8 Command[7]
Actual command for the Command Page.
Definition AppleNec.h:74
EFI_STATUS KcRebuildMachHeader(IN OUT PRELINKED_CONTEXT *Context)
UINT32 KcGetKextSize(IN PRELINKED_CONTEXT *Context, IN UINT64 SourceAddress)
STATIC VOID InternalKcConvertRelocToFixup(IN OUT PRELINKED_CONTEXT *Context, IN OC_MACHO_CONTEXT *MachContext, IN CONST MACH_RELOCATION_INFO *RelocInfo, IN UINT64 RelocBase)
VOID KcKextIndexFixups(IN OUT PRELINKED_CONTEXT *Context, IN OC_MACHO_CONTEXT *MachContext)
STATIC VOID InternalKcWriteCommandHeaders(IN OUT PRELINKED_CONTEXT *Context, IN OUT MACH_HEADER_64 *MachHeader)
EFI_STATUS KcInitKextFixupChains(IN OUT PRELINKED_CONTEXT *Context, IN UINT32 SegChainSize, IN UINT32 ReservedSize)
EFI_STATUS KcKextApplyFileDelta(IN PRELINKED_CONTEXT *PrelinkedContext, IN OUT OC_MACHO_CONTEXT *Context, IN UINT32 Delta)
UINT64 KcFixupValue(IN UINT64 Value, IN CONST CHAR8 *Name OPTIONAL)
UINT32 KcGetSegmentFixupChainsSize(IN UINT32 SegmentSize)
STATIC UINTN InternalKcGetKextFilesetSize(IN OUT PRELINKED_CONTEXT *Context)
DMG_SIZE_DEVICE_PATH Size
#define PRELINKED_KEXTS_MAX_SIZE
#define KC_TEXT_SEGMENT
#define KC_REGION_SEGMENT_PREFIX
#define KC_MOSCOW_SEGMENT
STATIC_ASSERT(BYTES_PER_PIXEL==sizeof(UINT32), "Non 4-byte pixels are unsupported!")
MACH_SEGMENT_COMMAND_64 * MachoGetNextSegment64(IN OUT OC_MACHO_CONTEXT *Context, IN CONST MACH_SEGMENT_COMMAND_64 *Segment OPTIONAL)
MACH_HEADER_64 * MachoGetMachHeader64(IN OUT OC_MACHO_CONTEXT *Context)
UINT64 MachoGetLastAddress(IN OUT OC_MACHO_CONTEXT *Context)
Definition Header.c:99
VOID * MachoGetFileData(IN OUT OC_MACHO_CONTEXT *Context)
Definition Header.c:66
MACH_SEGMENT_COMMAND_64 * MachoGetSegmentByName64(IN OUT OC_MACHO_CONTEXT *Context, IN CONST CHAR8 *SegmentName)
#define MACHO_PAGE_SIZE
Definition OcMachoLib.h:23
MACH_LOAD_COMMAND * MachoGetNextCommand(IN OUT OC_MACHO_CONTEXT *Context, IN MACH_LOAD_COMMAND_TYPE LoadCommandType, IN CONST MACH_LOAD_COMMAND *LoadCommand OPTIONAL)
Definition Header.c:110
BOOLEAN MachoMergeSegments(IN OUT OC_MACHO_CONTEXT *Context, IN CONST CHAR8 *Prefix)
Definition Header.c:673
#define KERNEL_ADDRESS_MASK
#define KERNEL_ADDRESS_BASE
#define GET_INJECTED_KEXT_FROM_LINK(This)
#define KERNEL_ADDRESS_KEXT
#define KERNEL_FIXUP_OFFSET
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
UINT64 EFIAPI ReadUnaligned64(IN CONST UINT64 *Buffer)
VOID *EFIAPI SetMem(OUT VOID *Buffer, IN UINTN Length, IN UINT8 Value)
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
#define ASSERT(x)
Definition coder.h:55
UINT64 CacheLevel
what level of cache to bind to (indexes a mach_header array)
UINT64 Target
basePointers[cacheLevel] + target
UINT64 IsAuth
0 -> not authenticated. 1 -> authenticated
UINT32 NumOfLocalRelocations
number of local relocation entries
UINT32 LocalRelocationsOffset
offset to local relocation entries
UINT32 CommandsSize
the size of all load commands
MACH_HEADER_FLAGS Flags
flags
MACH_LOAD_COMMAND Commands[]
UINT32 Offset
file offset of this section
UINT32 NumSections
number of sections in segment
UINT64 FileOffset
file offset of this segment
UINT64 Size
memory size of this segment
MACH_SECTION_64 Sections[]
UINT64 FileSize
amount to map from the file
UINT64 VirtualAddress
memory address of this segment
MACH_LOAD_COMMAND_HDR_ UINT32 SymbolsOffset
symbol table offset
UINT32 StringsOffset
string table offset
OC_MACHO_CONTEXT MachContext
PATCHER_CONTEXT Context
CONST CHAR8 * Identifier