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