]>
Commit | Line | Data |
---|---|---|
0959b6d4 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved. | |
4 | * | |
5 | * @APPLE_LICENSE_HEADER_START@ | |
6 | * | |
7 | * This file contains Original Code and/or Modifications of Original Code | |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
20 | * limitations under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
bac542e6 A |
25 | #define __STDC_LIMIT_MACROS |
26 | #include <stdint.h> | |
0959b6d4 A |
27 | #include <stddef.h> |
28 | #include <string.h> | |
bac542e6 | 29 | #include <stdlib.h> |
0959b6d4 A |
30 | #include <mach/mach.h> |
31 | #include <mach-o/loader.h> | |
32 | #include <mach-o/ldsyms.h> | |
33 | #include <mach-o/reloc.h> | |
34 | #if __ppc__ || __ppc64__ | |
35 | #include <mach-o/ppc/reloc.h> | |
36 | #endif | |
8bc9f0af A |
37 | #if __x86_64__ |
38 | #include <mach-o/x86_64/reloc.h> | |
39 | #endif | |
0959b6d4 A |
40 | #include "dyld.h" |
41 | ||
bac542e6 A |
42 | #ifndef MH_PIE |
43 | #define MH_PIE 0x200000 | |
44 | #endif | |
45 | ||
46 | ||
0959b6d4 A |
47 | #if __LP64__ |
48 | #define macho_header mach_header_64 | |
49 | #define LC_SEGMENT_COMMAND LC_SEGMENT_64 | |
50 | #define macho_segment_command segment_command_64 | |
51 | #define macho_section section_64 | |
52 | #define RELOC_SIZE 3 | |
53 | #else | |
54 | #define macho_header mach_header | |
55 | #define LC_SEGMENT_COMMAND LC_SEGMENT | |
56 | #define macho_segment_command segment_command | |
57 | #define macho_section section | |
58 | #define RELOC_SIZE 2 | |
59 | #endif | |
60 | ||
8bc9f0af A |
61 | #if __x86_64__ |
62 | #define POINTER_RELOC X86_64_RELOC_UNSIGNED | |
63 | #else | |
3d7c199a | 64 | #define POINTER_RELOC GENERIC_RELOC_VANILLA |
8bc9f0af | 65 | #endif |
0959b6d4 | 66 | |
bac542e6 A |
67 | // from dyld.cpp |
68 | namespace dyld { extern bool isRosetta(); }; | |
69 | ||
70 | ||
0959b6d4 A |
71 | // |
72 | // Code to bootstrap dyld into a runnable state | |
73 | // | |
74 | // | |
75 | ||
76 | namespace dyldbootstrap { | |
77 | ||
78 | ||
79 | typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[]); | |
80 | ||
81 | // | |
82 | // For a regular executable, the crt code calls dyld to run the executables initializers. | |
83 | // For a static executable, crt directly runs the initializers. | |
84 | // dyld (should be static) but is a dynamic executable and needs this hack to run its own initializers. | |
85 | // We pass argc, argv, etc in case libc.a uses those arguments | |
86 | // | |
87 | static void runDyldInitializers(const struct macho_header* mh, intptr_t slide, int argc, const char* argv[], const char* envp[], const char* apple[]) | |
88 | { | |
89 | const uint32_t cmd_count = mh->ncmds; | |
90 | const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); | |
91 | const struct load_command* cmd = cmds; | |
92 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
93 | switch (cmd->cmd) { | |
94 | case LC_SEGMENT_COMMAND: | |
95 | { | |
96 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
97 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
98 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
99 | for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
100 | const uint8_t type = sect->flags & SECTION_TYPE; | |
101 | if ( type == S_MOD_INIT_FUNC_POINTERS ){ | |
102 | Initializer* inits = (Initializer*)(sect->addr + slide); | |
103 | const uint32_t count = sect->size / sizeof(uintptr_t); | |
104 | for (uint32_t i=0; i < count; ++i) { | |
105 | Initializer func = inits[i]; | |
106 | func(argc, argv, envp, apple); | |
107 | } | |
108 | } | |
109 | } | |
110 | } | |
111 | break; | |
112 | } | |
113 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
114 | } | |
115 | } | |
116 | ||
0959b6d4 A |
117 | // |
118 | // If the kernel does not load dyld at its preferred address, we need to apply | |
119 | // fixups to various initialized parts of the __DATA segment | |
120 | // | |
121 | static void rebaseDyld(const struct macho_header* mh, intptr_t slide) | |
122 | { | |
3d7c199a A |
123 | // rebase non-lazy pointers (which all point internal to dyld, since dyld uses no shared libraries) |
124 | // and get interesting pointers into dyld | |
0959b6d4 A |
125 | const uint32_t cmd_count = mh->ncmds; |
126 | const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); | |
127 | const struct load_command* cmd = cmds; | |
128 | const struct macho_segment_command* linkEditSeg = NULL; | |
8bc9f0af A |
129 | #if __x86_64__ |
130 | const struct macho_segment_command* firstWritableSeg = NULL; | |
131 | #endif | |
0959b6d4 | 132 | const struct dysymtab_command* dynamicSymbolTable = NULL; |
0959b6d4 A |
133 | for (uint32_t i = 0; i < cmd_count; ++i) { |
134 | switch (cmd->cmd) { | |
135 | case LC_SEGMENT_COMMAND: | |
136 | { | |
137 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
138 | if ( strcmp(seg->segname, "__LINKEDIT") == 0 ) | |
139 | linkEditSeg = seg; | |
140 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
141 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
142 | for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
143 | const uint8_t type = sect->flags & SECTION_TYPE; | |
3d7c199a A |
144 | if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { |
145 | // rebase non-lazy pointers (which all point internal to dyld, since dyld uses no shared libraries) | |
146 | const uint32_t pointerCount = sect->size / sizeof(uintptr_t); | |
147 | uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + slide); | |
148 | for (uint32_t j=0; j < pointerCount; ++j) { | |
149 | symbolPointers[j] += slide; | |
150 | } | |
151 | } | |
0959b6d4 | 152 | } |
8bc9f0af A |
153 | #if __x86_64__ |
154 | if ( (firstWritableSeg == NULL) && (seg->initprot & VM_PROT_WRITE) ) | |
155 | firstWritableSeg = seg; | |
156 | #endif | |
0959b6d4 A |
157 | } |
158 | break; | |
159 | case LC_DYSYMTAB: | |
160 | dynamicSymbolTable = (struct dysymtab_command *)cmd; | |
161 | break; | |
162 | } | |
163 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
164 | } | |
165 | ||
166 | // use reloc's to rebase all random data pointers | |
8bc9f0af A |
167 | #if __x86_64__ |
168 | const uintptr_t relocBase = firstWritableSeg->vmaddr + slide; | |
169 | #else | |
0959b6d4 | 170 | const uintptr_t relocBase = (uintptr_t)mh; |
8bc9f0af | 171 | #endif |
0959b6d4 A |
172 | const relocation_info* const relocsStart = (struct relocation_info*)(linkEditSeg->vmaddr + slide + dynamicSymbolTable->locreloff - linkEditSeg->fileoff); |
173 | const relocation_info* const relocsEnd = &relocsStart[dynamicSymbolTable->nlocrel]; | |
174 | for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { | |
3d7c199a A |
175 | #if __ppc__ || __ppc64__ || __i36__ |
176 | if ( (reloc->r_address & R_SCATTERED) != 0 ) | |
177 | throw "scattered relocation in dyld"; | |
178 | #endif | |
179 | if ( reloc->r_length != RELOC_SIZE ) | |
180 | throw "relocation in dyld has wrong size"; | |
181 | ||
182 | if ( reloc->r_type != POINTER_RELOC ) | |
183 | throw "relocation in dyld has wrong type"; | |
184 | ||
185 | // update pointer by amount dyld slid | |
186 | *((uintptr_t*)(reloc->r_address + relocBase)) += slide; | |
0959b6d4 | 187 | } |
0959b6d4 A |
188 | } |
189 | ||
3d7c199a | 190 | |
0959b6d4 A |
191 | // |
192 | // For some reason the kernel loads dyld with __TEXT and __LINKEDIT writable | |
193 | // rdar://problem/3702311 | |
194 | // | |
195 | static void segmentProtectDyld(const struct macho_header* mh, intptr_t slide) | |
196 | { | |
197 | const uint32_t cmd_count = mh->ncmds; | |
198 | const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); | |
199 | const struct load_command* cmd = cmds; | |
200 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
201 | switch (cmd->cmd) { | |
202 | case LC_SEGMENT_COMMAND: | |
203 | { | |
204 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
205 | vm_address_t addr = seg->vmaddr + slide; | |
206 | vm_size_t size = seg->vmsize; | |
207 | const bool setCurrentPermissions = false; | |
208 | vm_protect(mach_task_self(), addr, size, setCurrentPermissions, seg->initprot); | |
bac542e6 | 209 | //dyld::log("dyld: segment %s, 0x%08X -> 0x%08X, set to %d\n", seg->segname, addr, addr+size-1, seg->initprot); |
0959b6d4 A |
210 | } |
211 | break; | |
212 | } | |
213 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
214 | } | |
215 | ||
216 | } | |
217 | ||
bac542e6 A |
218 | |
219 | // | |
220 | // re-map the main executable to a new random address | |
221 | // | |
222 | static const struct mach_header* randomizeExecutableLoadAddress(const struct mach_header* orgMH, uintptr_t* appsSlide) | |
223 | { | |
224 | #if __ppc__ | |
225 | // don't slide PIE programs running under rosetta | |
226 | if ( dyld::isRosetta() ) | |
227 | return orgMH; | |
228 | #endif | |
229 | // count segments | |
230 | uint32_t segCount = 0; | |
231 | const uint32_t cmd_count = orgMH->ncmds; | |
232 | const struct load_command* const cmds = (struct load_command*)(((char*)orgMH)+sizeof(macho_header)); | |
233 | const struct load_command* cmd = cmds; | |
234 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
235 | if ( cmd->cmd == LC_SEGMENT_COMMAND ) { | |
236 | const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd; | |
237 | // page-zero and custom stacks don't move | |
238 | if ( (strcmp(segCmd->segname, "__PAGEZERO") != 0) && (strcmp(segCmd->segname, "__UNIXSTACK") != 0) ) | |
239 | ++segCount; | |
240 | } | |
241 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
242 | } | |
243 | ||
244 | // make copy of segment info | |
245 | macho_segment_command segs[segCount]; | |
246 | uint32_t index = 0; | |
247 | uintptr_t highestAddressUsed = 0; | |
248 | uintptr_t lowestAddressUsed = UINTPTR_MAX; | |
249 | cmd = cmds; | |
250 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
251 | if ( cmd->cmd == LC_SEGMENT_COMMAND ) { | |
252 | const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd; | |
253 | if ( (strcmp(segCmd->segname, "__PAGEZERO") != 0) && (strcmp(segCmd->segname, "__UNIXSTACK") != 0) ) { | |
254 | segs[index++] = *segCmd; | |
255 | if ( (segCmd->vmaddr + segCmd->vmsize) > highestAddressUsed ) | |
256 | highestAddressUsed = ((segCmd->vmaddr + segCmd->vmsize) + 4095) & -4096; | |
257 | if ( segCmd->vmaddr < lowestAddressUsed ) | |
258 | lowestAddressUsed = segCmd->vmaddr; | |
259 | // do nothing if kernel has already randomized load address | |
260 | if ( (strcmp(segCmd->segname, "__TEXT") == 0) && (segCmd->vmaddr != (uintptr_t)orgMH) ) | |
261 | return orgMH; | |
262 | } | |
263 | } | |
264 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
265 | } | |
266 | ||
267 | // choose a random new base address | |
268 | #if __LP64__ | |
269 | uintptr_t highestAddressPossible = highestAddressUsed + 0x100000000ULL; | |
270 | #else | |
271 | uintptr_t highestAddressPossible = 0x80000000; | |
272 | #endif | |
273 | uintptr_t sizeNeeded = highestAddressUsed-lowestAddressUsed; | |
274 | if ( (highestAddressPossible-sizeNeeded) < highestAddressUsed ) { | |
275 | // new and old segments will overlap | |
276 | // need better algorithm for remapping | |
277 | // punt and don't re-map | |
278 | return orgMH; | |
279 | } | |
280 | uintptr_t possibleRange = (highestAddressPossible-sizeNeeded) - highestAddressUsed; | |
281 | uintptr_t newBaseAddress = highestAddressUsed + ((arc4random() % possibleRange) & -4096); | |
282 | ||
283 | vm_address_t addr = newBaseAddress; | |
284 | // reserve new address range | |
285 | if ( vm_allocate(mach_task_self(), &addr, sizeNeeded, VM_FLAGS_FIXED) == KERN_SUCCESS ) { | |
286 | // copy each segment to new address | |
287 | for (uint32_t i = 0; i < segCount; ++i) { | |
288 | uintptr_t newSegAddress = segs[i].vmaddr - lowestAddressUsed + newBaseAddress; | |
289 | if ( (vm_copy(mach_task_self(), segs[i].vmaddr, segs[i].vmsize, newSegAddress) != KERN_SUCCESS) | |
290 | || (vm_protect(mach_task_self(), newSegAddress, segs[i].vmsize, true, segs[i].maxprot) != KERN_SUCCESS) | |
291 | || (vm_protect(mach_task_self(), newSegAddress, segs[i].vmsize, false, segs[i].initprot) != KERN_SUCCESS) ) { | |
292 | // can't copy so dealloc new region and run with original base address | |
293 | vm_deallocate(mach_task_self(), newBaseAddress, sizeNeeded); | |
294 | dyld::warn("could not relocate position independent exectable\n"); | |
295 | return orgMH; | |
296 | } | |
297 | } | |
298 | // unmap original segments | |
299 | vm_deallocate(mach_task_self(), lowestAddressUsed, highestAddressUsed-lowestAddressUsed); | |
300 | ||
301 | // run with newly mapped executable | |
302 | *appsSlide = newBaseAddress - lowestAddressUsed; | |
303 | return (const struct mach_header*)newBaseAddress; | |
304 | } | |
305 | ||
306 | // can't get new range, so don't slide to random address | |
307 | return orgMH; | |
308 | } | |
309 | ||
310 | ||
0959b6d4 A |
311 | extern "C" void dyld_exceptions_init(const struct macho_header*, uintptr_t slide); // in dyldExceptions.cpp |
312 | extern "C" void mach_init(); | |
313 | ||
bac542e6 A |
314 | // |
315 | // _pthread_keys is partitioned in a lower part that dyld will use; libSystem | |
316 | // will use the upper part. We set __pthread_tsd_first to 1 as the start of | |
317 | // the lower part. Libc will take #1 and c++ exceptions will take #2. There | |
318 | // is one free key=3 left. | |
319 | // | |
320 | extern "C" { | |
321 | extern int __pthread_tsd_first; | |
322 | extern void _pthread_keys_init(); | |
323 | } | |
324 | ||
325 | ||
0959b6d4 A |
326 | // |
327 | // This is code to bootstrap dyld. This work in normally done for a program by dyld and crt. | |
328 | // In dyld we have to do this manually. | |
329 | // | |
330 | uintptr_t start(const struct mach_header* appsMachHeader, int argc, const char* argv[], intptr_t slide) | |
331 | { | |
332 | // _mh_dylinker_header is magic symbol defined by static linker (ld), see <mach-o/ldsyms.h> | |
333 | const struct macho_header* dyldsMachHeader = (const struct macho_header*)(((char*)&_mh_dylinker_header)+slide); | |
334 | ||
335 | // if kernel had to slide dyld, we need to fix up load sensitive locations | |
336 | // we have to do this before using any global variables | |
337 | if ( slide != 0 ) { | |
338 | rebaseDyld(dyldsMachHeader, slide); | |
339 | } | |
340 | ||
bac542e6 A |
341 | uintptr_t appsSlide = 0; |
342 | ||
343 | // set pthread keys to dyld range | |
344 | __pthread_tsd_first = 1; | |
345 | _pthread_keys_init(); | |
346 | ||
0959b6d4 A |
347 | // enable C++ exceptions to work inside dyld |
348 | dyld_exceptions_init(dyldsMachHeader, slide); | |
349 | ||
350 | // allow dyld to use mach messaging | |
351 | mach_init(); | |
352 | ||
353 | // set protection on segments (has to be done after mach_init) | |
354 | segmentProtectDyld(dyldsMachHeader, slide); | |
355 | ||
356 | // kernel sets up env pointer to be just past end of agv array | |
357 | const char** envp = &argv[argc+1]; | |
358 | ||
359 | // kernel sets up apple pointer to be just past end of envp array | |
360 | const char** apple = envp; | |
361 | while(*apple != NULL) { ++apple; } | |
362 | ++apple; | |
363 | ||
364 | // run all C++ initializers inside dyld | |
365 | runDyldInitializers(dyldsMachHeader, slide, argc, argv, envp, apple); | |
366 | ||
bac542e6 A |
367 | // if main executable was linked -pie, then randomize its load address |
368 | if ( appsMachHeader->flags & MH_PIE ) | |
369 | appsMachHeader = randomizeExecutableLoadAddress(appsMachHeader, &appsSlide); | |
370 | ||
0959b6d4 | 371 | // now that we are done bootstrapping dyld, call dyld's main |
bac542e6 | 372 | return dyld::_main(appsMachHeader, appsSlide, argc, argv, envp, apple); |
0959b6d4 A |
373 | } |
374 | ||
375 | ||
376 | ||
377 | ||
378 | } // end of namespace | |
379 | ||
380 | ||
381 | ||
382 |