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