]> git.saurik.com Git - apple/dyld.git/blob - launch-cache/MachORebaser.hpp
e3c187fefcc3751775c494dc45189c779c0b7290
[apple/dyld.git] / launch-cache / MachORebaser.hpp
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 #ifndef __MACHO_REBASER__
26 #define __MACHO_REBASER__
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/mman.h>
31 #include <mach/mach.h>
32 #include <limits.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <unistd.h>
38 #include <mach-o/loader.h>
39 #include <mach-o/fat.h>
40 #include <mach-o/reloc.h>
41 #include <mach-o/ppc/reloc.h>
42 #include <mach-o/x86_64/reloc.h>
43 #include <vector>
44 #include <set>
45
46 #include "MachOFileAbstraction.hpp"
47 #include "Architectures.hpp"
48 #include "MachOLayout.hpp"
49
50
51
52 class AbstractRebaser
53 {
54 public:
55 virtual cpu_type_t getArchitecture() const = 0;
56 virtual uint64_t getBaseAddress() const = 0;
57 virtual uint64_t getVMSize() const = 0;
58 virtual void rebase() = 0;
59 };
60
61
62 template <typename A>
63 class Rebaser : public AbstractRebaser
64 {
65 public:
66 Rebaser(const MachOLayoutAbstraction&);
67 virtual ~Rebaser() {}
68
69 virtual cpu_type_t getArchitecture() const;
70 virtual uint64_t getBaseAddress() const;
71 virtual uint64_t getVMSize() const;
72 virtual void rebase();
73
74 protected:
75 typedef typename A::P P;
76 typedef typename A::P::E E;
77 typedef typename A::P::uint_t pint_t;
78
79 pint_t* mappedAddressForNewAddress(pint_t vmaddress);
80 pint_t getSlideForNewAddress(pint_t newAddress);
81
82 private:
83 pint_t calculateRelocBase();
84 void adjustLoadCommands();
85 void adjustSymbolTable();
86 void adjustDATA();
87 void adjustCode();
88 void adjustSegmentLoadCommand(macho_segment_command<P>* seg);
89 pint_t getSlideForVMAddress(pint_t vmaddress);
90 pint_t* mappedAddressForVMAddress(pint_t vmaddress);
91 pint_t* mappedAddressForRelocAddress(pint_t r_address);
92 void adjustRelocBaseAddresses();
93 const uint8_t* doCodeUpdateForEachULEB128Address(const uint8_t* p, uint8_t kind, uint64_t orgBaseAddress, int64_t codeToDataDelta, int64_t codeToImportDelta);
94 void doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta);
95 void doLocalRelocation(const macho_relocation_info<P>* reloc);
96 bool unequalSlides() const;
97
98 protected:
99 const macho_header<P>* fHeader;
100 uint8_t* fLinkEditBase; // add file offset to this to get linkedit content
101 const MachOLayoutAbstraction& fLayout;
102 private:
103 pint_t fOrignalVMRelocBaseAddress; // add reloc address to this to get original address reloc referred to
104 bool fSplittingSegments;
105 };
106
107
108
109 template <typename A>
110 Rebaser<A>::Rebaser(const MachOLayoutAbstraction& layout)
111 : fLayout(layout), fOrignalVMRelocBaseAddress(NULL), fLinkEditBase(NULL), fSplittingSegments(false)
112 {
113 fHeader = (const macho_header<P>*)fLayout.getSegments()[0].mappedAddress();
114 switch ( fHeader->filetype() ) {
115 case MH_DYLIB:
116 case MH_BUNDLE:
117 break;
118 default:
119 throw "file is not a dylib or bundle";
120 }
121
122 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
123 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
124 const MachOLayoutAbstraction::Segment& seg = *it;
125 if ( strcmp(seg.name(), "__LINKEDIT") == 0 ) {
126 fLinkEditBase = (uint8_t*)seg.mappedAddress() - seg.fileOffset();
127 break;
128 }
129 }
130 if ( fLinkEditBase == NULL )
131 throw "no __LINKEDIT segment";
132
133 fOrignalVMRelocBaseAddress = calculateRelocBase();
134
135 fSplittingSegments = layout.hasSplitSegInfo() && this->unequalSlides();
136 }
137
138 template <> cpu_type_t Rebaser<ppc>::getArchitecture() const { return CPU_TYPE_POWERPC; }
139 template <> cpu_type_t Rebaser<ppc64>::getArchitecture() const { return CPU_TYPE_POWERPC64; }
140 template <> cpu_type_t Rebaser<x86>::getArchitecture() const { return CPU_TYPE_I386; }
141 template <> cpu_type_t Rebaser<x86_64>::getArchitecture() const { return CPU_TYPE_X86_64; }
142
143 template <typename A>
144 bool Rebaser<A>::unequalSlides() const
145 {
146 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
147 uint64_t slide = segments[0].newAddress() - segments[0].address();
148 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
149 const MachOLayoutAbstraction::Segment& seg = *it;
150 if ( (seg.newAddress() - seg.address()) != slide )
151 return true;
152 }
153 return false;
154 }
155
156 template <typename A>
157 uint64_t Rebaser<A>::getBaseAddress() const
158 {
159 return fLayout.getSegments()[0].address();
160 }
161
162 template <typename A>
163 uint64_t Rebaser<A>::getVMSize() const
164 {
165 uint64_t highestVMAddress = 0;
166 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
167 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
168 const MachOLayoutAbstraction::Segment& seg = *it;
169 if ( seg.address() > highestVMAddress )
170 highestVMAddress = seg.address();
171 }
172 return (((highestVMAddress - getBaseAddress()) + 4095) & (-4096));
173 }
174
175
176
177 template <typename A>
178 void Rebaser<A>::rebase()
179 {
180 // update writable segments that have internal pointers
181 this->adjustDATA();
182
183 // if splitting segments, update code-to-data references
184 this->adjustCode();
185
186 // change address on relocs now that segments are split
187 this->adjustRelocBaseAddresses();
188
189 // update load commands
190 this->adjustLoadCommands();
191
192 // update symbol table
193 this->adjustSymbolTable();
194 }
195
196 template <>
197 void Rebaser<x86>::adjustSegmentLoadCommand(macho_segment_command<P>* seg)
198 {
199 // __IMPORT segments are not-writable in shared cache
200 if ( strcmp(seg->segname(), "__IMPORT") == 0 )
201 seg->set_initprot(VM_PROT_READ|VM_PROT_EXECUTE);
202 }
203
204 template <typename A>
205 void Rebaser<A>::adjustSegmentLoadCommand(macho_segment_command<P>* seg)
206 {
207 }
208
209
210 template <typename A>
211 void Rebaser<A>::adjustLoadCommands()
212 {
213 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
214 const uint32_t cmd_count = fHeader->ncmds();
215 const macho_load_command<P>* cmd = cmds;
216 for (uint32_t i = 0; i < cmd_count; ++i) {
217 switch ( cmd->cmd() ) {
218 case LC_ID_DYLIB:
219 if ( (fHeader->flags() & MH_PREBOUND) != 0 ) {
220 // clear timestamp so that any prebound clients are invalidated
221 macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
222 dylib->set_timestamp(1);
223 }
224 break;
225 case LC_LOAD_DYLIB:
226 case LC_LOAD_WEAK_DYLIB:
227 case LC_REEXPORT_DYLIB:
228 if ( (fHeader->flags() & MH_PREBOUND) != 0 ) {
229 // clear expected timestamps so that this image will load with invalid prebinding
230 macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
231 dylib->set_timestamp(2);
232 }
233 break;
234 case macho_routines_command<P>::CMD:
235 // update -init command
236 {
237 struct macho_routines_command<P>* routines = (struct macho_routines_command<P>*)cmd;
238 routines->set_init_address(routines->init_address() + this->getSlideForVMAddress(routines->init_address()));
239 }
240 break;
241 case macho_segment_command<P>::CMD:
242 // update segment commands
243 {
244 macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
245 this->adjustSegmentLoadCommand(seg);
246 pint_t slide = this->getSlideForVMAddress(seg->vmaddr());
247 seg->set_vmaddr(seg->vmaddr() + slide);
248 macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
249 macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
250 for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
251 sect->set_addr(sect->addr() + slide);
252 }
253 }
254 break;
255 }
256 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
257 }
258 }
259
260
261
262 template <typename A>
263 typename A::P::uint_t Rebaser<A>::getSlideForVMAddress(pint_t vmaddress)
264 {
265 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
266 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
267 const MachOLayoutAbstraction::Segment& seg = *it;
268 if ( (seg.address() <= vmaddress) && (seg.size() != 0) && ((vmaddress < (seg.address()+seg.size())) || (seg.address() == vmaddress)) ) {
269 return seg.newAddress() - seg.address();
270 }
271 }
272 throwf("vm address 0x%08llX not found", (uint64_t)vmaddress);
273 }
274
275
276 template <typename A>
277 typename A::P::uint_t* Rebaser<A>::mappedAddressForVMAddress(pint_t vmaddress)
278 {
279 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
280 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
281 const MachOLayoutAbstraction::Segment& seg = *it;
282 if ( (seg.address() <= vmaddress) && (vmaddress < (seg.address()+seg.size())) ) {
283 return (pint_t*)((vmaddress - seg.address()) + (uint8_t*)seg.mappedAddress());
284 }
285 }
286 throwf("mappedAddressForVMAddress(0x%08llX) not found", (uint64_t)vmaddress);
287 }
288
289 template <typename A>
290 typename A::P::uint_t* Rebaser<A>::mappedAddressForNewAddress(pint_t vmaddress)
291 {
292 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
293 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
294 const MachOLayoutAbstraction::Segment& seg = *it;
295 if ( (seg.newAddress() <= vmaddress) && (vmaddress < (seg.newAddress()+seg.size())) ) {
296 return (pint_t*)((vmaddress - seg.newAddress()) + (uint8_t*)seg.mappedAddress());
297 }
298 }
299 throwf("mappedAddressForNewAddress(0x%08llX) not found", (uint64_t)vmaddress);
300 }
301
302 template <typename A>
303 typename A::P::uint_t Rebaser<A>::getSlideForNewAddress(pint_t newAddress)
304 {
305 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
306 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
307 const MachOLayoutAbstraction::Segment& seg = *it;
308 if ( (seg.newAddress() <= newAddress) && (newAddress < (seg.newAddress()+seg.size())) ) {
309 return seg.newAddress() - seg.address();
310 }
311 }
312 throwf("new address 0x%08llX not found", (uint64_t)newAddress);
313 }
314
315 template <typename A>
316 typename A::P::uint_t* Rebaser<A>::mappedAddressForRelocAddress(pint_t r_address)
317 {
318 return this->mappedAddressForVMAddress(r_address + fOrignalVMRelocBaseAddress);
319 }
320
321
322 template <typename A>
323 void Rebaser<A>::adjustSymbolTable()
324 {
325 const macho_dysymtab_command<P>* dysymtab = NULL;
326 macho_nlist<P>* symbolTable = NULL;
327
328 // get symbol table info
329 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
330 const uint32_t cmd_count = fHeader->ncmds();
331 const macho_load_command<P>* cmd = cmds;
332 for (uint32_t i = 0; i < cmd_count; ++i) {
333 switch (cmd->cmd()) {
334 case LC_SYMTAB:
335 {
336 const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd;
337 symbolTable = (macho_nlist<P>*)(&fLinkEditBase[symtab->symoff()]);
338 }
339 break;
340 case LC_DYSYMTAB:
341 dysymtab = (macho_dysymtab_command<P>*)cmd;
342 break;
343 }
344 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
345 }
346
347 // walk all exports and slide their n_value
348 macho_nlist<P>* lastExport = &symbolTable[dysymtab->iextdefsym()+dysymtab->nextdefsym()];
349 for (macho_nlist<P>* entry = &symbolTable[dysymtab->iextdefsym()]; entry < lastExport; ++entry) {
350 if ( (entry->n_type() & N_TYPE) == N_SECT )
351 entry->set_n_value(entry->n_value() + this->getSlideForVMAddress(entry->n_value()));
352 }
353
354 // walk all local symbols and slide their n_value (don't adjust and stabs)
355 macho_nlist<P>* lastLocal = &symbolTable[dysymtab->ilocalsym()+dysymtab->nlocalsym()];
356 for (macho_nlist<P>* entry = &symbolTable[dysymtab->ilocalsym()]; entry < lastLocal; ++entry) {
357 if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) )
358 entry->set_n_value(entry->n_value() + this->getSlideForVMAddress(entry->n_value()));
359 }
360 }
361
362
363
364
365 template <typename A>
366 void Rebaser<A>::doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta)
367 {
368 //fprintf(stderr, "doCodeUpdate(kind=%d, address=0x%0llX, dataDelta=0x%08llX, importDelta=0x%08llX)\n", kind, address, codeToDataDelta, codeToImportDelta);
369 uint32_t* p;
370 uint32_t instruction;
371 uint32_t value;
372 uint64_t value64;
373 switch (kind) {
374 case 1: // 32-bit pointer
375 p = (uint32_t*)mappedAddressForVMAddress(address);
376 value = A::P::E::get32(*p);
377 value += codeToDataDelta;
378 A::P::E::set32(*p, value);
379 break;
380 case 2: // 64-bit pointer
381 p = (uint32_t*)mappedAddressForVMAddress(address);
382 value64 = A::P::E::get64(*(uint64_t*)p);
383 value64 += codeToDataDelta;
384 A::P::E::set64(*(uint64_t*)p, value64);
385 break;
386 case 3: // used only for ppc/ppc64, an instruction that sets the hi16 of a register
387 // adjust low 16 bits of instruction which contain hi16 of distance to something in DATA
388 if ( (codeToDataDelta & 0xFFFF) != 0 )
389 throwf("codeToDataDelta=0x%0llX is not a multiple of 64K", codeToDataDelta);
390 p = (uint32_t*)mappedAddressForVMAddress(address);
391 instruction = BigEndian::get32(*p);
392 {
393 uint16_t originalLo16 = instruction & 0x0000FFFF;
394 uint16_t delta64Ks = codeToDataDelta >> 16;
395 instruction = (instruction & 0xFFFF0000) | ((originalLo16+delta64Ks) & 0x0000FFFF);
396 }
397 BigEndian::set32(*p, instruction);
398 break;
399 case 4: // only used for i386, a reference to something in the IMPORT segment
400 p = (uint32_t*)mappedAddressForVMAddress(address);
401 value = A::P::E::get32(*p);
402 value += codeToImportDelta;
403 A::P::E::set32(*p, value);
404 break;
405 default:
406 throwf("invalid kind=%d in split seg info", kind);
407 }
408 }
409
410 template <typename A>
411 const uint8_t* Rebaser<A>::doCodeUpdateForEachULEB128Address(const uint8_t* p, uint8_t kind, uint64_t orgBaseAddress, int64_t codeToDataDelta, int64_t codeToImportDelta)
412 {
413 uint64_t address = 0;
414 uint64_t delta = 0;
415 uint32_t shift = 0;
416 bool more = true;
417 do {
418 uint8_t byte = *p++;
419 delta |= ((byte & 0x7F) << shift);
420 shift += 7;
421 if ( byte < 0x80 ) {
422 if ( delta != 0 ) {
423 address += delta;
424 doCodeUpdate(kind, address+orgBaseAddress, codeToDataDelta, codeToImportDelta);
425 delta = 0;
426 shift = 0;
427 }
428 else {
429 more = false;
430 }
431 }
432 } while (more);
433 return p;
434 }
435
436 template <typename A>
437 void Rebaser<A>::adjustCode()
438 {
439 if ( fSplittingSegments ) {
440 // get uleb128 compressed runs of code addresses to update
441 const uint8_t* infoStart = NULL;
442 const uint8_t* infoEnd = NULL;
443 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
444 const uint32_t cmd_count = fHeader->ncmds();
445 const macho_load_command<P>* cmd = cmds;
446 for (uint32_t i = 0; i < cmd_count; ++i) {
447 switch (cmd->cmd()) {
448 case LC_SEGMENT_SPLIT_INFO:
449 {
450 const macho_linkedit_data_command<P>* segInfo = (macho_linkedit_data_command<P>*)cmd;
451 infoStart = &fLinkEditBase[segInfo->dataoff()];
452 infoEnd = &infoStart[segInfo->datasize()];
453 }
454 break;
455 }
456 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
457 }
458 // calculate how much we need to slide writable segments
459 const uint64_t orgBaseAddress = this->getBaseAddress();
460 int64_t codeToDataDelta = 0;
461 int64_t codeToImportDelta = 0;
462 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
463 const MachOLayoutAbstraction::Segment& codeSeg = segments[0];
464 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
465 const MachOLayoutAbstraction::Segment& dataSeg = *it;
466 if ( strcmp(dataSeg.name(), "__IMPORT") == 0 )
467 codeToImportDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address());
468 else if ( dataSeg.writable() )
469 codeToDataDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address());
470 }
471 // decompress and call doCodeUpdate() on each address
472 for(const uint8_t* p = infoStart; *p != 0;) {
473 uint8_t kind = *p++;
474 p = this->doCodeUpdateForEachULEB128Address(p, kind, orgBaseAddress, codeToDataDelta, codeToImportDelta);
475 }
476 }
477 }
478
479
480 template <typename A>
481 void Rebaser<A>::adjustDATA()
482 {
483 const macho_dysymtab_command<P>* dysymtab = NULL;
484
485 // get symbol table info
486 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
487 const uint32_t cmd_count = fHeader->ncmds();
488 const macho_load_command<P>* cmd = cmds;
489 for (uint32_t i = 0; i < cmd_count; ++i) {
490 switch (cmd->cmd()) {
491 case LC_DYSYMTAB:
492 dysymtab = (macho_dysymtab_command<P>*)cmd;
493 break;
494 }
495 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
496 }
497
498
499 // walk all local relocations and slide every pointer
500 const macho_relocation_info<P>* const relocsStart = (macho_relocation_info<P>*)(&fLinkEditBase[dysymtab->locreloff()]);
501 const macho_relocation_info<P>* const relocsEnd = &relocsStart[dysymtab->nlocrel()];
502 for (const macho_relocation_info<P>* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
503 this->doLocalRelocation(reloc);
504 }
505
506 // walk non-lazy-pointers and slide the ones that are LOCAL
507 cmd = cmds;
508 for (uint32_t i = 0; i < cmd_count; ++i) {
509 if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
510 const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
511 const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
512 const macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
513 const uint32_t* const indirectTable = (uint32_t*)(&fLinkEditBase[dysymtab->indirectsymoff()]);
514 for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
515 if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) {
516 const uint32_t indirectTableOffset = sect->reserved1();
517 uint32_t pointerCount = sect->size() / sizeof(pint_t);
518 pint_t* nonLazyPointerAddr = this->mappedAddressForVMAddress(sect->addr());
519 for (uint32_t j=0; j < pointerCount; ++j, ++nonLazyPointerAddr) {
520 if ( E::get32(indirectTable[indirectTableOffset + j]) == INDIRECT_SYMBOL_LOCAL ) {
521 pint_t value = A::P::getP(*nonLazyPointerAddr);
522 P::setP(*nonLazyPointerAddr, value + this->getSlideForVMAddress(value));
523 }
524 }
525 }
526 }
527 }
528 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
529 }
530 }
531
532
533 template <typename A>
534 void Rebaser<A>::adjustRelocBaseAddresses()
535 {
536 // split seg file alreday have reloc base of first writable segment
537 // only non-split-segs that are being split need this adjusted
538 if ( (fHeader->flags() & MH_SPLIT_SEGS) == 0 ) {
539
540 // get symbol table to find relocation records
541 const macho_dysymtab_command<P>* dysymtab = NULL;
542 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
543 const uint32_t cmd_count = fHeader->ncmds();
544 const macho_load_command<P>* cmd = cmds;
545 for (uint32_t i = 0; i < cmd_count; ++i) {
546 switch (cmd->cmd()) {
547 case LC_DYSYMTAB:
548 dysymtab = (macho_dysymtab_command<P>*)cmd;
549 break;
550 }
551 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
552 }
553
554 // get amount to adjust reloc address
555 int32_t relocAddressAdjust = 0;
556 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
557 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
558 const MachOLayoutAbstraction::Segment& seg = *it;
559 if ( seg.writable() ) {
560 relocAddressAdjust = seg.address() - segments[0].address();
561 break;
562 }
563 }
564
565 // walk all local relocations and adjust every address
566 macho_relocation_info<P>* const relocsStart = (macho_relocation_info<P>*)(&fLinkEditBase[dysymtab->locreloff()]);
567 macho_relocation_info<P>* const relocsEnd = &relocsStart[dysymtab->nlocrel()];
568 for (macho_relocation_info<P>* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
569 reloc->set_r_address(reloc->r_address()-relocAddressAdjust);
570 }
571
572 // walk all external relocations and adjust every address
573 macho_relocation_info<P>* const externRelocsStart = (macho_relocation_info<P>*)(&fLinkEditBase[dysymtab->extreloff()]);
574 macho_relocation_info<P>* const externRelocsEnd = &externRelocsStart[dysymtab->nextrel()];
575 for (macho_relocation_info<P>* reloc=externRelocsStart; reloc < externRelocsEnd; ++reloc) {
576 reloc->set_r_address(reloc->r_address()-relocAddressAdjust);
577 }
578 }
579 }
580
581 template <>
582 void Rebaser<x86_64>::adjustRelocBaseAddresses()
583 {
584 // x86_64 already have reloc base of first writable segment
585 }
586
587
588 template <>
589 void Rebaser<x86_64>::doLocalRelocation(const macho_relocation_info<x86_64::P>* reloc)
590 {
591 if ( reloc->r_type() == X86_64_RELOC_UNSIGNED ) {
592 pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address());
593 pint_t value = P::getP(*addr);
594 P::setP(*addr, value + this->getSlideForVMAddress(value));
595 }
596 else {
597 throw "invalid relocation type";
598 }
599 }
600
601 template <>
602 void Rebaser<ppc>::doLocalRelocation(const macho_relocation_info<P>* reloc)
603 {
604 if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
605 if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) {
606 pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address());
607 pint_t value = P::getP(*addr);
608 P::setP(*addr, value + this->getSlideForVMAddress(value));
609 }
610 }
611 else {
612 macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
613 if ( sreloc->r_type() == PPC_RELOC_PB_LA_PTR ) {
614 sreloc->set_r_value( sreloc->r_value() + this->getSlideForVMAddress(sreloc->r_value()) );
615 }
616 else {
617 throw "cannot rebase final linked image with scattered relocations";
618 }
619 }
620 }
621
622 template <>
623 void Rebaser<x86>::doLocalRelocation(const macho_relocation_info<P>* reloc)
624 {
625 if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
626 if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) {
627 pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address());
628 pint_t value = P::getP(*addr);
629 P::setP(*addr, value + this->getSlideForVMAddress(value));
630 }
631 }
632 else {
633 macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
634 if ( sreloc->r_type() == GENERIC_RELOC_PB_LA_PTR ) {
635 sreloc->set_r_value( sreloc->r_value() + this->getSlideForVMAddress(sreloc->r_value()) );
636 }
637 else {
638 throw "cannot rebase final linked image with scattered relocations";
639 }
640 }
641 }
642
643 template <typename A>
644 void Rebaser<A>::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 = this->mappedAddressForRelocAddress(reloc->r_address());
649 pint_t value = P::getP(*addr);
650 P::setP(*addr, value + this->getSlideForVMAddress(value));
651 }
652 }
653 else {
654 throw "cannot rebase final linked image with scattered relocations";
655 }
656 }
657
658
659 template <typename A>
660 typename A::P::uint_t Rebaser<A>::calculateRelocBase()
661 {
662 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
663 if ( fHeader->flags() & MH_SPLIT_SEGS ) {
664 // reloc addresses are from the start of the first writable segment
665 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
666 const MachOLayoutAbstraction::Segment& seg = *it;
667 if ( seg.writable() ) {
668 // found first writable segment
669 return seg.address();
670 }
671 }
672 throw "no writable segment";
673 }
674 else {
675 // reloc addresses are from the start of the mapped file (base address)
676 return segments[0].address();
677 }
678 }
679
680 template <>
681 ppc64::P::uint_t Rebaser<ppc64>::calculateRelocBase()
682 {
683 // reloc addresses either:
684 // 1) from the first segment vmaddr if no writable segment is > 4GB from first segment vmaddr
685 // 2) from start of first writable segment
686 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
687 uint64_t threshold = segments[0].address() + 0x100000000ULL;
688 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
689 const MachOLayoutAbstraction::Segment& seg = *it;
690 if ( seg.writable() && (seg.address()+seg.size()) > threshold ) {
691 // found writable segment with address > 4GB past base address
692 return seg.address();
693 }
694 }
695 // just use base address
696 return segments[0].address();
697 }
698
699 template <>
700 x86_64::P::uint_t Rebaser<x86_64>::calculateRelocBase()
701 {
702 // reloc addresses are always based from the start of the first writable segment
703 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
704 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
705 const MachOLayoutAbstraction::Segment& seg = *it;
706 if ( seg.writable() ) {
707 // found first writable segment
708 return seg.address();
709 }
710 }
711 throw "no writable segment";
712 }
713
714
715 #if 0
716 class MultiArchRebaser
717 {
718 public:
719 MultiArchRebaser::MultiArchRebaser(const char* path, bool writable=false)
720 : fMappingAddress(0), fFileSize(0)
721 {
722 // map in whole file
723 int fd = ::open(path, (writable ? O_RDWR : O_RDONLY), 0);
724 if ( fd == -1 )
725 throwf("can't open file, errno=%d", errno);
726 struct stat stat_buf;
727 if ( fstat(fd, &stat_buf) == -1)
728 throwf("can't stat open file %s, errno=%d", path, errno);
729 if ( stat_buf.st_size < 20 )
730 throwf("file too small %s", path);
731 const int prot = writable ? (PROT_READ | PROT_WRITE) : PROT_READ;
732 const int flags = writable ? (MAP_FILE | MAP_SHARED) : (MAP_FILE | MAP_PRIVATE);
733 uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, prot, flags, fd, 0);
734 if ( p == (uint8_t*)(-1) )
735 throwf("can't map file %s, errno=%d", path, errno);
736 ::close(fd);
737
738 // if fat file, process each architecture
739 const fat_header* fh = (fat_header*)p;
740 const mach_header* mh = (mach_header*)p;
741 if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
742 // Fat header is always big-endian
743 const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
744 for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
745 uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset);
746 try {
747 switch ( OSSwapBigToHostInt32(archs[i].cputype) ) {
748 case CPU_TYPE_POWERPC:
749 fRebasers.push_back(new Rebaser<ppc>(&p[fileOffset]));
750 break;
751 case CPU_TYPE_POWERPC64:
752 fRebasers.push_back(new Rebaser<ppc64>(&p[fileOffset]));
753 break;
754 case CPU_TYPE_I386:
755 fRebasers.push_back(new Rebaser<x86>(&p[fileOffset]));
756 break;
757 case CPU_TYPE_X86_64:
758 fRebasers.push_back(new Rebaser<x86_64>(&p[fileOffset]));
759 break;
760 default:
761 throw "unknown file format";
762 }
763 }
764 catch (const char* msg) {
765 fprintf(stderr, "rebase warning: %s for %s\n", msg, path);
766 }
767 }
768 }
769 else {
770 try {
771 if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) {
772 fRebasers.push_back(new Rebaser<ppc>(mh));
773 }
774 else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) {
775 fRebasers.push_back(new Rebaser<ppc64>(mh));
776 }
777 else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) {
778 fRebasers.push_back(new Rebaser<x86>(mh));
779 }
780 else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) {
781 fRebasers.push_back(new Rebaser<x86_64>(mh));
782 }
783 else {
784 throw "unknown file format";
785 }
786 }
787 catch (const char* msg) {
788 fprintf(stderr, "rebase warning: %s for %s\n", msg, path);
789 }
790 }
791
792 fMappingAddress = p;
793 fFileSize = stat_buf.st_size;
794 }
795
796
797 ~MultiArchRebaser() {::munmap(fMappingAddress, fFileSize); }
798
799 const std::vector<AbstractRebaser*>& getArchs() const { return fRebasers; }
800 void commit() { ::msync(fMappingAddress, fFileSize, MS_ASYNC); }
801
802 private:
803 std::vector<AbstractRebaser*> fRebasers;
804 void* fMappingAddress;
805 uint64_t fFileSize;
806 };
807 #endif
808
809
810 #endif // __MACHO_REBASER__
811
812
813
814