2 #ifndef KERNEL_FIXUPS_H
3 #define KERNEL_FIXUPS_H
5 #include <mach-o/fixup-chains.h>
6 #include <mach-o/loader.h>
8 typedef int (*FixupsLogFunc
)(const char*, ...);
9 static const int LogFixups
= 0;
11 // We may not have strcmp, so make our own
13 __attribute__((section(("__HIB, __text"))))
15 __attribute__((section(("__TEXT_EXEC, __text"))))
17 static int areEqual(const char* a
, const char* b
) {
27 #pragma clang diagnostic push
28 #pragma clang diagnostic ignored "-Wmissing-declarations"
29 union ChainedFixupPointerOnDisk
32 struct dyld_chained_ptr_64_kernel_cache_rebase fixup64
;
34 #pragma clang diagnostic pop
36 // Temporary until we have <rdar://problem/57025372>
38 __attribute__((section(("__HIB, __text"))))
40 __attribute__((section(("__TEXT_EXEC, __text"))))
42 static uint64_t signPointer(struct dyld_chained_ptr_64_kernel_cache_rebase pointer
,
46 #if __has_feature(ptrauth_calls)
47 uint64_t discriminator
= pointer
.diversity
;
48 if ( pointer
.addrDiv
) {
49 if ( discriminator
!= 0 ) {
50 discriminator
= __builtin_ptrauth_blend_discriminator(loc
, discriminator
);
52 discriminator
= (uint64_t)(uintptr_t)loc
;
55 switch ( pointer
.key
) {
57 return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target
, 0, discriminator
);
59 return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target
, 1, discriminator
);
61 return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target
, 2, discriminator
);
63 return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target
, 3, discriminator
);
69 // Temporary until we have <rdar://problem/57025372>
71 __attribute__((section(("__HIB, __text"))))
73 __attribute__((section(("__TEXT_EXEC, __text"))))
75 void fixupValue(union ChainedFixupPointerOnDisk
* fixupLoc
,
76 const struct dyld_chained_starts_in_segment
* segInfo
,
78 const void* basePointers
[4],
80 FixupsLogFunc logFunc
) {
82 logFunc("[LOG] kernel-fixups: fixupValue %p\n", fixupLoc
);
84 switch (segInfo
->pointer_format
) {
86 case DYLD_CHAINED_PTR_64_KERNEL_CACHE
:
87 case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE
: {
88 const void* baseAddress
= basePointers
[fixupLoc
->fixup64
.cacheLevel
];
89 if ( baseAddress
== 0 ) {
90 logFunc("Invalid cache level: %d\n", fixupLoc
->fixup64
.cacheLevel
);
94 uintptr_t slidValue
= (uintptr_t)baseAddress
+ fixupLoc
->fixup64
.target
;
96 logFunc("[LOG] kernel-fixups: slidValue %p = %p + %p\n", (void*)slidValue
, (void*)baseAddress
, (void*)fixupLoc
->fixup64
.target
);
98 #if __has_feature(ptrauth_calls)
99 if ( fixupLoc
->fixup64
.isAuth
) {
100 slidValue
= signPointer(fixupLoc
->fixup64
, fixupLoc
, slidValue
);
103 if ( fixupLoc
->fixup64
.isAuth
) {
104 logFunc("Unexpected authenticated fixup\n");
108 #endif // __has_feature(ptrauth_calls)
109 fixupLoc
->raw64
= slidValue
;
114 logFunc("unsupported pointer chain format: 0x%04X", segInfo
->pointer_format
);
120 // Temporary until we have <rdar://problem/57025372>
122 __attribute__((section(("__HIB, __text"))))
124 __attribute__((section(("__TEXT_EXEC, __text"))))
126 int walkChain(const struct mach_header
* mh
,
127 const struct dyld_chained_starts_in_segment
* segInfo
,
129 uint16_t offsetInPage
,
131 const void* basePointers
[4],
132 FixupsLogFunc logFunc
)
135 logFunc("[LOG] kernel-fixups: walkChain page[%d]\n", pageIndex
);
138 uint8_t* pageContentStart
= (uint8_t*)mh
+ segInfo
->segment_offset
+ (pageIndex
* segInfo
->page_size
);
139 union ChainedFixupPointerOnDisk
* chain
= (union ChainedFixupPointerOnDisk
*)(pageContentStart
+offsetInPage
);
141 while (!stop
&& !chainEnd
) {
142 // copy chain content, in case handler modifies location to final value
143 union ChainedFixupPointerOnDisk chainContent
= *chain
;
144 fixupValue(chain
, segInfo
, slide
, basePointers
, &stop
, logFunc
);
146 switch (segInfo
->pointer_format
) {
148 case DYLD_CHAINED_PTR_64_KERNEL_CACHE
:
149 if ( chainContent
.fixup64
.next
== 0 )
152 chain
= (union ChainedFixupPointerOnDisk
*)((uint8_t*)chain
+ chainContent
.fixup64
.next
*4);
154 case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE
:
155 if ( chainContent
.fixup64
.next
== 0 )
158 chain
= (union ChainedFixupPointerOnDisk
*)((uint8_t*)chain
+ chainContent
.fixup64
.next
);
162 logFunc("unknown pointer format 0x%04X", segInfo
->pointer_format
);
170 // Temporary until we have <rdar://problem/57025372>
172 __attribute__((section(("__HIB, __text"))))
174 __attribute__((section(("__TEXT_EXEC, __text"))))
176 int slide(const struct mach_header
* mh
, const void* basePointers
[4], FixupsLogFunc logFunc
) {
177 // First find the slide and chained fixups load command
178 uint64_t textVMAddr
= 0;
179 const struct linkedit_data_command
* chainedFixups
= 0;
180 uint64_t linkeditVMAddr
= 0;
181 uint64_t linkeditFileOffset
= 0;
184 logFunc("[LOG] kernel-fixups: mh %p\n", mh
);
188 logFunc("[LOG] kernel-fixups: parsing load commands\n");
191 const struct load_command
* startCmds
= 0;
192 if ( mh
->magic
== MH_MAGIC_64
)
193 startCmds
= (struct load_command
*)((char *)mh
+ sizeof(struct mach_header_64
));
194 else if ( mh
->magic
== MH_MAGIC
)
195 startCmds
= (struct load_command
*)((char *)mh
+ sizeof(struct mach_header
));
197 const uint32_t* h
= (uint32_t*)mh
;
198 logFunc("[LOG] kernel-fixups: file does not start with MH_MAGIC[_64] 0x%08X 0x%08X\n", h
[0], h
[1]);
199 //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
200 return 1; // not a mach-o file
202 const struct load_command
* const cmdsEnd
= (struct load_command
*)((char*)startCmds
+ mh
->sizeofcmds
);
203 const struct load_command
* cmd
= startCmds
;
204 for (uint32_t i
= 0; i
< mh
->ncmds
; ++i
) {
206 logFunc("[LOG] kernel-fixups: parsing load command %d with cmd=0x%x\n", i
, cmd
->cmd
);
208 const struct load_command
* nextCmd
= (struct load_command
*)((char *)cmd
+ cmd
->cmdsize
);
209 if ( cmd
->cmdsize
< 8 ) {
210 //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize);
213 if ( (nextCmd
> cmdsEnd
) || (nextCmd
< startCmds
) ) {
214 //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd);
217 if ( cmd
->cmd
== LC_DYLD_CHAINED_FIXUPS
) {
218 chainedFixups
= (const struct linkedit_data_command
*)cmd
;
219 } else if ( cmd
->cmd
== LC_SEGMENT_64
) {
220 const struct segment_command_64
* seg
= (const struct segment_command_64
*)cmd
;
221 if ( areEqual(seg
->segname
, "__TEXT") ) {
222 textVMAddr
= seg
->vmaddr
;
223 } else if ( areEqual(seg
->segname
, "__LINKEDIT") ) {
224 linkeditVMAddr
= seg
->vmaddr
;
225 linkeditFileOffset
= seg
->fileoff
;
231 uintptr_t slide
= (uintptr_t)mh
- textVMAddr
;
234 logFunc("[LOG] kernel-fixups: slide 0x%llx\n", slide
);
237 if ( chainedFixups
== 0 )
241 logFunc("[LOG] kernel-fixups: found chained fixups %p\n", chainedFixups
);
242 logFunc("[LOG] kernel-fixups: found linkeditVMAddr %p\n", (void*)linkeditVMAddr
);
243 logFunc("[LOG] kernel-fixups: found linkeditFileOffset %p\n", (void*)linkeditFileOffset
);
246 // Now we have the chained fixups, walk it to apply all the rebases
247 uint32_t offsetInLinkedit
= chainedFixups
->dataoff
- linkeditFileOffset
;
248 uintptr_t linkeditStartAddr
= linkeditVMAddr
+ slide
;
250 logFunc("[LOG] kernel-fixups: offsetInLinkedit 0x%x\n", offsetInLinkedit
);
251 logFunc("[LOG] kernel-fixups: linkeditStartAddr %p\n", (void*)linkeditStartAddr
);
254 const struct dyld_chained_fixups_header
* fixupsHeader
= (const struct dyld_chained_fixups_header
*)(linkeditStartAddr
+ offsetInLinkedit
);
255 const struct dyld_chained_starts_in_image
* fixupStarts
= (const struct dyld_chained_starts_in_image
*)((uint8_t*)fixupsHeader
+ fixupsHeader
->starts_offset
);
257 logFunc("[LOG] kernel-fixups: fixupsHeader %p\n", fixupsHeader
);
258 logFunc("[LOG] kernel-fixups: fixupStarts %p\n", fixupStarts
);
262 for (uint32_t segIndex
=0; segIndex
< fixupStarts
->seg_count
&& !stopped
; ++segIndex
) {
264 logFunc("[LOG] kernel-fixups: segment %d\n", segIndex
);
266 if ( fixupStarts
->seg_info_offset
[segIndex
] == 0 )
268 const struct dyld_chained_starts_in_segment
* segInfo
= (const struct dyld_chained_starts_in_segment
*)((uint8_t*)fixupStarts
+ fixupStarts
->seg_info_offset
[segIndex
]);
269 for (uint32_t pageIndex
=0; pageIndex
< segInfo
->page_count
&& !stopped
; ++pageIndex
) {
270 uint16_t offsetInPage
= segInfo
->page_start
[pageIndex
];
271 if ( offsetInPage
== DYLD_CHAINED_PTR_START_NONE
)
273 if ( offsetInPage
& DYLD_CHAINED_PTR_START_MULTI
) {
274 // FIXME: Implement this
278 // one chain per page
279 if ( walkChain(mh
, segInfo
, pageIndex
, offsetInPage
, slide
, basePointers
, logFunc
) )
288 #endif /* KERNEL_FIXUPS_H */