dyld-832.7.1.tar.gz
[apple/dyld.git] / testing / test-cases / kernel-fixups.h
1
2 #ifndef KERNEL_FIXUPS_H
3 #define KERNEL_FIXUPS_H
4
5 #include <mach-o/fixup-chains.h>
6 #include <mach-o/loader.h>
7
8 typedef int (*FixupsLogFunc)(const char*, ...);
9 static const int LogFixups = 0;
10
11 // We may not have strcmp, so make our own
12 #if __x86_64__
13 __attribute__((section(("__HIB, __text"))))
14 #else
15 __attribute__((section(("__TEXT_EXEC, __text"))))
16 #endif
17 static int areEqual(const char* a, const char* b) {
18 while (*a && *b) {
19 if (*a != *b)
20 return 0;
21 ++a;
22 ++b;
23 }
24 return *a == *b;
25 }
26
27 #pragma clang diagnostic push
28 #pragma clang diagnostic ignored "-Wmissing-declarations"
29 union ChainedFixupPointerOnDisk
30 {
31 uint64_t raw64;
32 struct dyld_chained_ptr_64_kernel_cache_rebase fixup64;
33 };
34 #pragma clang diagnostic pop
35
36 // Temporary until we have <rdar://problem/57025372>
37 #if __x86_64__
38 __attribute__((section(("__HIB, __text"))))
39 #else
40 __attribute__((section(("__TEXT_EXEC, __text"))))
41 #endif
42 static uint64_t signPointer(struct dyld_chained_ptr_64_kernel_cache_rebase pointer,
43 void* loc,
44 uint64_t target)
45 {
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);
51 } else {
52 discriminator = (uint64_t)(uintptr_t)loc;
53 }
54 }
55 switch ( pointer.key ) {
56 case 0: // IA
57 return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 0, discriminator);
58 case 1: // IB
59 return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 1, discriminator);
60 case 2: // DA
61 return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 2, discriminator);
62 case 3: // DB
63 return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 3, discriminator);
64 }
65 #endif
66 return target;
67 }
68
69 // Temporary until we have <rdar://problem/57025372>
70 #if __x86_64__
71 __attribute__((section(("__HIB, __text"))))
72 #else
73 __attribute__((section(("__TEXT_EXEC, __text"))))
74 #endif
75 void fixupValue(union ChainedFixupPointerOnDisk* fixupLoc,
76 const struct dyld_chained_starts_in_segment* segInfo,
77 uintptr_t slide,
78 const void* basePointers[4],
79 int* stop,
80 FixupsLogFunc logFunc) {
81 if (LogFixups) {
82 logFunc("[LOG] kernel-fixups: fixupValue %p\n", fixupLoc);
83 }
84 switch (segInfo->pointer_format) {
85 #if __LP64__
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);
91 *stop = 1;
92 return;
93 }
94 uintptr_t slidValue = (uintptr_t)baseAddress + fixupLoc->fixup64.target;
95 if (LogFixups) {
96 logFunc("[LOG] kernel-fixups: slidValue %p = %p + %p\n", (void*)slidValue, (void*)baseAddress, (void*)fixupLoc->fixup64.target);
97 }
98 #if __has_feature(ptrauth_calls)
99 if ( fixupLoc->fixup64.isAuth ) {
100 slidValue = signPointer(fixupLoc->fixup64, fixupLoc, slidValue);
101 }
102 #else
103 if ( fixupLoc->fixup64.isAuth ) {
104 logFunc("Unexpected authenticated fixup\n");
105 *stop = 1;
106 return;
107 }
108 #endif // __has_feature(ptrauth_calls)
109 fixupLoc->raw64 = slidValue;
110 break;
111 }
112 #endif // __LP64__
113 default:
114 logFunc("unsupported pointer chain format: 0x%04X", segInfo->pointer_format);
115 *stop = 1;
116 break;
117 }
118 }
119
120 // Temporary until we have <rdar://problem/57025372>
121 #if __x86_64__
122 __attribute__((section(("__HIB, __text"))))
123 #else
124 __attribute__((section(("__TEXT_EXEC, __text"))))
125 #endif
126 int walkChain(const struct mach_header* mh,
127 const struct dyld_chained_starts_in_segment* segInfo,
128 uint32_t pageIndex,
129 uint16_t offsetInPage,
130 uintptr_t slide,
131 const void* basePointers[4],
132 FixupsLogFunc logFunc)
133 {
134 if (LogFixups) {
135 logFunc("[LOG] kernel-fixups: walkChain page[%d]\n", pageIndex);
136 }
137 int stop = 0;
138 uint8_t* pageContentStart = (uint8_t*)mh + segInfo->segment_offset + (pageIndex * segInfo->page_size);
139 union ChainedFixupPointerOnDisk* chain = (union ChainedFixupPointerOnDisk*)(pageContentStart+offsetInPage);
140 int chainEnd = 0;
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);
145 if ( !stop ) {
146 switch (segInfo->pointer_format) {
147 #if __LP64__
148 case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
149 if ( chainContent.fixup64.next == 0 )
150 chainEnd = 1;
151 else
152 chain = (union ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.fixup64.next*4);
153 break;
154 case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
155 if ( chainContent.fixup64.next == 0 )
156 chainEnd = 1;
157 else
158 chain = (union ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.fixup64.next);
159 break;
160 #endif // __LP64__
161 default:
162 logFunc("unknown pointer format 0x%04X", segInfo->pointer_format);
163 stop = 1;
164 }
165 }
166 }
167 return stop;
168 }
169
170 // Temporary until we have <rdar://problem/57025372>
171 #if __x86_64__
172 __attribute__((section(("__HIB, __text"))))
173 #else
174 __attribute__((section(("__TEXT_EXEC, __text"))))
175 #endif
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;
182
183 if (LogFixups) {
184 logFunc("[LOG] kernel-fixups: mh %p\n", mh);
185 }
186
187 if (LogFixups) {
188 logFunc("[LOG] kernel-fixups: parsing load commands\n");
189 }
190
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));
196 else {
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
201 }
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) {
205 if (LogFixups) {
206 logFunc("[LOG] kernel-fixups: parsing load command %d with cmd=0x%x\n", i, cmd->cmd);
207 }
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);
211 return 1;
212 }
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);
215 return 1;
216 }
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;
226 }
227 }
228 cmd = nextCmd;
229 }
230
231 uintptr_t slide = (uintptr_t)mh - textVMAddr;
232
233 if (LogFixups) {
234 logFunc("[LOG] kernel-fixups: slide 0x%llx\n", slide);
235 }
236
237 if ( chainedFixups == 0 )
238 return 0;
239
240 if (LogFixups) {
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);
244 }
245
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;
249 if (LogFixups) {
250 logFunc("[LOG] kernel-fixups: offsetInLinkedit 0x%x\n", offsetInLinkedit);
251 logFunc("[LOG] kernel-fixups: linkeditStartAddr %p\n", (void*)linkeditStartAddr);
252 }
253
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);
256 if (LogFixups) {
257 logFunc("[LOG] kernel-fixups: fixupsHeader %p\n", fixupsHeader);
258 logFunc("[LOG] kernel-fixups: fixupStarts %p\n", fixupStarts);
259 }
260
261 int stopped = 0;
262 for (uint32_t segIndex=0; segIndex < fixupStarts->seg_count && !stopped; ++segIndex) {
263 if (LogFixups) {
264 logFunc("[LOG] kernel-fixups: segment %d\n", segIndex);
265 }
266 if ( fixupStarts->seg_info_offset[segIndex] == 0 )
267 continue;
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 )
272 continue;
273 if ( offsetInPage & DYLD_CHAINED_PTR_START_MULTI ) {
274 // FIXME: Implement this
275 return 1;
276 }
277 else {
278 // one chain per page
279 if ( walkChain(mh, segInfo, pageIndex, offsetInPage, slide, basePointers, logFunc) )
280 stopped = 1;
281 }
282 }
283 }
284
285 return stopped;
286 }
287
288 #endif /* KERNEL_FIXUPS_H */