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