]>
Commit | Line | Data |
---|---|---|
69a49097 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2006 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 | ||
25 | #include <sys/types.h> | |
26 | #include <sys/stat.h> | |
27 | #include <sys/mman.h> | |
28 | #include <mach/mach.h> | |
29 | #include <limits.h> | |
30 | #include <stdarg.h> | |
31 | #include <stdio.h> | |
32 | #include <fcntl.h> | |
33 | #include <errno.h> | |
34 | #include <unistd.h> | |
35 | #include <mach-o/loader.h> | |
36 | #include <mach-o/fat.h> | |
37 | #include <mach-o/reloc.h> | |
38 | #include <mach-o/ppc/reloc.h> | |
39 | #include <mach-o/x86_64/reloc.h> | |
40 | #include <vector> | |
41 | #include <set> | |
42 | ||
43 | #include "MachOFileAbstraction.hpp" | |
44 | #include "Architectures.hpp" | |
45 | ||
46 | ||
47 | static bool verbose = false; | |
48 | ||
49 | __attribute__((noreturn)) | |
50 | void throwf(const char* format, ...) | |
51 | { | |
52 | va_list list; | |
53 | char* p; | |
54 | va_start(list, format); | |
55 | vasprintf(&p, format, list); | |
56 | va_end(list); | |
57 | ||
58 | const char* t = p; | |
59 | throw t; | |
60 | } | |
61 | ||
62 | ||
63 | class AbstractRebaser | |
64 | { | |
65 | public: | |
66 | virtual cpu_type_t getArchitecture() const = 0; | |
67 | virtual uint64_t getBaseAddress() const = 0; | |
68 | virtual uint64_t getVMSize() const = 0; | |
69 | virtual void setBaseAddress(uint64_t) = 0; | |
70 | }; | |
71 | ||
72 | ||
73 | template <typename A> | |
74 | class Rebaser : public AbstractRebaser | |
75 | { | |
76 | public: | |
77 | Rebaser(const void* machHeader); | |
78 | virtual ~Rebaser() {} | |
79 | ||
80 | virtual cpu_type_t getArchitecture() const; | |
81 | virtual uint64_t getBaseAddress() const; | |
82 | virtual uint64_t getVMSize() const; | |
83 | virtual void setBaseAddress(uint64_t); | |
84 | ||
85 | private: | |
86 | typedef typename A::P P; | |
87 | typedef typename A::P::E E; | |
88 | typedef typename A::P::uint_t pint_t; | |
89 | ||
90 | struct vmmap { pint_t vmaddr; pint_t vmsize; pint_t fileoff; }; | |
91 | ||
92 | void setRelocBase(); | |
93 | void buildSectionTable(); | |
94 | void adjustLoadCommands(); | |
95 | void adjustSymbolTable(); | |
96 | void adjustDATA(); | |
97 | void doLocalRelocation(const macho_relocation_info<P>* reloc); | |
98 | pint_t* mappedAddressForVMAddress(uint32_t vmaddress); | |
99 | ||
100 | const macho_header<P>* fHeader; | |
101 | pint_t fOrignalVMRelocBaseAddress; | |
102 | pint_t fSlide; | |
103 | pint_t fRelocBase; | |
104 | std::vector<vmmap> fVMMApping; | |
105 | }; | |
106 | ||
107 | ||
108 | ||
109 | class MultiArchRebaser | |
110 | { | |
111 | public: | |
112 | MultiArchRebaser(const char* path, bool writable=false); | |
113 | ~MultiArchRebaser(); | |
114 | ||
115 | const std::vector<AbstractRebaser*>& getArchs() const { return fRebasers; } | |
116 | void commit(); | |
117 | ||
118 | private: | |
119 | std::vector<AbstractRebaser*> fRebasers; | |
120 | void* fMappingAddress; | |
121 | uint64_t fFileSize; | |
122 | }; | |
123 | ||
124 | ||
125 | ||
126 | MultiArchRebaser::MultiArchRebaser(const char* path, bool writable) | |
127 | : fMappingAddress(0), fFileSize(0) | |
128 | { | |
129 | // map in whole file | |
130 | int fd = ::open(path, (writable ? O_RDWR : O_RDONLY), 0); | |
131 | if ( fd == -1 ) | |
132 | throwf("can't open file, errno=%d", errno); | |
133 | struct stat stat_buf; | |
134 | if ( fstat(fd, &stat_buf) == -1) | |
135 | throwf("can't stat open file %s, errno=%d", path, errno); | |
136 | if ( stat_buf.st_size < 20 ) | |
137 | throwf("file too small %s", path); | |
138 | const int prot = writable ? (PROT_READ | PROT_WRITE) : PROT_READ; | |
139 | const int flags = writable ? (MAP_FILE | MAP_SHARED) : (MAP_FILE | MAP_PRIVATE); | |
140 | uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, prot, flags, fd, 0); | |
141 | if ( p == (uint8_t*)(-1) ) | |
142 | throwf("can't map file %s, errno=%d", path, errno); | |
143 | ::close(fd); | |
144 | ||
145 | // if fat file, process each architecture | |
146 | const fat_header* fh = (fat_header*)p; | |
147 | const mach_header* mh = (mach_header*)p; | |
148 | if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { | |
149 | // Fat header is always big-endian | |
150 | const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); | |
151 | for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { | |
152 | uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset); | |
153 | try { | |
154 | switch ( OSSwapBigToHostInt32(archs[i].cputype) ) { | |
155 | case CPU_TYPE_POWERPC: | |
156 | fRebasers.push_back(new Rebaser<ppc>(&p[fileOffset])); | |
157 | break; | |
158 | case CPU_TYPE_POWERPC64: | |
159 | fRebasers.push_back(new Rebaser<ppc64>(&p[fileOffset])); | |
160 | break; | |
161 | case CPU_TYPE_I386: | |
162 | fRebasers.push_back(new Rebaser<x86>(&p[fileOffset])); | |
163 | break; | |
164 | case CPU_TYPE_X86_64: | |
165 | fRebasers.push_back(new Rebaser<x86_64>(&p[fileOffset])); | |
166 | break; | |
167 | default: | |
168 | throw "unknown file format"; | |
169 | } | |
170 | } | |
171 | catch (const char* msg) { | |
172 | fprintf(stderr, "rebase warning: %s for %s\n", msg, path); | |
173 | } | |
174 | } | |
175 | } | |
176 | else { | |
177 | try { | |
178 | if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) { | |
179 | fRebasers.push_back(new Rebaser<ppc>(mh)); | |
180 | } | |
181 | else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) { | |
182 | fRebasers.push_back(new Rebaser<ppc64>(mh)); | |
183 | } | |
184 | else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) { | |
185 | fRebasers.push_back(new Rebaser<x86>(mh)); | |
186 | } | |
187 | else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) { | |
188 | fRebasers.push_back(new Rebaser<x86_64>(mh)); | |
189 | } | |
190 | else { | |
191 | throw "unknown file format"; | |
192 | } | |
193 | } | |
194 | catch (const char* msg) { | |
195 | fprintf(stderr, "rebase warning: %s for %s\n", msg, path); | |
196 | } | |
197 | } | |
198 | ||
199 | fMappingAddress = p; | |
200 | fFileSize = stat_buf.st_size; | |
201 | } | |
202 | ||
203 | ||
204 | MultiArchRebaser::~MultiArchRebaser() | |
205 | { | |
206 | ::munmap(fMappingAddress, fFileSize); | |
207 | } | |
208 | ||
209 | void MultiArchRebaser::commit() | |
210 | { | |
211 | ::msync(fMappingAddress, fFileSize, MS_ASYNC); | |
212 | } | |
213 | ||
214 | ||
215 | ||
216 | template <typename A> | |
217 | Rebaser<A>::Rebaser(const void* machHeader) | |
218 | : fHeader((const macho_header<P>*)machHeader) | |
219 | { | |
220 | switch ( fHeader->filetype() ) { | |
221 | case MH_DYLIB: | |
222 | if ( (fHeader->flags() & MH_SPLIT_SEGS) != 0 ) | |
223 | throw "split-seg dylibs cannot be rebased"; | |
224 | break; | |
225 | case MH_BUNDLE: | |
226 | break; | |
227 | default: | |
228 | throw "file is not a dylib or bundle"; | |
229 | } | |
230 | ||
231 | } | |
232 | ||
233 | template <> cpu_type_t Rebaser<ppc>::getArchitecture() const { return CPU_TYPE_POWERPC; } | |
234 | template <> cpu_type_t Rebaser<ppc64>::getArchitecture() const { return CPU_TYPE_POWERPC64; } | |
235 | template <> cpu_type_t Rebaser<x86>::getArchitecture() const { return CPU_TYPE_I386; } | |
236 | template <> cpu_type_t Rebaser<x86_64>::getArchitecture() const { return CPU_TYPE_X86_64; } | |
237 | ||
238 | ||
239 | template <typename A> | |
240 | uint64_t Rebaser<A>::getBaseAddress() const | |
241 | { | |
242 | uint64_t lowestSegmentAddress = LLONG_MAX; | |
243 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
244 | const uint32_t cmd_count = fHeader->ncmds(); | |
245 | const macho_load_command<P>* cmd = cmds; | |
246 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
247 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
248 | const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd; | |
249 | if ( segCmd->vmaddr() < lowestSegmentAddress ) { | |
250 | lowestSegmentAddress = segCmd->vmaddr(); | |
251 | } | |
252 | } | |
253 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
254 | } | |
255 | return lowestSegmentAddress; | |
256 | } | |
257 | ||
258 | template <typename A> | |
259 | uint64_t Rebaser<A>::getVMSize() const | |
260 | { | |
261 | const macho_segment_command<P>* highestSegmentCmd = NULL; | |
262 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
263 | const uint32_t cmd_count = fHeader->ncmds(); | |
264 | const macho_load_command<P>* cmd = cmds; | |
265 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
266 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
267 | const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd; | |
268 | if ( (highestSegmentCmd == NULL) || (segCmd->vmaddr() > highestSegmentCmd->vmaddr()) ) { | |
269 | highestSegmentCmd = segCmd; | |
270 | } | |
271 | } | |
272 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
273 | } | |
274 | ||
275 | return ((highestSegmentCmd->vmaddr() + highestSegmentCmd->vmsize() - this->getBaseAddress() + 4095) & (-4096)); | |
276 | } | |
277 | ||
278 | ||
279 | template <typename A> | |
280 | void Rebaser<A>::setBaseAddress(uint64_t addr) | |
281 | { | |
282 | // calculate slide | |
283 | fSlide = addr - this->getBaseAddress(); | |
284 | ||
285 | // compute base address for relocations | |
286 | this->setRelocBase(); | |
287 | ||
288 | // build cache of section index to section | |
289 | this->buildSectionTable(); | |
290 | ||
291 | // update load commands | |
292 | this->adjustLoadCommands(); | |
293 | ||
294 | // update symbol table | |
295 | this->adjustSymbolTable(); | |
296 | ||
297 | // update writable segments that have internal pointers | |
298 | this->adjustDATA(); | |
299 | } | |
300 | ||
301 | template <typename A> | |
302 | void Rebaser<A>::adjustLoadCommands() | |
303 | { | |
304 | const macho_segment_command<P>* highestSegmentCmd = NULL; | |
305 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
306 | const uint32_t cmd_count = fHeader->ncmds(); | |
307 | const macho_load_command<P>* cmd = cmds; | |
308 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
309 | switch ( cmd->cmd() ) { | |
310 | case LC_ID_DYLIB: | |
311 | if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { | |
312 | // clear timestamp so that any prebound clients are invalidated | |
313 | macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd; | |
314 | dylib->set_timestamp(1); | |
315 | } | |
316 | break; | |
317 | case LC_LOAD_DYLIB: | |
318 | case LC_LOAD_WEAK_DYLIB: | |
319 | if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { | |
320 | // clear expected timestamps so that this image will load with invalid prebinding | |
321 | macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd; | |
322 | dylib->set_timestamp(2); | |
323 | } | |
324 | break; | |
325 | case macho_routines_command<P>::CMD: | |
326 | // update -init command | |
327 | { | |
328 | struct macho_routines_command<P>* routines = (struct macho_routines_command<P>*)cmd; | |
329 | routines->set_init_address(routines->init_address() + fSlide); | |
330 | } | |
331 | break; | |
332 | case macho_segment_command<P>::CMD: | |
333 | // update segment commands | |
334 | { | |
335 | macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd; | |
336 | seg->set_vmaddr(seg->vmaddr() + fSlide); | |
337 | macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>)); | |
338 | macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()]; | |
339 | for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) { | |
340 | sect->set_addr(sect->addr() + fSlide); | |
341 | } | |
342 | } | |
343 | break; | |
344 | } | |
345 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
346 | } | |
347 | } | |
348 | ||
349 | ||
350 | template <typename A> | |
351 | void Rebaser<A>::buildSectionTable() | |
352 | { | |
353 | // build vector of sections | |
354 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
355 | const uint32_t cmd_count = fHeader->ncmds(); | |
356 | const macho_load_command<P>* cmd = cmds; | |
357 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
358 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
359 | const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd; | |
360 | vmmap mapping; | |
361 | mapping.vmaddr = seg->vmaddr(); | |
362 | mapping.vmsize = seg->vmsize(); | |
363 | mapping.fileoff = seg->fileoff(); | |
364 | fVMMApping.push_back(mapping); | |
365 | } | |
366 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
367 | } | |
368 | } | |
369 | ||
370 | ||
371 | template <typename A> | |
372 | void Rebaser<A>::adjustSymbolTable() | |
373 | { | |
374 | const macho_dysymtab_command<P>* dysymtab = NULL; | |
375 | macho_nlist<P>* symbolTable = NULL; | |
376 | ||
377 | // get symbol table info | |
378 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
379 | const uint32_t cmd_count = fHeader->ncmds(); | |
380 | const macho_load_command<P>* cmd = cmds; | |
381 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
382 | switch (cmd->cmd()) { | |
383 | case LC_SYMTAB: | |
384 | { | |
385 | const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd; | |
386 | symbolTable = (macho_nlist<P>*)(((uint8_t*)fHeader) + symtab->symoff()); | |
387 | } | |
388 | break; | |
389 | case LC_DYSYMTAB: | |
390 | dysymtab = (macho_dysymtab_command<P>*)cmd; | |
391 | break; | |
392 | } | |
393 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
394 | } | |
395 | ||
396 | // walk all exports and slide their n_value | |
397 | macho_nlist<P>* lastExport = &symbolTable[dysymtab->iextdefsym()+dysymtab->nextdefsym()]; | |
398 | for (macho_nlist<P>* entry = &symbolTable[dysymtab->iextdefsym()]; entry < lastExport; ++entry) { | |
399 | if ( (entry->n_type() & N_TYPE) == N_SECT ) | |
400 | entry->set_n_value(entry->n_value() + fSlide); | |
401 | } | |
402 | ||
403 | // walk all local symbols and slide their n_value | |
404 | macho_nlist<P>* lastLocal = &symbolTable[dysymtab->ilocalsym()+dysymtab->nlocalsym()]; | |
405 | for (macho_nlist<P>* entry = &symbolTable[dysymtab->ilocalsym()]; entry < lastLocal; ++entry) { | |
406 | if ( entry->n_sect() != NO_SECT ) | |
407 | entry->set_n_value(entry->n_value() + fSlide); | |
408 | } | |
409 | } | |
410 | ||
411 | template <typename A> | |
412 | void Rebaser<A>::adjustDATA() | |
413 | { | |
414 | const macho_dysymtab_command<P>* dysymtab = NULL; | |
415 | ||
416 | // get symbol table info | |
417 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
418 | const uint32_t cmd_count = fHeader->ncmds(); | |
419 | const macho_load_command<P>* cmd = cmds; | |
420 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
421 | switch (cmd->cmd()) { | |
422 | case LC_DYSYMTAB: | |
423 | dysymtab = (macho_dysymtab_command<P>*)cmd; | |
424 | break; | |
425 | } | |
426 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
427 | } | |
428 | ||
429 | // walk all local relocations and slide every pointer | |
430 | const macho_relocation_info<P>* const relocsStart = (macho_relocation_info<P>*)(((uint8_t*)fHeader) + dysymtab->locreloff()); | |
431 | const macho_relocation_info<P>* const relocsEnd = &relocsStart[dysymtab->nlocrel()]; | |
432 | for (const macho_relocation_info<P>* reloc=relocsStart; reloc < relocsEnd; ++reloc) { | |
433 | this->doLocalRelocation(reloc); | |
434 | } | |
435 | ||
436 | // walk non-lazy-pointers and slide the ones that are LOCAL | |
437 | cmd = cmds; | |
438 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
439 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
440 | const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd; | |
441 | const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>)); | |
442 | const macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()]; | |
443 | const uint32_t* const indirectTable = (uint32_t*)(((uint8_t*)fHeader) + dysymtab->indirectsymoff()); | |
444 | for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) { | |
445 | if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { | |
446 | const uint32_t indirectTableOffset = sect->reserved1(); | |
447 | uint32_t pointerCount = sect->size() / sizeof(pint_t); | |
448 | pint_t* nonLazyPointer = (pint_t*)(((uint8_t*)fHeader) + sect->offset()); | |
449 | for (uint32_t i=0; i < pointerCount; ++i, ++nonLazyPointer) { | |
450 | if ( E::get32(indirectTable[indirectTableOffset + i]) == INDIRECT_SYMBOL_LOCAL ) { | |
451 | P::setP(*nonLazyPointer, A::P::getP(*nonLazyPointer) + fSlide); | |
452 | } | |
453 | } | |
454 | } | |
455 | } | |
456 | } | |
457 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
458 | } | |
459 | ||
460 | } | |
461 | ||
462 | ||
463 | template <typename A> | |
464 | typename A::P::uint_t* Rebaser<A>::mappedAddressForVMAddress(uint32_t vmaddress) | |
465 | { | |
466 | for(typename std::vector<vmmap>::iterator it = fVMMApping.begin(); it != fVMMApping.end(); ++it) { | |
467 | //fprintf(stderr, "vmaddr=0x%08lX, vmsize=0x%08lX\n", it->vmaddr, it->vmsize); | |
468 | if ( (vmaddress >= it->vmaddr) && (vmaddress < (it->vmaddr+it->vmsize)) ) { | |
469 | return (pint_t*)((vmaddress - it->vmaddr) + it->fileoff + (uint8_t*)fHeader); | |
470 | } | |
471 | } | |
472 | throwf("reloc address 0x%08X not found", vmaddress); | |
473 | } | |
474 | ||
475 | ||
476 | template <> | |
477 | void Rebaser<x86_64>::doLocalRelocation(const macho_relocation_info<x86_64::P>* reloc) | |
478 | { | |
479 | if ( reloc->r_type() == X86_64_RELOC_UNSIGNED ) { | |
480 | pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); | |
481 | P::setP(*addr, P::getP(*addr) + fSlide); | |
482 | } | |
483 | else { | |
484 | throw "invalid relocation type"; | |
485 | } | |
486 | } | |
487 | ||
488 | template <> | |
489 | void Rebaser<ppc>::doLocalRelocation(const macho_relocation_info<P>* reloc) | |
490 | { | |
491 | if ( (reloc->r_address() & R_SCATTERED) == 0 ) { | |
492 | if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { | |
493 | pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); | |
494 | P::setP(*addr, P::getP(*addr) + fSlide); | |
495 | } | |
496 | } | |
497 | else { | |
498 | macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc; | |
499 | if ( sreloc->r_type() == PPC_RELOC_PB_LA_PTR ) { | |
500 | sreloc->set_r_value( sreloc->r_value() + fSlide ); | |
501 | } | |
502 | else { | |
503 | throw "cannot rebase final linked image with scattered relocations"; | |
504 | } | |
505 | } | |
506 | } | |
507 | ||
508 | template <> | |
509 | void Rebaser<x86>::doLocalRelocation(const macho_relocation_info<P>* reloc) | |
510 | { | |
511 | if ( (reloc->r_address() & R_SCATTERED) == 0 ) { | |
512 | if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { | |
513 | pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); | |
514 | P::setP(*addr, P::getP(*addr) + fSlide); | |
515 | } | |
516 | } | |
517 | else { | |
518 | macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc; | |
519 | if ( sreloc->r_type() == GENERIC_RELOC_PB_LA_PTR ) { | |
520 | sreloc->set_r_value( sreloc->r_value() + fSlide ); | |
521 | } | |
522 | else { | |
523 | throw "cannot rebase final linked image with scattered relocations"; | |
524 | } | |
525 | } | |
526 | } | |
527 | ||
528 | template <typename A> | |
529 | void Rebaser<A>::doLocalRelocation(const macho_relocation_info<P>* reloc) | |
530 | { | |
531 | if ( (reloc->r_address() & R_SCATTERED) == 0 ) { | |
532 | if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { | |
533 | pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); | |
534 | P::setP(*addr, P::getP(*addr) + fSlide); | |
535 | } | |
536 | } | |
537 | else { | |
538 | throw "cannot rebase final linked image with scattered relocations"; | |
539 | } | |
540 | } | |
541 | ||
542 | ||
543 | template <typename A> | |
544 | void Rebaser<A>::setRelocBase() | |
545 | { | |
546 | // reloc addresses are from the start of the mapped file (base address) | |
547 | fRelocBase = (pint_t)fHeader; | |
548 | fOrignalVMRelocBaseAddress = this->getBaseAddress(); | |
549 | //fprintf(stderr, "fOrignalVMRelocBaseAddress=0x%08X\n", fOrignalVMRelocBaseAddress); | |
550 | } | |
551 | ||
552 | template <> | |
553 | void Rebaser<ppc64>::setRelocBase() | |
554 | { | |
555 | // reloc addresses either: | |
556 | // 1) from the base address if no writable segment is > 4GB from base address | |
557 | // 2) from start of first writable segment | |
558 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
559 | const uint32_t cmd_count = fHeader->ncmds(); | |
560 | const macho_load_command<P>* cmd = cmds; | |
561 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
562 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
563 | const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd; | |
564 | if ( segCmd->initprot() & VM_PROT_WRITE ) { | |
565 | if ( (segCmd->vmaddr() + segCmd->vmsize() - this->getBaseAddress()) > 0x100000000ULL ) { | |
566 | // found writable segment with address > 4GB past base address | |
567 | fRelocBase = segCmd->fileoff() + (pint_t)fHeader; | |
568 | fOrignalVMRelocBaseAddress = segCmd->vmaddr(); | |
569 | return; | |
570 | } | |
571 | } | |
572 | } | |
573 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
574 | } | |
575 | // just use base address | |
576 | fRelocBase = (pint_t)fHeader; | |
577 | fOrignalVMRelocBaseAddress = this->getBaseAddress(); | |
578 | } | |
579 | ||
580 | template <> | |
581 | void Rebaser<x86_64>::setRelocBase() | |
582 | { | |
583 | // reloc addresses are always based from the start of the first writable segment | |
584 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
585 | const uint32_t cmd_count = fHeader->ncmds(); | |
586 | const macho_load_command<P>* cmd = cmds; | |
587 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
588 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
589 | const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd; | |
590 | if ( segCmd->initprot() & VM_PROT_WRITE ) { | |
591 | fRelocBase = segCmd->fileoff() + (pint_t)fHeader; | |
592 | fOrignalVMRelocBaseAddress = segCmd->vmaddr(); | |
593 | return; | |
594 | } | |
595 | } | |
596 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
597 | } | |
598 | throw "no writable segment"; | |
599 | } | |
600 | ||
601 | ||
602 | static void copyFile(const char* srcFile, const char* dstFile) | |
603 | { | |
604 | // open files | |
605 | int src = open(srcFile, O_RDONLY); | |
606 | if ( src == -1 ) | |
607 | throwf("can't open file %s, errno=%d", srcFile, errno); | |
608 | struct stat stat_buf; | |
609 | if ( fstat(src, &stat_buf) == -1) | |
610 | throwf("can't stat open file %s, errno=%d", srcFile, errno); | |
611 | ||
612 | // create new file with all same permissions to hold copy of dylib | |
613 | ::unlink(dstFile); | |
614 | int dst = open(dstFile, O_CREAT | O_RDWR | O_TRUNC, stat_buf.st_mode); | |
615 | if ( dst == -1 ) | |
616 | throwf("can't create temp file %s, errnor=%d", dstFile, errno); | |
617 | ||
618 | // mark source as "don't cache" | |
619 | (void)fcntl(src, F_NOCACHE, 1); | |
620 | // we want to cache the dst because we are about to map it in and modify it | |
621 | ||
622 | // copy permission bits | |
623 | if ( chmod(dstFile, stat_buf.st_mode & 07777) == -1 ) | |
624 | throwf("can't chmod temp file %s, errno=%d", dstFile, errno); | |
625 | if ( chown(dstFile, stat_buf.st_uid, stat_buf.st_gid) == -1) | |
626 | throwf("can't chown temp file %s, errno=%d", dstFile, errno); | |
627 | ||
628 | // copy contents | |
629 | ssize_t len; | |
630 | const uint32_t kBufferSize = 128*1024; | |
631 | static uint8_t* buffer = NULL; | |
632 | if ( buffer == NULL ) { | |
633 | vm_address_t addr = 0; | |
634 | if ( vm_allocate(mach_task_self(), &addr, kBufferSize, true /*find range*/) == KERN_SUCCESS ) | |
635 | buffer = (uint8_t*)addr; | |
636 | else | |
637 | throw "can't allcoate copy buffer"; | |
638 | } | |
639 | while ( (len = read(src, buffer, kBufferSize)) > 0 ) { | |
640 | if ( write(dst, buffer, len) == -1 ) | |
641 | throwf("write failure copying feil %s, errno=%d", dstFile, errno); | |
642 | } | |
643 | ||
644 | // close files | |
645 | int result1 = close(dst); | |
646 | int result2 = close(src); | |
647 | if ( (result1 != 0) || (result2 != 0) ) | |
648 | throw "can't close file"; | |
649 | } | |
650 | ||
651 | ||
652 | // scan dylibs and collect size info | |
653 | // calculate new base address for each dylib | |
654 | // rebase each file | |
655 | // copy to temp and mmap | |
656 | // update content | |
657 | // unmap/flush | |
658 | // rename | |
659 | ||
660 | struct archInfo { | |
661 | cpu_type_t arch; | |
662 | uint64_t vmSize; | |
663 | uint64_t orgBase; | |
664 | uint64_t newBase; | |
665 | }; | |
666 | ||
667 | struct fileInfo | |
668 | { | |
669 | fileInfo(const char* p) : path(p) {} | |
670 | ||
671 | const char* path; | |
672 | std::vector<archInfo> archs; | |
673 | }; | |
674 | ||
675 | // | |
676 | // add archInfos to fileInfo for every slice of a fat file | |
677 | // for ppc, there may be duplicate architectures (with different sub-types) | |
678 | // | |
679 | static void setSizes(fileInfo& info, const std::set<cpu_type_t>& onlyArchs) | |
680 | { | |
681 | const MultiArchRebaser mar(info.path); | |
682 | const std::vector<AbstractRebaser*>& rebasers = mar.getArchs(); | |
683 | for(std::set<cpu_type_t>::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) { | |
684 | for(std::vector<AbstractRebaser*>::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) { | |
685 | AbstractRebaser* rebaser = *rit; | |
686 | if ( rebaser->getArchitecture() == *ait ) { | |
687 | archInfo ai; | |
688 | ai.arch = *ait; | |
689 | ai.vmSize = rebaser->getVMSize(); | |
690 | ai.orgBase = rebaser->getBaseAddress(); | |
691 | ai.newBase = 0; | |
692 | //fprintf(stderr, "base=0x%llX, size=0x%llX\n", ai.orgBase, ai.vmSize); | |
693 | info.archs.push_back(ai); | |
694 | } | |
695 | } | |
696 | } | |
697 | } | |
698 | ||
699 | static const char* nameForArch(cpu_type_t arch) | |
700 | { | |
701 | switch( arch ) { | |
702 | case CPU_TYPE_POWERPC: | |
703 | return "ppc"; | |
704 | case CPU_TYPE_POWERPC64: | |
705 | return "ppca64"; | |
706 | case CPU_TYPE_I386: | |
707 | return "i386"; | |
708 | case CPU_TYPE_X86_64: | |
709 | return "x86_64"; | |
710 | } | |
711 | return "unknown"; | |
712 | } | |
713 | ||
714 | static void rebase(const fileInfo& info) | |
715 | { | |
716 | // generate temp file name | |
717 | char realFilePath[PATH_MAX]; | |
718 | if ( realpath(info.path, realFilePath) == NULL ) { | |
719 | throwf("realpath() failed on %s, errno=%d", info.path, errno); | |
720 | } | |
721 | const char* tempPath; | |
722 | asprintf((char**)&tempPath, "%s_rebase", realFilePath); | |
723 | ||
724 | // copy whole file to temp file | |
725 | copyFile(info.path, tempPath); | |
726 | ||
727 | try { | |
728 | // rebase temp file | |
729 | MultiArchRebaser mar(tempPath, true); | |
730 | const std::vector<AbstractRebaser*>& rebasers = mar.getArchs(); | |
731 | for(std::vector<archInfo>::const_iterator fait=info.archs.begin(); fait != info.archs.end(); ++fait) { | |
732 | for(std::vector<AbstractRebaser*>::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) { | |
733 | if ( (*rit)->getArchitecture() == fait->arch ) { | |
734 | (*rit)->setBaseAddress(fait->newBase); | |
735 | if ( verbose ) | |
736 | printf("%8s 0x%0llX -> 0x%0llX %s\n", nameForArch(fait->arch), fait->orgBase, fait->newBase, info.path); | |
737 | } | |
738 | } | |
739 | } | |
740 | ||
741 | // flush temp file out to disk | |
742 | mar.commit(); | |
743 | ||
744 | // rename | |
745 | int result = rename(tempPath, info.path); | |
746 | if ( result != 0 ) { | |
747 | throwf("can't swap temporary rebased file: rename(%s,%s) returned errno=%d", tempPath, info.path, errno); | |
748 | } | |
749 | ||
750 | // make sure every really gets out to disk | |
751 | ::sync(); | |
752 | } | |
753 | catch (const char* msg) { | |
754 | // delete temp file | |
755 | ::unlink(tempPath); | |
756 | ||
757 | // throw exception with file name added | |
758 | const char* newMsg; | |
759 | asprintf((char**)&newMsg, "%s for file %s", msg, info.path); | |
760 | throw newMsg; | |
761 | } | |
762 | } | |
763 | ||
764 | static uint64_t totalVMSize(cpu_type_t arch, std::vector<fileInfo>& files) | |
765 | { | |
766 | uint64_t totalSize = 0; | |
767 | for(std::vector<fileInfo>::iterator fit=files.begin(); fit != files.end(); ++fit) { | |
768 | fileInfo& fi = *fit; | |
769 | for(std::vector<archInfo>::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) { | |
770 | if ( fait->arch == arch ) | |
771 | totalSize += fait->vmSize; | |
772 | } | |
773 | } | |
774 | return totalSize; | |
775 | } | |
776 | ||
777 | static uint64_t startAddress(cpu_type_t arch, std::vector<fileInfo>& files, uint64_t lowAddress, uint64_t highAddress) | |
778 | { | |
779 | if ( lowAddress != 0 ) | |
780 | return lowAddress; | |
781 | else if ( highAddress != 0 ) { | |
782 | uint64_t totalSize = totalVMSize(arch, files); | |
783 | if ( highAddress < totalSize ) | |
784 | throwf("cannot use -high_address 0x%X because total size of images is greater: 0x%X", highAddress, totalSize); | |
785 | return highAddress - totalSize; | |
786 | } | |
787 | else { | |
788 | if ( (arch == CPU_TYPE_I386) || (arch == CPU_TYPE_POWERPC) ) { | |
789 | // place dylibs below dyld | |
790 | uint64_t topAddr = 0x8FE00000; | |
791 | uint64_t totalSize = totalVMSize(arch, files); | |
792 | if ( totalSize > topAddr ) | |
793 | throwf("total size of images (0x%X) does not fit below 0x8FE00000", totalSize); | |
794 | return topAddr - totalSize; | |
795 | } | |
796 | else if ( arch == CPU_TYPE_POWERPC64 ) { | |
797 | return 0x200000000ULL; | |
798 | } | |
799 | else if ( arch == CPU_TYPE_X86_64 ) { | |
800 | return 0x200000000ULL; | |
801 | } | |
802 | else | |
803 | throw "unknown architecture"; | |
804 | } | |
805 | } | |
806 | ||
807 | static void usage() | |
808 | { | |
809 | fprintf(stderr, "rebase [-low_address] [-high_address] [-v] [-arch <arch>] files...\n"); | |
810 | } | |
811 | ||
812 | ||
813 | int main(int argc, const char* argv[]) | |
814 | { | |
815 | std::vector<fileInfo> files; | |
816 | std::set<cpu_type_t> onlyArchs; | |
817 | uint64_t lowAddress = 0; | |
818 | uint64_t highAddress = 0; | |
819 | ||
820 | try { | |
821 | // parse command line options | |
822 | char* endptr; | |
823 | for(int i=1; i < argc; ++i) { | |
824 | const char* arg = argv[i]; | |
825 | if ( arg[0] == '-' ) { | |
826 | if ( strcmp(arg, "-v") == 0 ) { | |
827 | verbose = true; | |
828 | } | |
829 | else if ( strcmp(arg, "-low_address") == 0 ) { | |
830 | lowAddress = strtoull(argv[++i], &endptr, 16); | |
831 | } | |
832 | else if ( strcmp(arg, "-high_address") == 0 ) { | |
833 | highAddress = strtoull(argv[++i], &endptr, 16); | |
834 | } | |
835 | else if ( strcmp(arg, "-arch") == 0 ) { | |
836 | const char* arch = argv[++i]; | |
837 | if ( strcmp(arch, "ppc") == 0 ) | |
838 | onlyArchs.insert(CPU_TYPE_POWERPC); | |
839 | else if ( strcmp(arch, "ppc64") == 0 ) | |
840 | onlyArchs.insert(CPU_TYPE_POWERPC64); | |
841 | else if ( strcmp(arch, "i386") == 0 ) | |
842 | onlyArchs.insert(CPU_TYPE_I386); | |
843 | else if ( strcmp(arch, "x86_64") == 0 ) | |
844 | onlyArchs.insert(CPU_TYPE_X86_64); | |
845 | else | |
846 | throwf("unknown architecture %s", arch); | |
847 | } | |
848 | else { | |
849 | usage(); | |
850 | throwf("unknown option: %s\n", arg); | |
851 | } | |
852 | } | |
853 | else { | |
854 | files.push_back(fileInfo(arg)); | |
855 | } | |
856 | } | |
857 | ||
858 | if ( files.size() == 0 ) | |
859 | throw "no files specified"; | |
860 | ||
861 | // use all architectures if no restrictions specified | |
862 | if ( onlyArchs.size() == 0 ) { | |
863 | onlyArchs.insert(CPU_TYPE_POWERPC); | |
864 | onlyArchs.insert(CPU_TYPE_POWERPC64); | |
865 | onlyArchs.insert(CPU_TYPE_I386); | |
866 | onlyArchs.insert(CPU_TYPE_X86_64); | |
867 | } | |
868 | ||
869 | // scan files and collect sizes | |
870 | for(std::vector<fileInfo>::iterator it=files.begin(); it != files.end(); ++it) { | |
871 | setSizes(*it, onlyArchs); | |
872 | } | |
873 | ||
874 | // assign new base address for each arch | |
875 | for(std::set<cpu_type_t>::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) { | |
876 | cpu_type_t arch = *ait; | |
877 | uint64_t baseAddress = startAddress(arch, files, lowAddress, highAddress); | |
878 | for(std::vector<fileInfo>::iterator fit=files.begin(); fit != files.end(); ++fit) { | |
879 | fileInfo& fi = *fit; | |
880 | for(std::vector<archInfo>::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) { | |
881 | if ( fait->arch == arch ) { | |
882 | fait->newBase = baseAddress; | |
883 | baseAddress += fait->vmSize; | |
884 | baseAddress = (baseAddress + 4095) & (-4096); // page align | |
885 | } | |
886 | } | |
887 | } | |
888 | } | |
889 | ||
890 | // rebase each file if it contains something rebaseable | |
891 | for(std::vector<fileInfo>::iterator it=files.begin(); it != files.end(); ++it) { | |
892 | fileInfo& fi = *it; | |
893 | if ( fi.archs.size() > 0 ) | |
894 | rebase(fi); | |
895 | } | |
896 | ||
897 | } | |
898 | catch (const char* msg) { | |
899 | fprintf(stderr, "rebase failed: %s\n", msg); | |
900 | return 1; | |
901 | } | |
902 | ||
903 | return 0; | |
904 | } | |
905 | ||
906 | ||
907 |