OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
PrelinkedContext.c
Go to the documentation of this file.
1
15#include <Uefi.h>
16
18
19#include <Library/BaseLib.h>
20#include <Library/BaseMemoryLib.h>
21#include <Library/DebugLib.h>
22#include <Library/MemoryAllocationLib.h>
24#include <Library/OcMachoLib.h>
25#include <Library/OcStringLib.h>
26
27#include "PrelinkedInternal.h"
28
29STATIC
30UINT64
32 IN XML_NODE *KextList
33 )
34{
35 UINT32 KextCount;
36 UINT32 KextIndex;
37 UINT32 FieldIndex;
38 UINT32 FieldCount;
39 XML_NODE *LastKext;
40 CONST CHAR8 *KextPlistKey;
41 XML_NODE *KextPlistValue;
42 UINT64 LoadAddress;
43 UINT64 LoadSize;
44
45 KextCount = XmlNodeChildren (KextList);
46 if (KextCount == 0) {
47 return 0;
48 }
49
50 //
51 // Here we make an assumption that last kext has the highest load address,
52 // yet there might be an arbitrary amount of trailing executable-less kexts.
53 //
54 for (KextIndex = 1; KextIndex <= KextCount; ++KextIndex) {
55 LastKext = PlistNodeCast (XmlNodeChild (KextList, KextCount - KextIndex), PLIST_NODE_TYPE_DICT);
56 if (LastKext == NULL) {
57 return 0;
58 }
59
60 LoadAddress = 0;
61 LoadSize = 0;
62
63 FieldCount = PlistDictChildren (LastKext);
64 for (FieldIndex = 0; FieldIndex < FieldCount; ++FieldIndex) {
65 KextPlistKey = PlistKeyValue (PlistDictChild (LastKext, FieldIndex, &KextPlistValue));
66 if (KextPlistKey == NULL) {
67 continue;
68 }
69
70 if ((LoadAddress == 0) && (AsciiStrCmp (KextPlistKey, PRELINK_INFO_EXECUTABLE_LOAD_ADDR_KEY) == 0)) {
71 if (!PlistIntegerValue (KextPlistValue, &LoadAddress, sizeof (LoadAddress), TRUE)) {
72 return 0;
73 }
74 } else if ((LoadSize == 0) && (AsciiStrCmp (KextPlistKey, PRELINK_INFO_EXECUTABLE_SIZE_KEY) == 0)) {
75 if (!PlistIntegerValue (KextPlistValue, &LoadSize, sizeof (LoadSize), TRUE)) {
76 return 0;
77 }
78 }
79
80 if ((LoadSize != 0) && (LoadAddress != 0)) {
81 break;
82 }
83 }
84
85 if (BaseOverflowAddU64 (LoadAddress, LoadSize, &LoadAddress)) {
86 return 0;
87 }
88
89 if (LoadAddress != 0) {
90 break;
91 }
92 }
93
94 return LoadAddress;
95}
96
97STATIC
98EFI_STATUS
100 IN OC_MACHO_CONTEXT *MachoContext,
101 OUT MACH_SEGMENT_COMMAND_ANY **PrelinkedInfoSegment,
102 OUT MACH_SECTION_ANY **PrelinkedInfoSection
103 )
104{
105 *PrelinkedInfoSegment = MachoGetSegmentByName (
106 MachoContext,
108 );
109 if (*PrelinkedInfoSegment == NULL) {
110 return EFI_NOT_FOUND;
111 }
112
113 if ((MachoContext->Is32Bit ? (*PrelinkedInfoSegment)->Segment32.FileOffset : (*PrelinkedInfoSegment)->Segment64.FileOffset) > MAX_UINT32) {
114 return EFI_UNSUPPORTED;
115 }
116
117 *PrelinkedInfoSection = MachoGetSectionByName (
118 MachoContext,
119 *PrelinkedInfoSegment,
121 );
122 if (*PrelinkedInfoSection == NULL) {
123 return EFI_NOT_FOUND;
124 }
125
126 if ((MachoContext->Is32Bit ? (*PrelinkedInfoSection)->Section32.Size : (*PrelinkedInfoSection)->Section64.Size) > MAX_UINT32) {
127 return EFI_UNSUPPORTED;
128 }
129
130 return EFI_SUCCESS;
131}
132
133EFI_STATUS
135 IN OUT OC_MACHO_CONTEXT *Context,
136 OUT OC_MACHO_CONTEXT *InnerContext,
137 IN UINT8 *Buffer,
138 IN UINT32 BufferSize,
139 OUT BOOLEAN *KernelCollection OPTIONAL
140 )
141{
144 BOOLEAN IsKernelCollection;
145
146 //
147 // Detect kernel type.
148 //
149 Header = MachoGetMachHeader (Context);
150 IsKernelCollection = (Context->Is32Bit ?
151 Header->Header32.FileType : Header->Header64.FileType) == MachHeaderFileTypeFileSet;
152
153 if (KernelCollection != NULL) {
154 *KernelCollection = IsKernelCollection;
155 }
156
157 //
158 // When dealing with the kernel collections the actual kernel is pointed by one of the segments.
159 //
160 if (IsKernelCollection) {
161 Segment = MachoGetSegmentByName64 (
162 Context,
163 "__TEXT_EXEC"
164 );
165 if ((Segment == NULL) || (Segment->VirtualAddress < Segment->FileOffset)) {
166 DEBUG ((
167 DEBUG_INFO,
168 "OCAK: KC symtab failed locating inner %Lx %Lx (%d)\n",
169 Segment != NULL ? Segment->VirtualAddress : 0,
170 Segment != NULL ? Segment->FileOffset : 0,
171 Segment != NULL
172 ));
173 return EFI_INVALID_PARAMETER;
174 }
175
177 InnerContext,
178 Buffer,
179 BufferSize,
180 (UINT32)Segment->FileOffset,
181 (UINT32)(BufferSize - Segment->FileOffset)
182 ))
183 {
184 DEBUG ((
185 DEBUG_INFO,
186 "OCAK: KC symtab failed initialising inner %Lx %x\n",
187 Segment->FileOffset,
188 BufferSize
189 ));
190 return EFI_INVALID_PARAMETER;
191 }
192
193 if (!MachoInitialiseSymtabsExternal (Context, InnerContext)) {
194 DEBUG ((
195 DEBUG_INFO,
196 "OCAK: KC symtab failed getting symtab from inner %Lx %x\n",
197 Segment->FileOffset,
198 BufferSize
199 ));
200 return EFI_INVALID_PARAMETER;
201 }
202 }
203
204 return EFI_SUCCESS;
205}
206
207EFI_STATUS
209 IN OUT PRELINKED_CONTEXT *Context,
210 IN OUT UINT8 *Prelinked,
211 IN UINT32 PrelinkedSize,
212 IN UINT32 PrelinkedAllocSize,
213 IN BOOLEAN Is32Bit
214 )
215{
216 EFI_STATUS Status;
217 XML_NODE *DocumentRoot;
218 XML_NODE *PrelinkedInfoRoot;
219 CONST CHAR8 *PrelinkedInfoRootKey;
220 UINT64 SegmentEndOffset;
221 UINT32 PrelinkedInfoRootIndex;
222 UINT32 PrelinkedInfoRootCount;
223 PRELINKED_KEXT *PrelinkedKext;
224
225 ASSERT (Context != NULL);
226 ASSERT (Prelinked != NULL);
227 ASSERT (PrelinkedSize > 0);
228 ASSERT (PrelinkedAllocSize >= PrelinkedSize);
229
230 ZeroMem (Context, sizeof (*Context));
231
232 Context->Prelinked = Prelinked;
233 Context->PrelinkedSize = MACHO_ALIGN (PrelinkedSize);
234 Context->PrelinkedAllocSize = PrelinkedAllocSize;
235 Context->Is32Bit = Is32Bit;
236
237 //
238 // Ensure that PrelinkedSize is always aligned.
239 //
240 if (Context->PrelinkedSize != PrelinkedSize) {
241 if (Context->PrelinkedSize > PrelinkedAllocSize) {
242 return EFI_BUFFER_TOO_SMALL;
243 }
244
245 ZeroMem (&Prelinked[PrelinkedSize], Context->PrelinkedSize - PrelinkedSize);
246 }
247
248 //
249 // Initialise primary context.
250 //
251 if (!MachoInitializeContext (&Context->PrelinkedMachContext, Prelinked, PrelinkedSize, 0, PrelinkedSize, Context->Is32Bit)) {
252 return EFI_INVALID_PARAMETER;
253 }
254
256 &Context->PrelinkedMachContext,
257 &Context->InnerMachContext,
258 Context->Prelinked,
259 Context->PrelinkedSize,
260 &Context->IsKernelCollection
261 );
262 if (EFI_ERROR (Status)) {
263 return Status;
264 }
265
266 //
267 // Initialize kext list with kernel pseudo kext.
268 //
269 InitializeListHead (&Context->PrelinkedKexts);
270 InitializeListHead (&Context->InjectedKexts);
271 PrelinkedKext = InternalCachedPrelinkedKernel (Context);
272 if (PrelinkedKext == NULL) {
273 return EFI_INVALID_PARAMETER;
274 }
275
276 Context->VirtualBase = PrelinkedKext->Context.VirtualBase;
277
278 Context->PrelinkedLastAddress = MACHO_ALIGN (MachoGetLastAddress (&Context->PrelinkedMachContext));
279 if (Context->PrelinkedLastAddress == 0) {
280 return EFI_INVALID_PARAMETER;
281 }
282
283 //
284 // Register normal segment entries.
285 //
287 &Context->PrelinkedMachContext,
288 &Context->PrelinkedInfoSegment,
289 &Context->PrelinkedInfoSection
290 );
291 if (EFI_ERROR (Status)) {
292 return Status;
293 }
294
295 if (Context->IsKernelCollection) {
296 //
297 // Additionally process special entries for KC.
298 //
300 &Context->InnerMachContext,
301 (MACH_SEGMENT_COMMAND_ANY **)&Context->InnerInfoSegment,
302 (MACH_SECTION_ANY **)&Context->InnerInfoSection
303 );
304 if (EFI_ERROR (Status)) {
305 return Status;
306 }
307
308 Context->RegionSegment = MachoGetSegmentByName64 (
309 &Context->PrelinkedMachContext,
311 );
312 if (Context->RegionSegment == NULL) {
313 return EFI_NOT_FOUND;
314 }
315
316 Context->LinkEditSegment = MachoGetSegmentByName (
317 &Context->PrelinkedMachContext,
319 );
320 if (Context->LinkEditSegment == NULL) {
321 return EFI_NOT_FOUND;
322 }
323 } else {
324 Context->PrelinkedTextSegment = MachoGetSegmentByName (
325 &Context->PrelinkedMachContext,
327 );
328 if (Context->PrelinkedTextSegment == NULL) {
329 return EFI_NOT_FOUND;
330 }
331
332 Context->PrelinkedTextSection = MachoGetSectionByName (
333 &Context->PrelinkedMachContext,
334 Context->PrelinkedTextSegment,
336 );
337 if (Context->PrelinkedTextSection == NULL) {
338 return EFI_NOT_FOUND;
339 }
340 }
341
342 Context->PrelinkedInfo = Context->Is32Bit ?
343 AllocateCopyPool (
344 Context->PrelinkedInfoSection->Section32.Size,
345 &Context->Prelinked[Context->PrelinkedInfoSection->Section32.Offset]
346 ) :
347 AllocateCopyPool (
348 (UINTN)Context->PrelinkedInfoSection->Section64.Size,
349 &Context->Prelinked[Context->PrelinkedInfoSection->Section64.Offset]
350 );
351 if (Context->PrelinkedInfo == NULL) {
352 PrelinkedContextFree (Context);
353 return EFI_OUT_OF_RESOURCES;
354 }
355
356 Context->PrelinkedInfoDocument = XmlDocumentParse (
357 Context->PrelinkedInfo,
358 (UINT32)(Context->Is32Bit ?
359 Context->PrelinkedInfoSection->Section32.Size : Context->PrelinkedInfoSection->Section64.Size),
360 TRUE
361 );
362 if (Context->PrelinkedInfoDocument == NULL) {
363 PrelinkedContextFree (Context);
364 return EFI_INVALID_PARAMETER;
365 }
366
367 //
368 // For a kernel collection the this is a full plist, while for legacy prelinked format
369 // it starts with a <dict> node.
370 //
371 if (Context->IsKernelCollection) {
372 DocumentRoot = PlistDocumentRoot (Context->PrelinkedInfoDocument);
373 } else {
374 DocumentRoot = XmlDocumentRoot (Context->PrelinkedInfoDocument);
375 }
376
377 PrelinkedInfoRoot = PlistNodeCast (DocumentRoot, PLIST_NODE_TYPE_DICT);
378 if (PrelinkedInfoRoot == NULL) {
379 PrelinkedContextFree (Context);
380 return EFI_INVALID_PARAMETER;
381 }
382
383 if (Context->IsKernelCollection) {
384 //
385 // In KC mode last load address is the __LINKEDIT address.
386 //
387 SegmentEndOffset = Context->LinkEditSegment->Segment64.FileOffset + Context->LinkEditSegment->Segment64.FileSize;
388
389 if (MACHO_ALIGN (SegmentEndOffset) != Context->PrelinkedSize) {
390 PrelinkedContextFree (Context);
391 return EFI_INVALID_PARAMETER;
392 }
393
394 Context->PrelinkedLastLoadAddress = Context->LinkEditSegment->Segment64.VirtualAddress + Context->LinkEditSegment->Segment64.Size;
395 }
396
397 //
398 // In legacy mode last load address is last kext address.
399 //
400 PrelinkedInfoRootCount = PlistDictChildren (PrelinkedInfoRoot);
401 for (PrelinkedInfoRootIndex = 0; PrelinkedInfoRootIndex < PrelinkedInfoRootCount; ++PrelinkedInfoRootIndex) {
402 PrelinkedInfoRootKey = PlistKeyValue (PlistDictChild (PrelinkedInfoRoot, PrelinkedInfoRootIndex, &Context->KextList));
403 if (PrelinkedInfoRootKey == NULL) {
404 continue;
405 }
406
407 if (AsciiStrCmp (PrelinkedInfoRootKey, PRELINK_INFO_DICTIONARY_KEY) == 0) {
408 if (PlistNodeCast (Context->KextList, PLIST_NODE_TYPE_ARRAY) != NULL) {
409 if (Context->PrelinkedLastLoadAddress == 0) {
410 Context->PrelinkedLastLoadAddress = PrelinkedFindLastLoadAddress (Context->KextList);
411 }
412
413 if (Context->PrelinkedLastLoadAddress != 0) {
414 return EFI_SUCCESS;
415 }
416 }
417
418 break;
419 }
420 }
421
422 PrelinkedContextFree (Context);
423 return EFI_INVALID_PARAMETER;
424}
425
426VOID
428 IN OUT PRELINKED_CONTEXT *Context
429 )
430{
431 UINT32 Index;
432 LIST_ENTRY *Link;
433 PRELINKED_KEXT *Kext;
434
435 if (Context->PrelinkedInfoDocument != NULL) {
436 XmlDocumentFree (Context->PrelinkedInfoDocument);
437 Context->PrelinkedInfoDocument = NULL;
438 }
439
440 if (Context->KextScratchBuffer != NULL) {
441 FreePool (Context->KextScratchBuffer);
442 Context->KextScratchBuffer = NULL;
443 }
444
445 if (Context->PrelinkedInfo != NULL) {
446 FreePool (Context->PrelinkedInfo);
447 Context->PrelinkedInfo = NULL;
448 }
449
450 if (Context->PooledBuffers != NULL) {
451 for (Index = 0; Index < Context->PooledBuffersCount; ++Index) {
452 FreePool (Context->PooledBuffers[Index]);
453 }
454
455 FreePool (Context->PooledBuffers);
456 Context->PooledBuffers = NULL;
457 }
458
459 if (Context->LinkBuffer != NULL) {
460 ZeroMem (Context->LinkBuffer, Context->LinkBufferSize);
461 FreePool (Context->LinkBuffer);
462 Context->LinkBuffer = NULL;
463 }
464
465 if (Context->PrelinkedStateKernel != NULL) {
466 FreePool (Context->PrelinkedStateKernel);
467 Context->PrelinkedStateKernel = NULL;
468 }
469
470 if (Context->PrelinkedStateKexts != NULL) {
471 FreePool (Context->PrelinkedStateKexts);
472 Context->PrelinkedStateKexts = NULL;
473 }
474
475 while (!IsListEmpty (&Context->PrelinkedKexts)) {
476 Link = GetFirstNode (&Context->PrelinkedKexts);
477 Kext = GET_PRELINKED_KEXT_FROM_LINK (Link);
478 RemoveEntryList (Link);
480 }
481
482 ZeroMem (&Context->PrelinkedKexts, sizeof (Context->PrelinkedKexts));
483
484 //
485 // We do not need to iterate InjectedKexts here, as its memory was freed above.
486 //
487 ZeroMem (&Context->InjectedKexts, sizeof (Context->InjectedKexts));
488}
489
490EFI_STATUS
492 IN OUT PRELINKED_CONTEXT *Context,
493 IN VOID *Buffer
494 )
495{
496 VOID **NewPooledBuffers;
497
498 if (Context->PooledBuffersCount == Context->PooledBuffersAllocCount) {
499 NewPooledBuffers = AllocatePool (
500 2 * (Context->PooledBuffersAllocCount + 1) * sizeof (NewPooledBuffers[0])
501 );
502 if (NewPooledBuffers == NULL) {
503 return EFI_OUT_OF_RESOURCES;
504 }
505
506 if (Context->PooledBuffers != NULL) {
507 CopyMem (
508 &NewPooledBuffers[0],
509 &Context->PooledBuffers[0],
510 Context->PooledBuffersCount * sizeof (NewPooledBuffers[0])
511 );
512 FreePool (Context->PooledBuffers);
513 }
514
515 Context->PooledBuffers = NewPooledBuffers;
516 Context->PooledBuffersAllocCount = 2 * (Context->PooledBuffersAllocCount + 1);
517 }
518
519 Context->PooledBuffers[Context->PooledBuffersCount] = Buffer;
520 Context->PooledBuffersCount++;
521
522 return EFI_SUCCESS;
523}
524
525EFI_STATUS
527 IN OUT PRELINKED_CONTEXT *Context,
528 IN UINT32 LinkedExpansion,
529 IN UINT32 ReservedExeSize
530 )
531{
532 EFI_STATUS Status;
533 UINT64 SegmentEndOffset;
534 UINT32 AlignedExpansion;
535
536 if (Context->IsKernelCollection) {
537 //
538 // For newer variant (KC mode) __LINKEDIT is last, and we need to expand it to enable
539 // dyld fixup generation.
540 //
541 if ( (Context->PrelinkedAllocSize < LinkedExpansion)
542 || (Context->PrelinkedAllocSize - LinkedExpansion < Context->PrelinkedSize))
543 {
544 return EFI_OUT_OF_RESOURCES;
545 }
546
547 ASSERT (Context->PrelinkedLastAddress == Context->PrelinkedLastLoadAddress);
548 //
549 // Ensured by PrelinkedContextInit().
550 //
551 ASSERT (Context->PrelinkedSize % MACHO_PAGE_SIZE == 0);
554 "KextsFixupChains may be unaligned"
555 );
556
557 Context->KextsFixupChains = (VOID *)(Context->Prelinked + Context->PrelinkedSize);
558
559 AlignedExpansion = MACHO_ALIGN (LinkedExpansion);
560 //
561 // Zero the expansion to account for padding.
562 //
563 ZeroMem (
564 Context->Prelinked + Context->PrelinkedSize,
565 AlignedExpansion
566 );
567
568 Context->PrelinkedSize += AlignedExpansion;
569 Context->PrelinkedLastAddress += AlignedExpansion;
570 Context->PrelinkedLastLoadAddress += AlignedExpansion;
571 Context->LinkEditSegment->Segment64.Size += AlignedExpansion;
572 Context->LinkEditSegment->Segment64.FileSize += AlignedExpansion;
573 } else {
574 //
575 // For older variant of the prelinkedkernel plist info is normally
576 // the last segment, so we may potentially save some data by removing
577 // it and then appending new kexts over. This is different for KC,
578 // where plist info is in the middle of the file. For 10.6.8 we will
579 // also need to move __PRELINK_STATE, which is just some blob for kextd.
580 //
581 // 10.6.8
582 //
583 // ffffff8000200000 - ffffff8000600000 (at 0000000000000000 - 0000000000400000) - __TEXT
584 // ffffff8000600000 - ffffff80006da000 (at 0000000000400000 - 000000000046f000) - __DATA
585 // ffffff8000106000 - ffffff8000107000 (at 000000000046f000 - 0000000000470000) - __INITGDT
586 // ffffff8000100000 - ffffff8000106000 (at 0000000000470000 - 0000000000476000) - __INITPT
587 // ffffff80006da000 - ffffff80006db000 (at 0000000000476000 - 0000000000477000) - __DESC
588 // ffffff80006db000 - ffffff80006dc000 (at 0000000000477000 - 0000000000478000) - __VECTORS
589 // ffffff8000108000 - ffffff800010e000 (at 0000000000478000 - 000000000047d000) - __HIB
590 // ffffff8000107000 - ffffff8000108000 (at 000000000047d000 - 000000000047e000) - __SLEEP
591 // ffffff80006dc000 - ffffff80006de000 (at 000000000047e000 - 0000000000480000) - __KLD
592 // ffffff80006de000 - ffffff80006de000 (at 0000000000480000 - 0000000000480000) - __LAST
593 // ffffff8000772000 - ffffff800100a000 (at 0000000000514000 - 0000000000dac000) - __PRELINK_TEXT
594 // ffffff800100a000 - ffffff800155c000 (at 0000000000dac000 - 00000000012fe000) - __PRELINK_STATE
595 // ffffff800155c000 - ffffff8001612000 (at 00000000012fe000 - 00000000013b4000) - __PRELINK_INFO
596 // 0000000000000000 - 0000000000000000 (at 0000000000513018 - 0000000000551269) - __CTF
597 // ffffff80006de000 - ffffff8000771018 (at 0000000000480000 - 0000000000513018) - __LINKEDIT
598 //
599 // 10.15.6
600 //
601 // ffffff8000200000 - ffffff8000c00000 (at 0000000000000000 - 0000000000a00000) - __TEXT
602 // ffffff8000c00000 - ffffff8000e70000 (at 0000000000a00000 - 0000000000c70000) - __DATA
603 // ffffff8000e70000 - ffffff8000ea9000 (at 0000000000c70000 - 0000000000ca9000) - __DATA_CONST
604 // ffffff8000100000 - ffffff800019e000 (at 0000000000ca9000 - 0000000000d47000) - __HIB
605 // ffffff8000ea9000 - ffffff8000eaa000 (at 0000000000d47000 - 0000000000d48000) - __VECTORS
606 // ffffff8000eaa000 - ffffff8000ec4000 (at 0000000000d48000 - 0000000000d62000) - __KLD
607 // ffffff8000ec4000 - ffffff8000ec5000 (at 0000000000d62000 - 0000000000d63000) - __LAST
608 // ffffff8001036000 - ffffff8002e24000 (at 0000000000f48000 - 0000000002d36000) - __PRELINK_TEXT
609 // ffffff8002e24000 - ffffff80030d2000 (at 0000000002d36000 - 0000000002fe3389) - __PRELINK_INFO
610 // ffffff8000ec5000 - ffffff8000ec5000 (at 0000000000d63000 - 0000000000dd7000) - __CTF
611 // ffffff8000ec5000 - ffffff80010358a8 (at 0000000000dd7000 - 0000000000f478a8) - __LINKEDIT
612 //
613 SegmentEndOffset = Context->Is32Bit ?
614 Context->PrelinkedInfoSegment->Segment32.FileOffset + Context->PrelinkedInfoSegment->Segment32.FileSize :
615 Context->PrelinkedInfoSegment->Segment64.FileOffset + Context->PrelinkedInfoSegment->Segment64.FileSize;
616
617 if (MACHO_ALIGN (SegmentEndOffset) == Context->PrelinkedSize) {
618 DEBUG ((
619 DEBUG_INFO,
620 "OCAK: Reducing %a-bit prelink size from %X to %X via plist\n",
621 Context->Is32Bit ? "32" : "64",
622 Context->PrelinkedSize,
623 (UINT32)MACHO_ALIGN (
624 Context->Is32Bit ?
625 Context->PrelinkedInfoSegment->Segment32.FileOffset : Context->PrelinkedInfoSegment->Segment64.FileOffset
626 )
627 ));
628 Context->PrelinkedSize = (UINT32)MACHO_ALIGN (
629 Context->Is32Bit ?
630 Context->PrelinkedInfoSegment->Segment32.FileOffset : Context->PrelinkedInfoSegment->Segment64.FileOffset
631 );
632 } else {
633 DEBUG ((
634 DEBUG_INFO,
635 "OCAK: Leaving unchanged %a-bit prelink size %X due to %LX plist\n",
636 Context->Is32Bit ? "32" : "64",
637 Context->PrelinkedSize,
638 SegmentEndOffset
639 ));
640 }
641
642 if (Context->PrelinkedStateSegment != NULL) {
643 SegmentEndOffset = Context->Is32Bit ?
644 Context->PrelinkedStateSegment->Segment32.FileOffset + Context->PrelinkedStateSegment->Segment32.FileSize :
645 Context->PrelinkedStateSegment->Segment64.FileOffset + Context->PrelinkedStateSegment->Segment64.FileSize;
646
647 if (MACHO_ALIGN (SegmentEndOffset) == Context->PrelinkedSize) {
648 DEBUG ((
649 DEBUG_INFO,
650 "OCAK: Reducing %a-bit prelink size from %X to %X via state\n",
651 Context->Is32Bit ? "32" : "64",
652 Context->PrelinkedSize,
653 (UINT32)MACHO_ALIGN (
654 Context->Is32Bit ?
655 Context->PrelinkedStateSegment->Segment32.FileOffset : Context->PrelinkedStateSegment->Segment64.FileOffset
656 )
657 ));
658 Context->PrelinkedSize = (UINT32)MACHO_ALIGN (
659 Context->Is32Bit ?
660 Context->PrelinkedStateSegment->Segment32.FileOffset : Context->PrelinkedStateSegment->Segment64.FileOffset
661 );
662
663 //
664 // Need to NULL this, as they are used in address calculations
665 // in e.g. MachoGetLastAddress.
666 //
667 if (Context->Is32Bit) {
668 Context->PrelinkedStateSegment->Segment32.VirtualAddress = 0;
669 Context->PrelinkedStateSegment->Segment32.Size = 0;
670 Context->PrelinkedStateSegment->Segment32.FileOffset = 0;
671 Context->PrelinkedStateSegment->Segment32.FileSize = 0;
672 Context->PrelinkedStateSectionKernel->Section32.Address = 0;
673 Context->PrelinkedStateSectionKernel->Section32.Size = 0;
674 Context->PrelinkedStateSectionKernel->Section32.Offset = 0;
675 Context->PrelinkedStateSectionKexts->Section32.Address = 0;
676 Context->PrelinkedStateSectionKexts->Section32.Size = 0;
677 Context->PrelinkedStateSectionKexts->Section32.Offset = 0;
678 } else {
679 Context->PrelinkedStateSegment->Segment64.VirtualAddress = 0;
680 Context->PrelinkedStateSegment->Segment64.Size = 0;
681 Context->PrelinkedStateSegment->Segment64.FileOffset = 0;
682 Context->PrelinkedStateSegment->Segment64.FileSize = 0;
683 Context->PrelinkedStateSectionKernel->Section64.Address = 0;
684 Context->PrelinkedStateSectionKernel->Section64.Size = 0;
685 Context->PrelinkedStateSectionKernel->Section64.Offset = 0;
686 Context->PrelinkedStateSectionKexts->Section64.Address = 0;
687 Context->PrelinkedStateSectionKexts->Section64.Size = 0;
688 Context->PrelinkedStateSectionKexts->Section64.Offset = 0;
689 }
690 } else {
691 DEBUG ((
692 DEBUG_INFO,
693 "OCAK: Leaving unchanged %a-bit prelink size %X due to %LX state\n",
694 Context->Is32Bit ? "32" : "64",
695 Context->PrelinkedSize,
696 SegmentEndOffset
697 ));
698 }
699 }
700
701 if (Context->Is32Bit) {
702 Context->PrelinkedInfoSegment->Segment32.VirtualAddress = 0;
703 Context->PrelinkedInfoSegment->Segment32.Size = 0;
704 Context->PrelinkedInfoSegment->Segment32.FileOffset = 0;
705 Context->PrelinkedInfoSegment->Segment32.FileSize = 0;
706 Context->PrelinkedInfoSection->Section32.Address = 0;
707 Context->PrelinkedInfoSection->Section32.Size = 0;
708 Context->PrelinkedInfoSection->Section32.Offset = 0;
709 } else {
710 Context->PrelinkedInfoSegment->Segment64.VirtualAddress = 0;
711 Context->PrelinkedInfoSegment->Segment64.Size = 0;
712 Context->PrelinkedInfoSegment->Segment64.FileOffset = 0;
713 Context->PrelinkedInfoSegment->Segment64.FileSize = 0;
714 Context->PrelinkedInfoSection->Section64.Address = 0;
715 Context->PrelinkedInfoSection->Section64.Size = 0;
716 Context->PrelinkedInfoSection->Section64.Offset = 0;
717 }
718
719 Context->PrelinkedLastAddress = MACHO_ALIGN (MachoGetLastAddress (&Context->PrelinkedMachContext));
720 if (Context->PrelinkedLastAddress == 0) {
721 return EFI_INVALID_PARAMETER;
722 }
723
724 //
725 // Prior to plist there usually is prelinked text.
726 //
727 SegmentEndOffset = Context->Is32Bit ?
728 Context->PrelinkedTextSegment->Segment32.FileOffset + Context->PrelinkedTextSegment->Segment32.FileSize :
729 Context->PrelinkedTextSegment->Segment64.FileOffset + Context->PrelinkedTextSegment->Segment64.FileSize;
730
731 if (MACHO_ALIGN (SegmentEndOffset) != Context->PrelinkedSize) {
732 //
733 // TODO: Implement prelinked text relocation when it is not preceding prelinked info
734 // and is not in the end of prelinked info.
735 //
736 return EFI_UNSUPPORTED;
737 }
738 }
739
740 //
741 // Append the injected KEXTs to the current kernel end.
742 //
743 Context->KextsFileOffset = Context->PrelinkedSize;
744 Context->KextsVmAddress = Context->PrelinkedLastAddress;
745
746 if (Context->IsKernelCollection) {
747 Status = KcInitKextFixupChains (Context, LinkedExpansion, ReservedExeSize);
748 if (EFI_ERROR (Status)) {
749 return Status;
750 }
751 }
752
753 return EFI_SUCCESS;
754}
755
756EFI_STATUS
758 IN OUT PRELINKED_CONTEXT *Context
759 )
760{
761 EFI_STATUS Status;
762 CHAR8 *ExportedInfo;
763 UINT32 ExportedInfoSize;
764 UINT32 NewSize;
765 UINT32 KextsSize;
766 UINT32 ChainSize;
767
768 if (Context->IsKernelCollection) {
769 //
770 // Fix up the segment fixup chains structure to reflect the actually
771 // injected segment size.
772 //
773 KextsSize = Context->PrelinkedSize - Context->KextsFileOffset;
774
775 ChainSize = KcGetSegmentFixupChainsSize (KextsSize);
776 ASSERT (ChainSize != 0);
777 ASSERT (ChainSize <= Context->KextsFixupChains->Size);
778
779 Context->KextsFixupChains->Size = ChainSize;
780 Context->KextsFixupChains->PageCount = (UINT16)(KextsSize / MACHO_PAGE_SIZE);
781
782 Status = KcRebuildMachHeader (Context);
783 if (EFI_ERROR (Status)) {
784 return Status;
785 }
786 } else if ((Context->PrelinkedStateSegment != NULL) && ((Context->Is32Bit ?
787 Context->PrelinkedStateSegment->Segment32.VirtualAddress : Context->PrelinkedStateSegment->Segment64.VirtualAddress)
788 == 0))
789 {
790 Status = InternalKxldStateRebuild (Context);
791 if (EFI_ERROR (Status)) {
792 return Status;
793 }
794 }
795
796 ExportedInfo = XmlDocumentExport (Context->PrelinkedInfoDocument, &ExportedInfoSize, 0, FALSE);
797 if (ExportedInfo == NULL) {
798 return EFI_OUT_OF_RESOURCES;
799 }
800
801 //
802 // Include \0 terminator.
803 //
804 ExportedInfoSize++;
805
806 if ( BaseOverflowAddU32 (Context->PrelinkedSize, MACHO_ALIGN (ExportedInfoSize), &NewSize)
807 || (NewSize > Context->PrelinkedAllocSize))
808 {
809 FreePool (ExportedInfo);
810 return EFI_BUFFER_TOO_SMALL;
811 }
812
813 #if 0
814 //
815 // This is a potential optimisation for smaller kexts allowing us to use less space.
816 // This requires disable __KREMLIN relocation segment addition.
817 //
818 if (Context->IsKernelCollection && (MACHO_ALIGN (ExportedInfoSize) <= Context->PrelinkedInfoSegment->Size)) {
819 CopyMem (
820 &Context->Prelinked[Context->PrelinkedInfoSegment->FileOffset],
821 ExportedInfo,
822 ExportedInfoSize
823 );
824
825 ZeroMem (
826 &Context->Prelinked[Context->PrelinkedInfoSegment->FileOffset + ExportedInfoSize],
827 Context->PrelinkedInfoSegment->FileSize - ExportedInfoSize
828 );
829
830 FreePool (ExportedInfo);
831
832 return EFI_SUCCESS;
833 }
834
835 #endif
836
837 if (Context->Is32Bit) {
838 Context->PrelinkedInfoSegment->Segment32.VirtualAddress = (UINT32)Context->PrelinkedLastAddress;
839 Context->PrelinkedInfoSegment->Segment32.Size = MACHO_ALIGN (ExportedInfoSize);
840 Context->PrelinkedInfoSegment->Segment32.FileOffset = Context->PrelinkedSize;
841 Context->PrelinkedInfoSegment->Segment32.FileSize = MACHO_ALIGN (ExportedInfoSize);
842 Context->PrelinkedInfoSection->Section32.Address = (UINT32)Context->PrelinkedLastAddress;
843 Context->PrelinkedInfoSection->Section32.Size = MACHO_ALIGN (ExportedInfoSize);
844 Context->PrelinkedInfoSection->Section32.Offset = Context->PrelinkedSize;
845 } else {
846 Context->PrelinkedInfoSegment->Segment64.VirtualAddress = Context->PrelinkedLastAddress;
847 Context->PrelinkedInfoSegment->Segment64.Size = MACHO_ALIGN (ExportedInfoSize);
848 Context->PrelinkedInfoSegment->Segment64.FileOffset = Context->PrelinkedSize;
849 Context->PrelinkedInfoSegment->Segment64.FileSize = MACHO_ALIGN (ExportedInfoSize);
850 Context->PrelinkedInfoSection->Section64.Address = Context->PrelinkedLastAddress;
851 Context->PrelinkedInfoSection->Section64.Size = MACHO_ALIGN (ExportedInfoSize);
852 Context->PrelinkedInfoSection->Section64.Offset = Context->PrelinkedSize;
853 }
854
855 if (Context->IsKernelCollection) {
856 //
857 // For newer variant of the prelinkedkernel plist we need to adapt it
858 // in both inner and outer images.
859 //
860 Context->InnerInfoSegment->VirtualAddress = Context->PrelinkedLastAddress;
861 Context->InnerInfoSegment->Size = MACHO_ALIGN (ExportedInfoSize);
862 Context->InnerInfoSegment->FileOffset = Context->PrelinkedSize;
863 Context->InnerInfoSegment->FileSize = MACHO_ALIGN (ExportedInfoSize);
864 Context->InnerInfoSection->Address = Context->PrelinkedLastAddress;
865 Context->InnerInfoSection->Size = MACHO_ALIGN (ExportedInfoSize);
866 Context->InnerInfoSection->Offset = Context->PrelinkedSize;
867 }
868
869 CopyMem (
870 &Context->Prelinked[Context->PrelinkedSize],
871 ExportedInfo,
872 ExportedInfoSize
873 );
874
875 ZeroMem (
876 &Context->Prelinked[Context->PrelinkedSize + ExportedInfoSize],
877 MACHO_ALIGN (ExportedInfoSize) - ExportedInfoSize
878 );
879
880 Context->PrelinkedLastAddress += MACHO_ALIGN (ExportedInfoSize);
881 Context->PrelinkedSize += MACHO_ALIGN (ExportedInfoSize);
882
883 Context->PrelinkedMachContext.FileSize = Context->PrelinkedSize;
884
885 if (Context->IsKernelCollection) {
886 //
887 // After rebuilding the KC we may have moved the __LINKEDIT command.
888 // This happens when __REGION kexts are squashed.
889 // Obtain its address again.
890 //
891 Context->LinkEditSegment = MachoGetSegmentByName (
892 &Context->PrelinkedMachContext,
894 );
895 }
896
897 FreePool (ExportedInfo);
898
899 return EFI_SUCCESS;
900}
901
902EFI_STATUS
904 IN OUT UINT32 *ReservedInfoSize,
905 IN OUT UINT32 *ReservedExeSize,
906 IN UINT32 InfoPlistSize,
907 IN UINT8 *Executable OPTIONAL,
908 IN UINT32 ExecutableSize OPTIONAL,
909 IN BOOLEAN Is32Bit
910 )
911{
912 OC_MACHO_CONTEXT Context;
913
914 //
915 // For new fields.
916 //
917 if (BaseOverflowAddU32 (InfoPlistSize, PLIST_EXPANSION_SIZE, &InfoPlistSize)) {
918 return EFI_INVALID_PARAMETER;
919 }
920
921 InfoPlistSize = MACHO_ALIGN (InfoPlistSize);
922
923 if (Executable != NULL) {
924 ASSERT (ExecutableSize > 0);
925 if (!MachoInitializeContext (&Context, Executable, ExecutableSize, 0, ExecutableSize, Is32Bit)) {
926 return EFI_INVALID_PARAMETER;
927 }
928
929 ExecutableSize = MachoGetExpandedImageSize (&Context);
930 if (ExecutableSize == 0) {
931 return EFI_INVALID_PARAMETER;
932 }
933 }
934
935 if ( BaseOverflowAddU32 (*ReservedInfoSize, InfoPlistSize, &InfoPlistSize)
936 || BaseOverflowAddU32 (*ReservedExeSize, ExecutableSize, &ExecutableSize))
937 {
938 return EFI_INVALID_PARAMETER;
939 }
940
941 *ReservedInfoSize = InfoPlistSize;
942 *ReservedExeSize = ExecutableSize;
943
944 return EFI_SUCCESS;
945}
946
947EFI_STATUS
949 IN OUT PRELINKED_CONTEXT *Context,
950 IN CONST CHAR8 *Identifier OPTIONAL,
951 IN CONST CHAR8 *BundlePath,
952 IN CONST CHAR8 *InfoPlist,
953 IN UINT32 InfoPlistSize,
954 IN CONST CHAR8 *ExecutablePath OPTIONAL,
955 IN CONST UINT8 *Executable OPTIONAL,
956 IN UINT32 ExecutableSize OPTIONAL,
957 OUT CHAR8 BundleVersion[MAX_INFO_BUNDLE_VERSION_KEY_SIZE] OPTIONAL
958 )
959{
960 EFI_STATUS Status;
961 BOOLEAN Result;
962
963 XML_DOCUMENT *InfoPlistDocument;
964 XML_NODE *InfoPlistRoot;
965 XML_NODE *KextPlistValue;
966 CHAR8 *TmpInfoPlist;
967 CHAR8 *NewInfoPlist;
968 OC_MACHO_CONTEXT ExecutableContext;
969 CONST CHAR8 *TmpKeyValue;
970 UINT32 FieldCount;
971 UINT32 FieldIndex;
972 UINT32 NewInfoPlistSize;
973 UINT32 NewPrelinkedSize;
974 UINT32 AlignedExecutableSize;
975 BOOLEAN Failed;
976 UINT64 KmodAddress;
977 PRELINKED_KEXT *PrelinkedKext;
978 CHAR8 ExecutableSourceAddrStr[24];
979 CHAR8 ExecutableSizeStr[24];
980 CHAR8 ExecutableLoadAddrStr[24];
981 CHAR8 KmodInfoStr[24];
982 UINT32 KextOffset;
983 UINT64 FileOffset;
984 UINT64 LoadAddressOffset;
985 CONST CHAR8 *BundleVerStr;
986
987 PrelinkedKext = NULL;
988
989 ASSERT (Context != NULL);
990 ASSERT (BundlePath != NULL);
991 ASSERT (InfoPlist != NULL);
992 ASSERT (InfoPlistSize > 0);
993
994 KmodAddress = 0;
995 AlignedExecutableSize = 0;
996 KextOffset = 0;
997
998 //
999 // If an identifier was passed, ensure it does not already exist.
1000 //
1001 if (Identifier != NULL) {
1002 if (InternalCachedPrelinkedKext (Context, Identifier) != NULL) {
1003 DEBUG ((DEBUG_INFO, "OCAK: Bundle %a is already present in prelinked\n", Identifier));
1004 return EFI_ALREADY_STARTED;
1005 }
1006 }
1007
1008 //
1009 // Copy executable to prelinkedkernel.
1010 //
1011 if (Executable != NULL) {
1012 ASSERT (ExecutableSize > 0);
1013 if (!MachoInitializeContext (&ExecutableContext, (UINT8 *)Executable, ExecutableSize, 0, ExecutableSize, Context->Is32Bit)) {
1014 DEBUG ((DEBUG_INFO, "OCAK: Injected kext %a/%a is not a supported executable\n", BundlePath, ExecutablePath));
1015 return EFI_INVALID_PARAMETER;
1016 }
1017
1018 //
1019 // Append the KEXT to the current prelinked end.
1020 //
1021 KextOffset = Context->PrelinkedSize;
1022
1023 ExecutableSize = MachoExpandImage (
1024 &ExecutableContext,
1025 &Context->Prelinked[KextOffset],
1026 Context->PrelinkedAllocSize - KextOffset,
1027 TRUE,
1028 &FileOffset
1029 );
1030
1031 AlignedExecutableSize = MACHO_ALIGN (ExecutableSize);
1032
1033 if ( BaseOverflowAddU32 (KextOffset, AlignedExecutableSize, &NewPrelinkedSize)
1034 || (NewPrelinkedSize > Context->PrelinkedAllocSize)
1035 || (ExecutableSize == 0))
1036 {
1037 return EFI_BUFFER_TOO_SMALL;
1038 }
1039
1040 ZeroMem (
1041 &Context->Prelinked[KextOffset + ExecutableSize],
1042 AlignedExecutableSize - ExecutableSize
1043 );
1044
1045 if ( !MachoInitializeContext (&ExecutableContext, &Context->Prelinked[KextOffset], ExecutableSize, 0, ExecutableSize, Context->Is32Bit)
1046 || BaseOverflowAddU64 (Context->PrelinkedLastLoadAddress, FileOffset, &LoadAddressOffset))
1047 {
1048 return EFI_INVALID_PARAMETER;
1049 }
1050
1051 Result = KextFindKmodAddress (&ExecutableContext, LoadAddressOffset, ExecutableSize, &KmodAddress);
1052 if (!Result) {
1053 return EFI_INVALID_PARAMETER;
1054 }
1055 }
1056
1057 //
1058 // Allocate Info.plist copy for XML_DOCUMENT.
1059 //
1060 TmpInfoPlist = AllocateCopyPool (InfoPlistSize, InfoPlist);
1061 if (TmpInfoPlist == NULL) {
1062 return EFI_OUT_OF_RESOURCES;
1063 }
1064
1065 InfoPlistDocument = XmlDocumentParse (TmpInfoPlist, InfoPlistSize, FALSE);
1066 if (InfoPlistDocument == NULL) {
1067 FreePool (TmpInfoPlist);
1068 return EFI_INVALID_PARAMETER;
1069 }
1070
1071 InfoPlistRoot = PlistNodeCast (PlistDocumentRoot (InfoPlistDocument), PLIST_NODE_TYPE_DICT);
1072 if (InfoPlistRoot == NULL) {
1073 XmlDocumentFree (InfoPlistDocument);
1074 FreePool (TmpInfoPlist);
1075 return EFI_INVALID_PARAMETER;
1076 }
1077
1078 //
1079 // We are not supposed to check for this, it is XNU responsibility, which reliably panics.
1080 // However, to avoid certain users making this kind of mistake, we still provide some
1081 // code in debug mode to diagnose it.
1082 //
1083 DEBUG_CODE_BEGIN ();
1084 FieldCount = PlistDictChildren (InfoPlistRoot);
1085
1086 if (BundleVersion != NULL) {
1087 for (FieldIndex = 0; FieldIndex < FieldCount; ++FieldIndex) {
1088 TmpKeyValue = PlistKeyValue (PlistDictChild (InfoPlistRoot, FieldIndex, &KextPlistValue));
1089 if (TmpKeyValue == NULL) {
1090 continue;
1091 }
1092
1093 //
1094 // Match CFBundleVersion.
1095 //
1096 if (AsciiStrCmp (TmpKeyValue, INFO_BUNDLE_VERSION_KEY) == 0) {
1097 if (PlistNodeCast (KextPlistValue, PLIST_NODE_TYPE_STRING) == NULL) {
1098 break;
1099 }
1100
1101 BundleVerStr = XmlNodeContent (KextPlistValue);
1102 AsciiStrCpyS (BundleVersion, MAX_INFO_BUNDLE_VERSION_KEY_SIZE, BundleVerStr);
1103 break;
1104 }
1105 }
1106 }
1107
1108 if (Executable == NULL) {
1109 for (FieldIndex = 0; FieldIndex < FieldCount; ++FieldIndex) {
1110 TmpKeyValue = PlistKeyValue (PlistDictChild (InfoPlistRoot, FieldIndex, NULL));
1111 if (TmpKeyValue == NULL) {
1112 continue;
1113 }
1114
1115 if (AsciiStrCmp (TmpKeyValue, INFO_BUNDLE_EXECUTABLE_KEY) == 0) {
1116 DEBUG ((DEBUG_ERROR, "OCAK: Plist-only kext has %a key\n", INFO_BUNDLE_EXECUTABLE_KEY));
1117 ASSERT (FALSE);
1118 CpuDeadLoop ();
1119 }
1120 }
1121 }
1122
1123 DEBUG_CODE_END ();
1124
1125 Failed = FALSE;
1126 Failed |= XmlNodeAppend (InfoPlistRoot, "key", NULL, PRELINK_INFO_BUNDLE_PATH_KEY) == NULL;
1127 Failed |= XmlNodeAppend (InfoPlistRoot, "string", NULL, BundlePath) == NULL;
1128 if (Executable != NULL) {
1129 Failed |= XmlNodeAppend (InfoPlistRoot, "key", NULL, PRELINK_INFO_EXECUTABLE_RELATIVE_PATH_KEY) == NULL;
1130 Failed |= XmlNodeAppend (InfoPlistRoot, "string", NULL, ExecutablePath) == NULL;
1131 Failed |= !AsciiUint64ToLowerHex (ExecutableSourceAddrStr, sizeof (ExecutableSourceAddrStr), Context->PrelinkedLastAddress);
1132 Failed |= XmlNodeAppend (InfoPlistRoot, "key", NULL, PRELINK_INFO_EXECUTABLE_SOURCE_ADDR_KEY) == NULL;
1133 Failed |= XmlNodeAppend (InfoPlistRoot, "integer", PRELINK_INFO_INTEGER_ATTRIBUTES, ExecutableSourceAddrStr) == NULL;
1134 Failed |= !AsciiUint64ToLowerHex (ExecutableLoadAddrStr, sizeof (ExecutableLoadAddrStr), Context->PrelinkedLastLoadAddress);
1135 Failed |= XmlNodeAppend (InfoPlistRoot, "key", NULL, PRELINK_INFO_EXECUTABLE_LOAD_ADDR_KEY) == NULL;
1136 Failed |= XmlNodeAppend (InfoPlistRoot, "integer", PRELINK_INFO_INTEGER_ATTRIBUTES, ExecutableLoadAddrStr) == NULL;
1137 Failed |= !AsciiUint64ToLowerHex (ExecutableSizeStr, sizeof (ExecutableSizeStr), AlignedExecutableSize);
1138 Failed |= XmlNodeAppend (InfoPlistRoot, "key", NULL, PRELINK_INFO_EXECUTABLE_SIZE_KEY) == NULL;
1139 Failed |= XmlNodeAppend (InfoPlistRoot, "integer", PRELINK_INFO_INTEGER_ATTRIBUTES, ExecutableSizeStr) == NULL;
1140 Failed |= !AsciiUint64ToLowerHex (KmodInfoStr, sizeof (KmodInfoStr), KmodAddress);
1141 Failed |= XmlNodeAppend (InfoPlistRoot, "key", NULL, PRELINK_INFO_KMOD_INFO_KEY) == NULL;
1142 Failed |= XmlNodeAppend (InfoPlistRoot, "integer", PRELINK_INFO_INTEGER_ATTRIBUTES, KmodInfoStr) == NULL;
1143 }
1144
1145 if (Failed) {
1146 XmlDocumentFree (InfoPlistDocument);
1147 FreePool (TmpInfoPlist);
1148 return EFI_OUT_OF_RESOURCES;
1149 }
1150
1151 if (Executable != NULL) {
1152 PrelinkedKext = InternalLinkPrelinkedKext (
1153 Context,
1154 &ExecutableContext,
1155 InfoPlistRoot,
1156 Context->PrelinkedLastLoadAddress,
1157 KmodAddress,
1158 FileOffset
1159 );
1160
1161 if (PrelinkedKext == NULL) {
1162 XmlDocumentFree (InfoPlistDocument);
1163 FreePool (TmpInfoPlist);
1164 return EFI_INVALID_PARAMETER;
1165 }
1166
1167 //
1168 // XNU assumes that load size and source size are same, so we should append
1169 // whatever is bigger to all sizes.
1170 //
1171 Context->PrelinkedSize += AlignedExecutableSize;
1172 Context->PrelinkedLastAddress += AlignedExecutableSize;
1173 Context->PrelinkedLastLoadAddress += AlignedExecutableSize;
1174
1175 if (Context->IsKernelCollection) {
1176 //
1177 // For KC, our KEXTs have their own segment - do not mod __PRELINK_INFO.
1178 // Integrate the KEXT into KC by indexing its fixups and rebasing.
1179 // Note, we are no longer using ExecutableContext here, as the context
1180 // ownership was transferred by InternalLinkPrelinkedKext.
1181 //
1182 KcKextIndexFixups (Context, &PrelinkedKext->Context.MachContext);
1183 Status = KcKextApplyFileDelta (Context, &PrelinkedKext->Context.MachContext, KextOffset);
1184 if (EFI_ERROR (Status)) {
1185 DEBUG ((
1186 DEBUG_WARN,
1187 "Failed to rebase injected kext %a/%a by %u\n",
1188 BundlePath,
1189 ExecutablePath,
1190 KextOffset
1191 ));
1192 return Status;
1193 }
1194 } else {
1195 //
1196 // For legacy prelinkedkernel we append to __PRELINK_TEXT.
1197 //
1198 if (Context->Is32Bit) {
1199 Context->PrelinkedTextSegment->Segment32.Size += AlignedExecutableSize;
1200 Context->PrelinkedTextSegment->Segment32.FileSize += AlignedExecutableSize;
1201 Context->PrelinkedTextSection->Section32.Size += AlignedExecutableSize;
1202 } else {
1203 Context->PrelinkedTextSegment->Segment64.Size += AlignedExecutableSize;
1204 Context->PrelinkedTextSegment->Segment64.FileSize += AlignedExecutableSize;
1205 Context->PrelinkedTextSection->Section64.Size += AlignedExecutableSize;
1206 }
1207 }
1208 }
1209
1210 //
1211 // Strip outer plist & dict.
1212 //
1213 NewInfoPlist = XmlDocumentExport (InfoPlistDocument, &NewInfoPlistSize, 2, FALSE);
1214
1215 XmlDocumentFree (InfoPlistDocument);
1216 FreePool (TmpInfoPlist);
1217
1218 if (NewInfoPlist == NULL) {
1219 if (PrelinkedKext != NULL) {
1220 InternalFreePrelinkedKext (PrelinkedKext);
1221 }
1222
1223 return EFI_OUT_OF_RESOURCES;
1224 }
1225
1226 Status = PrelinkedDependencyInsert (Context, NewInfoPlist);
1227 if (EFI_ERROR (Status)) {
1228 FreePool (NewInfoPlist);
1229 if (PrelinkedKext != NULL) {
1230 InternalFreePrelinkedKext (PrelinkedKext);
1231 }
1232
1233 return Status;
1234 }
1235
1236 if (XmlNodeAppend (Context->KextList, "dict", NULL, NewInfoPlist) == NULL) {
1237 if (PrelinkedKext != NULL) {
1238 InternalFreePrelinkedKext (PrelinkedKext);
1239 }
1240
1241 return EFI_OUT_OF_RESOURCES;
1242 }
1243
1244 //
1245 // Let other kexts depend on this one.
1246 //
1247 if (PrelinkedKext != NULL) {
1248 InsertTailList (&Context->PrelinkedKexts, &PrelinkedKext->Link);
1249 //
1250 // Additionally register this kext in the injected list, as this is required
1251 // for KernelCollection support.
1252 //
1253 InsertTailList (&Context->InjectedKexts, &PrelinkedKext->InjectedLink);
1254 }
1255
1256 return EFI_SUCCESS;
1257}
1258
1259EFI_STATUS
1261 IN OUT PRELINKED_CONTEXT *Context,
1262 IN CONST CHAR8 *Identifier,
1263 IN PATCHER_GENERIC_PATCH *Patch
1264 )
1265{
1266 EFI_STATUS Status;
1267 PATCHER_CONTEXT Patcher;
1268
1269 ASSERT (Context != NULL);
1270 ASSERT (Identifier != NULL);
1271 ASSERT (Patch != NULL);
1272
1273 Status = PatcherInitContextFromPrelinked (&Patcher, Context, Identifier);
1274 if (EFI_ERROR (Status)) {
1275 DEBUG ((DEBUG_INFO, "OCAK: Failed to pk find %a - %r\n", Identifier, Status));
1276 return Status;
1277 }
1278
1279 return PatcherApplyGenericPatch (&Patcher, Patch);
1280}
1281
1282EFI_STATUS
1284 IN OUT PRELINKED_CONTEXT *Context,
1285 IN KERNEL_QUIRK_NAME Quirk,
1286 IN UINT32 KernelVersion
1287 )
1288{
1289 EFI_STATUS Status;
1290 KERNEL_QUIRK *KernelQuirk;
1291 PATCHER_CONTEXT Patcher;
1292
1293 ASSERT (Context != NULL);
1294
1295 KernelQuirk = &gKernelQuirks[Quirk];
1296 ASSERT (KernelQuirk->Identifier != NULL);
1297
1298 Status = PatcherInitContextFromPrelinked (&Patcher, Context, KernelQuirk->Identifier);
1299 if (!EFI_ERROR (Status)) {
1300 return KernelQuirk->PatchFunction (&Patcher, KernelVersion);
1301 }
1302
1303 //
1304 // It is up to the function to decide whether this is critical or not.
1305 //
1306 DEBUG ((DEBUG_INFO, "OCAK: Failed to pk find %a - %r\n", KernelQuirk->Identifier, Status));
1307 return KernelQuirk->PatchFunction (NULL, KernelVersion);
1308}
1309
1310EFI_STATUS
1312 IN OUT PRELINKED_CONTEXT *Context,
1313 IN CONST CHAR8 *Identifier,
1314 IN BOOLEAN Exclude
1315 )
1316{
1317 EFI_STATUS Status;
1318 PATCHER_CONTEXT Patcher;
1319
1320 ASSERT (Context != NULL);
1321 ASSERT (Identifier != NULL);
1322
1323 Status = PatcherInitContextFromPrelinked (&Patcher, Context, Identifier);
1324 if (EFI_ERROR (Status)) {
1325 DEBUG ((DEBUG_INFO, "OCAK: Failed to pk find %a - %r\n", Identifier, Status));
1326 return Status;
1327 }
1328
1329 return Exclude ? PatcherExcludePrelinkedKext (Identifier, &Patcher, Context) : PatcherBlockKext (&Patcher);
1330}
VENDOR_DEVICE_PATH Header
@ MachHeaderFileTypeFileSet
KERNEL_QUIRK gKernelQuirks[]
STATIC UINT32 KernelVersion
Definition KextInject.c:28
EFI_STATUS InternalKxldStateRebuild(IN OUT PRELINKED_CONTEXT *Context)
Definition KxldState.c:459
#define PRELINK_INFO_SECTION
EFI_STATUS KcRebuildMachHeader(IN OUT PRELINKED_CONTEXT *Context)
EFI_STATUS PatcherBlockKext(IN OUT PATCHER_CONTEXT *Context)
#define PRELINK_INFO_DICTIONARY_KEY
EFI_STATUS PatcherExcludePrelinkedKext(IN CONST CHAR8 *Identifier, IN OUT PATCHER_CONTEXT *PatcherContext, IN OUT PRELINKED_CONTEXT *PrelinkedContext)
#define PRELINK_INFO_EXECUTABLE_LOAD_ADDR_KEY
#define PRELINK_TEXT_SECTION
#define PRELINK_INFO_BUNDLE_PATH_KEY
#define MAX_INFO_BUNDLE_VERSION_KEY_SIZE
#define PRELINK_INFO_INTEGER_ATTRIBUTES
VOID KcKextIndexFixups(IN OUT PRELINKED_CONTEXT *Context, IN OC_MACHO_CONTEXT *MachContext)
#define PRELINK_INFO_EXECUTABLE_RELATIVE_PATH_KEY
#define INFO_BUNDLE_EXECUTABLE_KEY
#define KC_LINKEDIT_SEGMENT
#define PRELINK_INFO_KMOD_INFO_KEY
#define PRELINK_INFO_EXECUTABLE_SOURCE_ADDR_KEY
EFI_STATUS PatcherApplyGenericPatch(IN OUT PATCHER_CONTEXT *Context, IN PATCHER_GENERIC_PATCH *Patch)
#define PLIST_EXPANSION_SIZE
EFI_STATUS KcInitKextFixupChains(IN OUT PRELINKED_CONTEXT *Context, IN UINT32 SegChainSize, IN UINT32 ReservedSize)
#define PRELINK_INFO_EXECUTABLE_SIZE_KEY
#define INFO_BUNDLE_VERSION_KEY
#define PRELINK_TEXT_SEGMENT
#define PRELINK_INFO_SEGMENT
EFI_STATUS KcKextApplyFileDelta(IN PRELINKED_CONTEXT *PrelinkedContext, IN OUT OC_MACHO_CONTEXT *Context, IN UINT32 Delta)
KERNEL_QUIRK_NAME
EFI_STATUS PatcherInitContextFromPrelinked(IN OUT PATCHER_CONTEXT *Context, IN OUT PRELINKED_CONTEXT *Prelinked, IN CONST CHAR8 *Name)
Definition KextPatcher.c:99
#define KC_REGION0_SEGMENT
BOOLEAN KextFindKmodAddress(IN OC_MACHO_CONTEXT *ExecutableContext, IN UINT64 LoadAddress, IN UINT32 Size, OUT UINT64 *Kmod)
UINT32 KcGetSegmentFixupChainsSize(IN UINT32 SegmentSize)
STATIC_ASSERT(BYTES_PER_PIXEL==sizeof(UINT32), "Non 4-byte pixels are unsupported!")
BOOLEAN MachoInitializeContext64(OUT OC_MACHO_CONTEXT *Context, IN VOID *FileData, IN UINT32 FileSize, IN UINT32 HeaderOffset, IN UINT32 InnerSize)
MACH_SECTION_ANY * MachoGetSectionByName(IN OUT OC_MACHO_CONTEXT *Context, IN MACH_SEGMENT_COMMAND_ANY *Segment, IN CONST CHAR8 *SectionName)
Definition Header.c:214
UINT64 MachoGetLastAddress(IN OUT OC_MACHO_CONTEXT *Context)
Definition Header.c:99
BOOLEAN MachoInitialiseSymtabsExternal(IN OUT OC_MACHO_CONTEXT *Context, IN OC_MACHO_CONTEXT *SymsContext)
Definition Header.c:423
UINT32 MachoExpandImage(IN OC_MACHO_CONTEXT *Context, OUT UINT8 *Destination, IN UINT32 DestinationSize, IN BOOLEAN Strip, OUT UINT64 *FileOffset OPTIONAL)
Definition Header.c:645
MACH_HEADER_ANY * MachoGetMachHeader(IN OUT OC_MACHO_CONTEXT *Context)
Definition Header.c:44
BOOLEAN MachoInitializeContext(OUT OC_MACHO_CONTEXT *Context, IN VOID *FileData, IN UINT32 FileSize, IN UINT32 HeaderOffset, IN UINT32 InnerSize, IN BOOLEAN Is32Bit)
Definition Header.c:29
UINT32 MachoGetExpandedImageSize(IN OC_MACHO_CONTEXT *Context)
Definition Header.c:661
MACH_SEGMENT_COMMAND_64 * MachoGetSegmentByName64(IN OUT OC_MACHO_CONTEXT *Context, IN CONST CHAR8 *SegmentName)
#define MACHO_ALIGN(x)
Definition OcMachoLib.h:28
#define MACHO_PAGE_SIZE
Definition OcMachoLib.h:23
MACH_SEGMENT_COMMAND_ANY * MachoGetSegmentByName(IN OUT OC_MACHO_CONTEXT *Context, IN CONST CHAR8 *SegmentName)
Definition Header.c:201
BOOLEAN AsciiUint64ToLowerHex(OUT CHAR8 *Buffer, IN UINT32 BufferSize, IN UINT64 Value)
Definition OcAsciiLib.c:150
OC_TYPING_BUFFER_ENTRY Buffer[OC_TYPING_BUFFER_SIZE]
Definition OcTypingLib.h:42
XML_NODE * XmlDocumentRoot(IN CONST XML_DOCUMENT *Document)
Definition OcXmlLib.c:1447
@ PLIST_NODE_TYPE_DICT
Definition OcXmlLib.h:91
@ PLIST_NODE_TYPE_STRING
Definition OcXmlLib.h:93
@ PLIST_NODE_TYPE_ARRAY
Definition OcXmlLib.h:90
XML_DOCUMENT * XmlDocumentParse(IN OUT CHAR8 *Buffer, IN UINT32 Length, IN BOOLEAN WithRefs)
Definition OcXmlLib.c:1308
CONST CHAR8 * PlistKeyValue(IN XML_NODE *Node OPTIONAL)
Definition OcXmlLib.c:1841
UINT32 XmlNodeChildren(IN CONST XML_NODE *Node)
Definition OcXmlLib.c:1493
CHAR8 * XmlDocumentExport(IN CONST XML_DOCUMENT *Document, OUT UINT32 *Length OPTIONAL, IN UINT32 Skip, IN BOOLEAN PrependPlistInfo)
Definition OcXmlLib.c:1367
CONST CHAR8 * XmlNodeContent(IN CONST XML_NODE *Node)
Definition OcXmlLib.c:1467
UINT32 PlistDictChildren(IN CONST XML_NODE *Node)
Definition OcXmlLib.c:1813
BOOLEAN PlistIntegerValue(IN XML_NODE *Node OPTIONAL, OUT VOID *Value, IN UINT32 Size, IN BOOLEAN Hex)
Definition OcXmlLib.c:1943
XML_NODE * PlistNodeCast(IN XML_NODE *Node OPTIONAL, IN PLIST_NODE_TYPE Type)
Definition OcXmlLib.c:1760
XML_NODE * XmlNodeAppend(IN OUT XML_NODE *Node, IN CONST CHAR8 *Name, IN CONST CHAR8 *Attributes OPTIONAL, IN CONST CHAR8 *Content OPTIONAL)
Definition OcXmlLib.c:1581
XML_NODE * PlistDocumentRoot(IN CONST XML_DOCUMENT *Document)
Definition OcXmlLib.c:1736
XML_NODE * XmlNodeChild(IN CONST XML_NODE *Node, IN UINT32 Child)
Definition OcXmlLib.c:1503
VOID XmlDocumentFree(IN OUT XML_DOCUMENT *Document)
Definition OcXmlLib.c:1435
XML_NODE * PlistDictChild(IN CONST XML_NODE *Node, IN UINT32 Child, OUT XML_NODE **Value OPTIONAL)
Definition OcXmlLib.c:1823
EFI_STATUS PrelinkedContextApplyPatch(IN OUT PRELINKED_CONTEXT *Context, IN CONST CHAR8 *Identifier, IN PATCHER_GENERIC_PATCH *Patch)
VOID PrelinkedContextFree(IN OUT PRELINKED_CONTEXT *Context)
EFI_STATUS PrelinkedDependencyInsert(IN OUT PRELINKED_CONTEXT *Context, IN VOID *Buffer)
STATIC EFI_STATUS PrelinkedGetSegmentsFromMacho(IN OC_MACHO_CONTEXT *MachoContext, OUT MACH_SEGMENT_COMMAND_ANY **PrelinkedInfoSegment, OUT MACH_SECTION_ANY **PrelinkedInfoSection)
EFI_STATUS PrelinkedContextInit(IN OUT PRELINKED_CONTEXT *Context, IN OUT UINT8 *Prelinked, IN UINT32 PrelinkedSize, IN UINT32 PrelinkedAllocSize, IN BOOLEAN Is32Bit)
EFI_STATUS PrelinkedInjectPrepare(IN OUT PRELINKED_CONTEXT *Context, IN UINT32 LinkedExpansion, IN UINT32 ReservedExeSize)
EFI_STATUS PrelinkedContextBlock(IN OUT PRELINKED_CONTEXT *Context, IN CONST CHAR8 *Identifier, IN BOOLEAN Exclude)
EFI_STATUS PrelinkedContextApplyQuirk(IN OUT PRELINKED_CONTEXT *Context, IN KERNEL_QUIRK_NAME Quirk, IN UINT32 KernelVersion)
EFI_STATUS PrelinkedReserveKextSize(IN OUT UINT32 *ReservedInfoSize, IN OUT UINT32 *ReservedExeSize, IN UINT32 InfoPlistSize, IN UINT8 *Executable OPTIONAL, IN UINT32 ExecutableSize OPTIONAL, IN BOOLEAN Is32Bit)
EFI_STATUS InternalConnectExternalSymtab(IN OUT OC_MACHO_CONTEXT *Context, OUT OC_MACHO_CONTEXT *InnerContext, IN UINT8 *Buffer, IN UINT32 BufferSize, OUT BOOLEAN *KernelCollection OPTIONAL)
EFI_STATUS PrelinkedInjectKext(IN OUT PRELINKED_CONTEXT *Context, IN CONST CHAR8 *Identifier OPTIONAL, IN CONST CHAR8 *BundlePath, IN CONST CHAR8 *InfoPlist, IN UINT32 InfoPlistSize, IN CONST CHAR8 *ExecutablePath OPTIONAL, IN CONST UINT8 *Executable OPTIONAL, IN UINT32 ExecutableSize OPTIONAL, OUT CHAR8 BundleVersion[MAX_INFO_BUNDLE_VERSION_KEY_SIZE] OPTIONAL)
EFI_STATUS PrelinkedInjectComplete(IN OUT PRELINKED_CONTEXT *Context)
STATIC UINT64 PrelinkedFindLastLoadAddress(IN XML_NODE *KextList)
VOID InternalFreePrelinkedKext(IN PRELINKED_KEXT *Kext)
PRELINKED_KEXT * InternalCachedPrelinkedKext(IN OUT PRELINKED_CONTEXT *Prelinked, IN CONST CHAR8 *Identifier)
#define GET_PRELINKED_KEXT_FROM_LINK(This)
PRELINKED_KEXT * InternalCachedPrelinkedKernel(IN OUT PRELINKED_CONTEXT *Prelinked)
PRELINKED_KEXT * InternalLinkPrelinkedKext(IN OUT PRELINKED_CONTEXT *Context, IN OUT OC_MACHO_CONTEXT *Executable, IN XML_NODE *PlistRoot, IN UINT64 LoadAddress, IN UINT64 KmodAddress, IN UINT64 FileOffset)
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
#define ASSERT(x)
Definition coder.h:55
KERNEL_QUIRK_PATCH_FUNCTION * PatchFunction
CONST CHAR8 * Identifier
UINT64 FileOffset
file offset of this segment
UINT64 VirtualAddress
memory address of this segment
OC_MACHO_CONTEXT MachContext
LIST_ENTRY InjectedLink
PATCHER_CONTEXT Context