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