]> git.saurik.com Git - apple/ld64.git/blob - src/rebase.cpp
e47ce34e9950955f67f37d984bf9ad3700b3b271
[apple/ld64.git] / src / rebase.cpp
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 = &sectionsStart[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 // FIXME ¥¥¥ adjust dylib_module if it exists
411 }
412
413 template <typename A>
414 void Rebaser<A>::adjustDATA()
415 {
416 const macho_dysymtab_command<P>* dysymtab = NULL;
417
418 // get symbol table info
419 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
420 const uint32_t cmd_count = fHeader->ncmds();
421 const macho_load_command<P>* cmd = cmds;
422 for (uint32_t i = 0; i < cmd_count; ++i) {
423 switch (cmd->cmd()) {
424 case LC_DYSYMTAB:
425 dysymtab = (macho_dysymtab_command<P>*)cmd;
426 break;
427 }
428 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
429 }
430
431 // walk all local relocations and slide every pointer
432 const macho_relocation_info<P>* const relocsStart = (macho_relocation_info<P>*)(((uint8_t*)fHeader) + dysymtab->locreloff());
433 const macho_relocation_info<P>* const relocsEnd = &relocsStart[dysymtab->nlocrel()];
434 for (const macho_relocation_info<P>* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
435 this->doLocalRelocation(reloc);
436 }
437
438 // walk non-lazy-pointers and slide the ones that are LOCAL
439 cmd = cmds;
440 for (uint32_t i = 0; i < cmd_count; ++i) {
441 if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
442 const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
443 const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
444 const macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
445 const uint32_t* const indirectTable = (uint32_t*)(((uint8_t*)fHeader) + dysymtab->indirectsymoff());
446 for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
447 if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) {
448 const uint32_t indirectTableOffset = sect->reserved1();
449 uint32_t pointerCount = sect->size() / sizeof(pint_t);
450 pint_t* nonLazyPointer = (pint_t*)(((uint8_t*)fHeader) + sect->offset());
451 for (uint32_t i=0; i < pointerCount; ++i, ++nonLazyPointer) {
452 if ( E::get32(indirectTable[indirectTableOffset + i]) == INDIRECT_SYMBOL_LOCAL ) {
453 P::setP(*nonLazyPointer, A::P::getP(*nonLazyPointer) + fSlide);
454 }
455 }
456 }
457 }
458 }
459 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
460 }
461
462 }
463
464
465 template <typename A>
466 typename A::P::uint_t* Rebaser<A>::mappedAddressForVMAddress(uint32_t vmaddress)
467 {
468 for(typename std::vector<vmmap>::iterator it = fVMMApping.begin(); it != fVMMApping.end(); ++it) {
469 //fprintf(stderr, "vmaddr=0x%08lX, vmsize=0x%08lX\n", it->vmaddr, it->vmsize);
470 if ( (vmaddress >= it->vmaddr) && (vmaddress < (it->vmaddr+it->vmsize)) ) {
471 return (pint_t*)((vmaddress - it->vmaddr) + it->fileoff + (uint8_t*)fHeader);
472 }
473 }
474 throwf("reloc address 0x%08X not found", vmaddress);
475 }
476
477
478 template <>
479 void Rebaser<x86_64>::doLocalRelocation(const macho_relocation_info<x86_64::P>* reloc)
480 {
481 if ( reloc->r_type() == X86_64_RELOC_UNSIGNED ) {
482 pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress);
483 P::setP(*addr, P::getP(*addr) + fSlide);
484 }
485 else {
486 throw "invalid relocation type";
487 }
488 }
489
490 template <>
491 void Rebaser<ppc>::doLocalRelocation(const macho_relocation_info<P>* reloc)
492 {
493 if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
494 if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) {
495 pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress);
496 P::setP(*addr, P::getP(*addr) + fSlide);
497 }
498 }
499 else {
500 macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
501 if ( sreloc->r_type() == PPC_RELOC_PB_LA_PTR ) {
502 sreloc->set_r_value( sreloc->r_value() + fSlide );
503 }
504 else {
505 throw "cannot rebase final linked image with scattered relocations";
506 }
507 }
508 }
509
510 template <>
511 void Rebaser<x86>::doLocalRelocation(const macho_relocation_info<P>* reloc)
512 {
513 if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
514 if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) {
515 pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress);
516 P::setP(*addr, P::getP(*addr) + fSlide);
517 }
518 }
519 else {
520 macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
521 if ( sreloc->r_type() == GENERIC_RELOC_PB_LA_PTR ) {
522 sreloc->set_r_value( sreloc->r_value() + fSlide );
523 }
524 else {
525 throw "cannot rebase final linked image with scattered relocations";
526 }
527 }
528 }
529
530 template <typename A>
531 void Rebaser<A>::doLocalRelocation(const macho_relocation_info<P>* reloc)
532 {
533 if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
534 if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) {
535 pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress);
536 P::setP(*addr, P::getP(*addr) + fSlide);
537 }
538 }
539 else {
540 throw "cannot rebase final linked image with scattered relocations";
541 }
542 }
543
544
545 template <typename A>
546 void Rebaser<A>::setRelocBase()
547 {
548 // reloc addresses are from the start of the mapped file (base address)
549 fRelocBase = (pint_t)fHeader;
550 fOrignalVMRelocBaseAddress = this->getBaseAddress();
551 //fprintf(stderr, "fOrignalVMRelocBaseAddress=0x%08X\n", fOrignalVMRelocBaseAddress);
552 }
553
554 template <>
555 void Rebaser<ppc64>::setRelocBase()
556 {
557 // reloc addresses either:
558 // 1) from the base address if no writable segment is > 4GB from base address
559 // 2) from start of first writable segment
560 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
561 const uint32_t cmd_count = fHeader->ncmds();
562 const macho_load_command<P>* cmd = cmds;
563 for (uint32_t i = 0; i < cmd_count; ++i) {
564 if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
565 const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
566 if ( segCmd->initprot() & VM_PROT_WRITE ) {
567 if ( (segCmd->vmaddr() + segCmd->vmsize() - this->getBaseAddress()) > 0x100000000ULL ) {
568 // found writable segment with address > 4GB past base address
569 fRelocBase = segCmd->fileoff() + (pint_t)fHeader;
570 fOrignalVMRelocBaseAddress = segCmd->vmaddr();
571 return;
572 }
573 }
574 }
575 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
576 }
577 // just use base address
578 fRelocBase = (pint_t)fHeader;
579 fOrignalVMRelocBaseAddress = this->getBaseAddress();
580 }
581
582 template <>
583 void Rebaser<x86_64>::setRelocBase()
584 {
585 // reloc addresses are always based from the start of the first writable segment
586 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
587 const uint32_t cmd_count = fHeader->ncmds();
588 const macho_load_command<P>* cmd = cmds;
589 for (uint32_t i = 0; i < cmd_count; ++i) {
590 if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
591 const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
592 if ( segCmd->initprot() & VM_PROT_WRITE ) {
593 fRelocBase = segCmd->fileoff() + (pint_t)fHeader;
594 fOrignalVMRelocBaseAddress = segCmd->vmaddr();
595 return;
596 }
597 }
598 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
599 }
600 throw "no writable segment";
601 }
602
603
604 static void copyFile(const char* srcFile, const char* dstFile)
605 {
606 // open files
607 int src = open(srcFile, O_RDONLY);
608 if ( src == -1 )
609 throwf("can't open file %s, errno=%d", srcFile, errno);
610 struct stat stat_buf;
611 if ( fstat(src, &stat_buf) == -1)
612 throwf("can't stat open file %s, errno=%d", srcFile, errno);
613
614 // create new file with all same permissions to hold copy of dylib
615 ::unlink(dstFile);
616 int dst = open(dstFile, O_CREAT | O_RDWR | O_TRUNC, stat_buf.st_mode);
617 if ( dst == -1 )
618 throwf("can't create temp file %s, errnor=%d", dstFile, errno);
619
620 // mark source as "don't cache"
621 (void)fcntl(src, F_NOCACHE, 1);
622 // we want to cache the dst because we are about to map it in and modify it
623
624 // copy permission bits
625 if ( chmod(dstFile, stat_buf.st_mode & 07777) == -1 )
626 throwf("can't chmod temp file %s, errno=%d", dstFile, errno);
627 if ( chown(dstFile, stat_buf.st_uid, stat_buf.st_gid) == -1)
628 throwf("can't chown temp file %s, errno=%d", dstFile, errno);
629
630 // copy contents
631 ssize_t len;
632 const uint32_t kBufferSize = 128*1024;
633 static uint8_t* buffer = NULL;
634 if ( buffer == NULL ) {
635 vm_address_t addr = 0;
636 if ( vm_allocate(mach_task_self(), &addr, kBufferSize, true /*find range*/) == KERN_SUCCESS )
637 buffer = (uint8_t*)addr;
638 else
639 throw "can't allcoate copy buffer";
640 }
641 while ( (len = read(src, buffer, kBufferSize)) > 0 ) {
642 if ( write(dst, buffer, len) == -1 )
643 throwf("write failure copying feil %s, errno=%d", dstFile, errno);
644 }
645
646 // close files
647 int result1 = close(dst);
648 int result2 = close(src);
649 if ( (result1 != 0) || (result2 != 0) )
650 throw "can't close file";
651 }
652
653
654 // scan dylibs and collect size info
655 // calculate new base address for each dylib
656 // rebase each file
657 // copy to temp and mmap
658 // update content
659 // unmap/flush
660 // rename
661
662 struct archInfo {
663 cpu_type_t arch;
664 uint64_t vmSize;
665 uint64_t orgBase;
666 uint64_t newBase;
667 };
668
669 struct fileInfo
670 {
671 fileInfo(const char* p) : path(p) {}
672
673 const char* path;
674 std::vector<archInfo> archs;
675 };
676
677 //
678 // add archInfos to fileInfo for every slice of a fat file
679 // for ppc, there may be duplicate architectures (with different sub-types)
680 //
681 static void setSizes(fileInfo& info, const std::set<cpu_type_t>& onlyArchs)
682 {
683 const MultiArchRebaser mar(info.path);
684 const std::vector<AbstractRebaser*>& rebasers = mar.getArchs();
685 for(std::set<cpu_type_t>::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) {
686 for(std::vector<AbstractRebaser*>::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) {
687 AbstractRebaser* rebaser = *rit;
688 if ( rebaser->getArchitecture() == *ait ) {
689 archInfo ai;
690 ai.arch = *ait;
691 ai.vmSize = rebaser->getVMSize();
692 ai.orgBase = rebaser->getBaseAddress();
693 ai.newBase = 0;
694 //fprintf(stderr, "base=0x%llX, size=0x%llX\n", ai.orgBase, ai.vmSize);
695 info.archs.push_back(ai);
696 }
697 }
698 }
699 }
700
701 static const char* nameForArch(cpu_type_t arch)
702 {
703 switch( arch ) {
704 case CPU_TYPE_POWERPC:
705 return "ppc";
706 case CPU_TYPE_POWERPC64:
707 return "ppca64";
708 case CPU_TYPE_I386:
709 return "i386";
710 case CPU_TYPE_X86_64:
711 return "x86_64";
712 }
713 return "unknown";
714 }
715
716 static void rebase(const fileInfo& info)
717 {
718 // generate temp file name
719 char realFilePath[PATH_MAX];
720 if ( realpath(info.path, realFilePath) == NULL ) {
721 throwf("realpath() failed on %s, errno=%d", info.path, errno);
722 }
723 const char* tempPath;
724 asprintf((char**)&tempPath, "%s_rebase", realFilePath);
725
726 // copy whole file to temp file
727 copyFile(info.path, tempPath);
728
729 try {
730 // rebase temp file
731 MultiArchRebaser mar(tempPath, true);
732 const std::vector<AbstractRebaser*>& rebasers = mar.getArchs();
733 for(std::vector<archInfo>::const_iterator fait=info.archs.begin(); fait != info.archs.end(); ++fait) {
734 for(std::vector<AbstractRebaser*>::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) {
735 if ( (*rit)->getArchitecture() == fait->arch ) {
736 (*rit)->setBaseAddress(fait->newBase);
737 if ( verbose )
738 printf("%8s 0x%0llX -> 0x%0llX %s\n", nameForArch(fait->arch), fait->orgBase, fait->newBase, info.path);
739 }
740 }
741 }
742
743 // flush temp file out to disk
744 mar.commit();
745
746 // rename
747 int result = rename(tempPath, info.path);
748 if ( result != 0 ) {
749 throwf("can't swap temporary rebased file: rename(%s,%s) returned errno=%d", tempPath, info.path, errno);
750 }
751
752 // make sure every really gets out to disk
753 ::sync();
754 }
755 catch (const char* msg) {
756 // delete temp file
757 ::unlink(tempPath);
758
759 // throw exception with file name added
760 const char* newMsg;
761 asprintf((char**)&newMsg, "%s for file %s", msg, info.path);
762 throw newMsg;
763 }
764 }
765
766 static uint64_t totalVMSize(cpu_type_t arch, std::vector<fileInfo>& files)
767 {
768 uint64_t totalSize = 0;
769 for(std::vector<fileInfo>::iterator fit=files.begin(); fit != files.end(); ++fit) {
770 fileInfo& fi = *fit;
771 for(std::vector<archInfo>::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) {
772 if ( fait->arch == arch )
773 totalSize += fait->vmSize;
774 }
775 }
776 return totalSize;
777 }
778
779 static uint64_t startAddress(cpu_type_t arch, std::vector<fileInfo>& files, uint64_t lowAddress, uint64_t highAddress)
780 {
781 if ( lowAddress != 0 )
782 return lowAddress;
783 else if ( highAddress != 0 ) {
784 uint64_t totalSize = totalVMSize(arch, files);
785 if ( highAddress < totalSize )
786 throwf("cannot use -high_address 0x%X because total size of images is greater: 0x%X", highAddress, totalSize);
787 return highAddress - totalSize;
788 }
789 else {
790 if ( (arch == CPU_TYPE_I386) || (arch == CPU_TYPE_POWERPC) ) {
791 // place dylibs below dyld
792 uint64_t topAddr = 0x8FE00000;
793 uint64_t totalSize = totalVMSize(arch, files);
794 if ( totalSize > topAddr )
795 throwf("total size of images (0x%X) does not fit below 0x8FE00000", totalSize);
796 return topAddr - totalSize;
797 }
798 else if ( arch == CPU_TYPE_POWERPC64 ) {
799 return 0x200000000ULL;
800 }
801 else if ( arch == CPU_TYPE_X86_64 ) {
802 return 0x200000000ULL;
803 }
804 else
805 throw "unknown architecture";
806 }
807 }
808
809 static void usage()
810 {
811 fprintf(stderr, "rebase [-low_address] [-high_address] [-v] [-arch <arch>] files...\n");
812 }
813
814
815 int main(int argc, const char* argv[])
816 {
817 std::vector<fileInfo> files;
818 std::set<cpu_type_t> onlyArchs;
819 uint64_t lowAddress = 0;
820 uint64_t highAddress = 0;
821
822 try {
823 // parse command line options
824 char* endptr;
825 for(int i=1; i < argc; ++i) {
826 const char* arg = argv[i];
827 if ( arg[0] == '-' ) {
828 if ( strcmp(arg, "-v") == 0 ) {
829 verbose = true;
830 }
831 else if ( strcmp(arg, "-low_address") == 0 ) {
832 lowAddress = strtoull(argv[++i], &endptr, 16);
833 }
834 else if ( strcmp(arg, "-high_address") == 0 ) {
835 highAddress = strtoull(argv[++i], &endptr, 16);
836 }
837 else if ( strcmp(arg, "-arch") == 0 ) {
838 const char* arch = argv[++i];
839 if ( strcmp(arch, "ppc") == 0 )
840 onlyArchs.insert(CPU_TYPE_POWERPC);
841 else if ( strcmp(arch, "ppc64") == 0 )
842 onlyArchs.insert(CPU_TYPE_POWERPC64);
843 else if ( strcmp(arch, "i386") == 0 )
844 onlyArchs.insert(CPU_TYPE_I386);
845 else if ( strcmp(arch, "x86_64") == 0 )
846 onlyArchs.insert(CPU_TYPE_X86_64);
847 else
848 throwf("unknown architecture %s", arch);
849 }
850 else {
851 usage();
852 throwf("unknown option: %s\n", arg);
853 }
854 }
855 else {
856 files.push_back(fileInfo(arg));
857 }
858 }
859
860 if ( files.size() == 0 )
861 throw "no files specified";
862
863 // use all architectures if no restrictions specified
864 if ( onlyArchs.size() == 0 ) {
865 onlyArchs.insert(CPU_TYPE_POWERPC);
866 onlyArchs.insert(CPU_TYPE_POWERPC64);
867 onlyArchs.insert(CPU_TYPE_I386);
868 onlyArchs.insert(CPU_TYPE_X86_64);
869 }
870
871 // scan files and collect sizes
872 for(std::vector<fileInfo>::iterator it=files.begin(); it != files.end(); ++it) {
873 setSizes(*it, onlyArchs);
874 }
875
876 // assign new base address for each arch
877 for(std::set<cpu_type_t>::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) {
878 cpu_type_t arch = *ait;
879 uint64_t baseAddress = startAddress(arch, files, lowAddress, highAddress);
880 for(std::vector<fileInfo>::iterator fit=files.begin(); fit != files.end(); ++fit) {
881 fileInfo& fi = *fit;
882 for(std::vector<archInfo>::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) {
883 if ( fait->arch == arch ) {
884 fait->newBase = baseAddress;
885 baseAddress += fait->vmSize;
886 baseAddress = (baseAddress + 4095) & (-4096); // page align
887 }
888 }
889 }
890 }
891
892 // rebase each file if it contains something rebaseable
893 for(std::vector<fileInfo>::iterator it=files.begin(); it != files.end(); ++it) {
894 fileInfo& fi = *it;
895 if ( fi.archs.size() > 0 )
896 rebase(fi);
897 }
898
899 }
900 catch (const char* msg) {
901 fprintf(stderr, "rebase failed: %s\n", msg);
902 return 1;
903 }
904
905 return 0;
906 }
907
908
909