]> git.saurik.com Git - apple/ld64.git/blame - src/other/rebase.cpp
ld64-253.6.tar.gz
[apple/ld64.git] / src / other / rebase.cpp
CommitLineData
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
43static bool verbose = false;
44
45__attribute__((noreturn))
46void 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
59class AbstractRebaser
60{
61public:
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
69template <typename A>
70class Rebaser : public AbstractRebaser
71{
72public:
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
81private:
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
105class MultiArchRebaser
106{
107public:
108 MultiArchRebaser(const char* path, bool writable=false);
109 ~MultiArchRebaser();
110
111 const std::vector<AbstractRebaser*>& getArchs() const { return fRebasers; }
112 void commit();
113
114private:
115 std::vector<AbstractRebaser*> fRebasers;
116 void* fMappingAddress;
117 uint64_t fFileSize;
118};
119
120
121
122MultiArchRebaser::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
206MultiArchRebaser::~MultiArchRebaser()
207{
208 ::munmap(fMappingAddress, fFileSize);
209}
210
211void MultiArchRebaser::commit()
212{
213 ::msync(fMappingAddress, fFileSize, MS_ASYNC);
214}
215
216
217
218template <typename A>
219Rebaser<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
235template <> cpu_type_t Rebaser<ppc>::getArchitecture() const { return CPU_TYPE_POWERPC; }
236template <> cpu_type_t Rebaser<ppc64>::getArchitecture() const { return CPU_TYPE_POWERPC64; }
237template <> cpu_type_t Rebaser<x86>::getArchitecture() const { return CPU_TYPE_I386; }
238template <> cpu_type_t Rebaser<x86_64>::getArchitecture() const { return CPU_TYPE_X86_64; }
2f2f92e4 239template <> cpu_type_t Rebaser<arm>::getArchitecture() const { return CPU_TYPE_ARM; }
69a49097
A
240
241template <typename A>
242uint64_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
260template <typename A>
261uint64_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
281template <typename A>
282void 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
303template <typename A>
304void 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 = &sectionsStart[seg->nsects()];
342 for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
343 sect->set_addr(sect->addr() + fSlide);
344 }
345 }
346 break;
347 }
348 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
349 }
350}
351
352
353template <typename A>
354void 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
374template <typename A>
375void 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