]>
Commit | Line | Data |
---|---|---|
bac542e6 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. | |
4 | * | |
5 | * @APPLE_LICENSE_HEADER_START@ | |
6 | * | |
7 | * This file contains Original Code and/or Modifications of Original Code | |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
20 | * limitations under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | #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 = §ionsStart[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); | |
2028a915 A |
392 | { |
393 | uint16_t originalLo16 = instruction & 0x0000FFFF; | |
394 | uint16_t delta64Ks = codeToDataDelta >> 16; | |
395 | instruction = (instruction & 0xFFFF0000) | ((originalLo16+delta64Ks) & 0x0000FFFF); | |
396 | } | |
bac542e6 A |
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 = §ionsStart[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 |