]>
Commit | Line | Data |
---|---|---|
69a49097 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
d425e388 | 3 | * Copyright (c) 2006-2012 Apple Inc. All rights reserved. |
69a49097 A |
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> | |
d425e388 | 35 | #include <stdlib.h> |
69a49097 A |
36 | #include <vector> |
37 | #include <set> | |
38 | ||
2f2f92e4 | 39 | |
69a49097 A |
40 | #include "MachOFileAbstraction.hpp" |
41 | #include "Architectures.hpp" | |
42 | ||
69a49097 A |
43 | static bool verbose = false; |
44 | ||
45 | __attribute__((noreturn)) | |
46 | void throwf(const char* format, ...) | |
47 | { | |
48 | va_list list; | |
49 | char* p; | |
50 | va_start(list, format); | |
51 | vasprintf(&p, format, list); | |
52 | va_end(list); | |
53 | ||
54 | const char* t = p; | |
55 | throw t; | |
56 | } | |
57 | ||
58 | ||
59 | class AbstractRebaser | |
60 | { | |
61 | public: | |
62 | virtual cpu_type_t getArchitecture() const = 0; | |
63 | virtual uint64_t getBaseAddress() const = 0; | |
64 | virtual uint64_t getVMSize() const = 0; | |
65 | virtual void setBaseAddress(uint64_t) = 0; | |
66 | }; | |
67 | ||
68 | ||
69 | template <typename A> | |
70 | class Rebaser : public AbstractRebaser | |
71 | { | |
72 | public: | |
73 | Rebaser(const void* machHeader); | |
74 | virtual ~Rebaser() {} | |
75 | ||
76 | virtual cpu_type_t getArchitecture() const; | |
77 | virtual uint64_t getBaseAddress() const; | |
78 | virtual uint64_t getVMSize() const; | |
79 | virtual void setBaseAddress(uint64_t); | |
80 | ||
81 | private: | |
82 | typedef typename A::P P; | |
83 | typedef typename A::P::E E; | |
84 | typedef typename A::P::uint_t pint_t; | |
85 | ||
86 | struct vmmap { pint_t vmaddr; pint_t vmsize; pint_t fileoff; }; | |
87 | ||
88 | void setRelocBase(); | |
89 | void buildSectionTable(); | |
90 | void adjustLoadCommands(); | |
91 | void adjustSymbolTable(); | |
92 | void adjustDATA(); | |
93 | void doLocalRelocation(const macho_relocation_info<P>* reloc); | |
94 | pint_t* mappedAddressForVMAddress(uint32_t vmaddress); | |
55e3d2f6 | 95 | void rebaseAt(int segIndex, uint64_t offset, uint8_t type); |
69a49097 A |
96 | |
97 | const macho_header<P>* fHeader; | |
98 | pint_t fOrignalVMRelocBaseAddress; | |
99 | pint_t fSlide; | |
69a49097 A |
100 | std::vector<vmmap> fVMMApping; |
101 | }; | |
102 | ||
103 | ||
104 | ||
105 | class MultiArchRebaser | |
106 | { | |
107 | public: | |
108 | MultiArchRebaser(const char* path, bool writable=false); | |
109 | ~MultiArchRebaser(); | |
110 | ||
111 | const std::vector<AbstractRebaser*>& getArchs() const { return fRebasers; } | |
112 | void commit(); | |
113 | ||
114 | private: | |
115 | std::vector<AbstractRebaser*> fRebasers; | |
116 | void* fMappingAddress; | |
117 | uint64_t fFileSize; | |
118 | }; | |
119 | ||
120 | ||
121 | ||
122 | MultiArchRebaser::MultiArchRebaser(const char* path, bool writable) | |
123 | : fMappingAddress(0), fFileSize(0) | |
124 | { | |
125 | // map in whole file | |
126 | int fd = ::open(path, (writable ? O_RDWR : O_RDONLY), 0); | |
127 | if ( fd == -1 ) | |
55e3d2f6 | 128 | throwf("can't open file %s, errno=%d", path, errno); |
69a49097 A |
129 | struct stat stat_buf; |
130 | if ( fstat(fd, &stat_buf) == -1) | |
131 | throwf("can't stat open file %s, errno=%d", path, errno); | |
132 | if ( stat_buf.st_size < 20 ) | |
133 | throwf("file too small %s", path); | |
134 | const int prot = writable ? (PROT_READ | PROT_WRITE) : PROT_READ; | |
135 | const int flags = writable ? (MAP_FILE | MAP_SHARED) : (MAP_FILE | MAP_PRIVATE); | |
136 | uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, prot, flags, fd, 0); | |
137 | if ( p == (uint8_t*)(-1) ) | |
138 | throwf("can't map file %s, errno=%d", path, errno); | |
139 | ::close(fd); | |
140 | ||
141 | // if fat file, process each architecture | |
142 | const fat_header* fh = (fat_header*)p; | |
143 | const mach_header* mh = (mach_header*)p; | |
144 | if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { | |
145 | // Fat header is always big-endian | |
146 | const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); | |
147 | for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { | |
148 | uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset); | |
149 | try { | |
150 | switch ( OSSwapBigToHostInt32(archs[i].cputype) ) { | |
151 | case CPU_TYPE_POWERPC: | |
152 | fRebasers.push_back(new Rebaser<ppc>(&p[fileOffset])); | |
153 | break; | |
154 | case CPU_TYPE_POWERPC64: | |
155 | fRebasers.push_back(new Rebaser<ppc64>(&p[fileOffset])); | |
156 | break; | |
157 | case CPU_TYPE_I386: | |
158 | fRebasers.push_back(new Rebaser<x86>(&p[fileOffset])); | |
159 | break; | |
160 | case CPU_TYPE_X86_64: | |
161 | fRebasers.push_back(new Rebaser<x86_64>(&p[fileOffset])); | |
162 | break; | |
2f2f92e4 A |
163 | case CPU_TYPE_ARM: |
164 | fRebasers.push_back(new Rebaser<arm>(&p[fileOffset])); | |
165 | break; | |
69a49097 A |
166 | default: |
167 | throw "unknown file format"; | |
168 | } | |
169 | } | |
170 | catch (const char* msg) { | |
171 | fprintf(stderr, "rebase warning: %s for %s\n", msg, path); | |
172 | } | |
173 | } | |
174 | } | |
175 | else { | |
176 | try { | |
177 | if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) { | |
178 | fRebasers.push_back(new Rebaser<ppc>(mh)); | |
179 | } | |
180 | else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) { | |
181 | fRebasers.push_back(new Rebaser<ppc64>(mh)); | |
182 | } | |
183 | else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) { | |
184 | fRebasers.push_back(new Rebaser<x86>(mh)); | |
185 | } | |
186 | else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) { | |
187 | fRebasers.push_back(new Rebaser<x86_64>(mh)); | |
188 | } | |
2f2f92e4 A |
189 | else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) { |
190 | fRebasers.push_back(new Rebaser<arm>(mh)); | |
191 | } | |
69a49097 A |
192 | else { |
193 | throw "unknown file format"; | |
194 | } | |
195 | } | |
196 | catch (const char* msg) { | |
197 | fprintf(stderr, "rebase warning: %s for %s\n", msg, path); | |
198 | } | |
199 | } | |
200 | ||
201 | fMappingAddress = p; | |
202 | fFileSize = stat_buf.st_size; | |
203 | } | |
204 | ||
205 | ||
206 | MultiArchRebaser::~MultiArchRebaser() | |
207 | { | |
208 | ::munmap(fMappingAddress, fFileSize); | |
209 | } | |
210 | ||
211 | void MultiArchRebaser::commit() | |
212 | { | |
213 | ::msync(fMappingAddress, fFileSize, MS_ASYNC); | |
214 | } | |
215 | ||
216 | ||
217 | ||
218 | template <typename A> | |
219 | Rebaser<A>::Rebaser(const void* machHeader) | |
220 | : fHeader((const macho_header<P>*)machHeader) | |
221 | { | |
222 | switch ( fHeader->filetype() ) { | |
223 | case MH_DYLIB: | |
224 | if ( (fHeader->flags() & MH_SPLIT_SEGS) != 0 ) | |
225 | throw "split-seg dylibs cannot be rebased"; | |
226 | break; | |
227 | case MH_BUNDLE: | |
228 | break; | |
229 | default: | |
230 | throw "file is not a dylib or bundle"; | |
231 | } | |
232 | ||
233 | } | |
234 | ||
235 | template <> cpu_type_t Rebaser<ppc>::getArchitecture() const { return CPU_TYPE_POWERPC; } | |
236 | template <> cpu_type_t Rebaser<ppc64>::getArchitecture() const { return CPU_TYPE_POWERPC64; } | |
237 | template <> cpu_type_t Rebaser<x86>::getArchitecture() const { return CPU_TYPE_I386; } | |
238 | template <> cpu_type_t Rebaser<x86_64>::getArchitecture() const { return CPU_TYPE_X86_64; } | |
2f2f92e4 | 239 | template <> cpu_type_t Rebaser<arm>::getArchitecture() const { return CPU_TYPE_ARM; } |
69a49097 A |
240 | |
241 | template <typename A> | |
242 | uint64_t Rebaser<A>::getBaseAddress() const | |
243 | { | |
244 | uint64_t lowestSegmentAddress = LLONG_MAX; | |
245 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
246 | const uint32_t cmd_count = fHeader->ncmds(); | |
247 | const macho_load_command<P>* cmd = cmds; | |
248 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
249 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
250 | const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd; | |
251 | if ( segCmd->vmaddr() < lowestSegmentAddress ) { | |
252 | lowestSegmentAddress = segCmd->vmaddr(); | |
253 | } | |
254 | } | |
255 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
256 | } | |
257 | return lowestSegmentAddress; | |
258 | } | |
259 | ||
260 | template <typename A> | |
261 | uint64_t Rebaser<A>::getVMSize() const | |
262 | { | |
263 | const macho_segment_command<P>* highestSegmentCmd = NULL; | |
264 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
265 | const uint32_t cmd_count = fHeader->ncmds(); | |
266 | const macho_load_command<P>* cmd = cmds; | |
267 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
268 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
269 | const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd; | |
270 | if ( (highestSegmentCmd == NULL) || (segCmd->vmaddr() > highestSegmentCmd->vmaddr()) ) { | |
271 | highestSegmentCmd = segCmd; | |
272 | } | |
273 | } | |
274 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
275 | } | |
276 | ||
277 | return ((highestSegmentCmd->vmaddr() + highestSegmentCmd->vmsize() - this->getBaseAddress() + 4095) & (-4096)); | |
278 | } | |
279 | ||
280 | ||
281 | template <typename A> | |
282 | void Rebaser<A>::setBaseAddress(uint64_t addr) | |
283 | { | |
284 | // calculate slide | |
285 | fSlide = addr - this->getBaseAddress(); | |
286 | ||
287 | // compute base address for relocations | |
288 | this->setRelocBase(); | |
289 | ||
290 | // build cache of section index to section | |
291 | this->buildSectionTable(); | |
292 | ||
293 | // update load commands | |
294 | this->adjustLoadCommands(); | |
295 | ||
296 | // update symbol table | |
297 | this->adjustSymbolTable(); | |
298 | ||
299 | // update writable segments that have internal pointers | |
300 | this->adjustDATA(); | |
301 | } | |
302 | ||
303 | template <typename A> | |
304 | void Rebaser<A>::adjustLoadCommands() | |
305 | { | |
69a49097 A |
306 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); |
307 | const uint32_t cmd_count = fHeader->ncmds(); | |
308 | const macho_load_command<P>* cmd = cmds; | |
309 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
310 | switch ( cmd->cmd() ) { | |
311 | case LC_ID_DYLIB: | |
312 | if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { | |
313 | // clear timestamp so that any prebound clients are invalidated | |
314 | macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd; | |
315 | dylib->set_timestamp(1); | |
316 | } | |
317 | break; | |
318 | case LC_LOAD_DYLIB: | |
319 | case LC_LOAD_WEAK_DYLIB: | |
a645023d A |
320 | case LC_REEXPORT_DYLIB: |
321 | case LC_LOAD_UPWARD_DYLIB: | |
69a49097 A |
322 | if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { |
323 | // clear expected timestamps so that this image will load with invalid prebinding | |
324 | macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd; | |
325 | dylib->set_timestamp(2); | |
326 | } | |
327 | break; | |
328 | case macho_routines_command<P>::CMD: | |
329 | // update -init command | |
330 | { | |
a645023d | 331 | macho_routines_command<P>* routines = (macho_routines_command<P>*)cmd; |
69a49097 A |
332 | routines->set_init_address(routines->init_address() + fSlide); |
333 | } | |
334 | break; | |
335 | case macho_segment_command<P>::CMD: | |
336 | // update segment commands | |
337 | { | |
338 | macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd; | |
339 | seg->set_vmaddr(seg->vmaddr() + fSlide); | |
340 | macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>)); | |
341 | macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()]; | |
342 | for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) { | |
343 | sect->set_addr(sect->addr() + fSlide); | |
344 | } | |
345 | } | |
346 | break; | |
347 | } | |
348 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
349 | } | |
350 | } | |
351 | ||
352 | ||
353 | template <typename A> | |
354 | void Rebaser<A>::buildSectionTable() | |
355 | { | |
356 | // build vector of sections | |
357 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
358 | const uint32_t cmd_count = fHeader->ncmds(); | |
359 | const macho_load_command<P>* cmd = cmds; | |
360 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
361 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
362 | const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd; | |
363 | vmmap mapping; | |
364 | mapping.vmaddr = seg->vmaddr(); | |
365 | mapping.vmsize = seg->vmsize(); | |
366 | mapping.fileoff = seg->fileoff(); | |
367 | fVMMApping.push_back(mapping); | |
368 | } | |
369 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
370 | } | |
371 | } | |
372 | ||
373 | ||
374 | template <typename A> | |
375 | void Rebaser<A>::adjustSymbolTable() | |
376 | { | |
377 | const macho_dysymtab_command<P>* dysymtab = NULL; | |
378 | macho_nlist<P>* symbolTable = NULL; | |
a645023d | 379 | const char* strings = NULL; |
69a49097 A |
380 | |
381 | // get symbol table info | |
382 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
383 | const uint32_t cmd_count = fHeader->ncmds(); | |
384 | const macho_load_command<P>* cmd = cmds; | |
385 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
386 | switch (cmd->cmd()) { | |
387 | case LC_SYMTAB: | |
388 | { | |
389 | const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd; | |
390 | symbolTable = (macho_nlist<P>*)(((uint8_t*)fHeader) + symtab->symoff()); | |
a645023d A |
391 | strings = (char*)(((uint8_t*)fHeader) + symtab->stroff()); |
392 | } | |
69a49097 A |
393 | break; |
394 | case LC_DYSYMTAB: | |
395 | dysymtab = (macho_dysymtab_command<P>*)cmd; | |
396 | break; | |
397 | } | |
398 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
399 | } | |
400 | ||
401 | // walk all exports and slide their n_value | |
402 | macho_nlist<P>* lastExport = &symbolTable[dysymtab->iextdefsym()+dysymtab->nextdefsym()]; | |
403 | for (macho_nlist<P>* entry = &symbolTable[dysymtab->iextdefsym()]; entry < lastExport; ++entry) { | |
404 | if ( (entry->n_type() & N_TYPE) == N_SECT ) | |
405 | entry->set_n_value(entry->n_value() + fSlide); | |
406 | } | |
407 | ||
408 | // walk all local symbols and slide their n_value | |
409 | macho_nlist<P>* lastLocal = &symbolTable[dysymtab->ilocalsym()+dysymtab->nlocalsym()]; | |
410 | for (macho_nlist<P>* entry = &symbolTable[dysymtab->ilocalsym()]; entry < lastLocal; ++entry) { | |
a645023d | 411 | if ( ((entry->n_type() & N_STAB) == 0) && ((entry->n_type() & N_TYPE) == N_SECT) ) { |
69a49097 | 412 | entry->set_n_value(entry->n_value() + fSlide); |
a645023d A |
413 | } |
414 | else if ( entry->n_type() & N_STAB ) { | |
415 | // some stabs need to be slid too | |
416 | switch ( entry->n_type() ) { | |
417 | case N_FUN: | |
418 | // don't slide end-of-function FUN which is FUN with no string | |
419 | if ( (entry->n_strx() == 0) || (strings[entry->n_strx()] == '\0') ) | |
420 | break; | |
421 | case N_BNSYM: | |
422 | case N_STSYM: | |
423 | case N_LCSYM: | |
424 | entry->set_n_value(entry->n_value() + fSlide); | |
425 | break; | |
426 | } | |
427 | } | |
69a49097 | 428 | } |
74cfe461 A |
429 | |
430 |