]>
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> | |
bac542e6 | 41 | #include <mach-o/x86_64/reloc.h> |
39a8cd10 | 42 | #include <mach-o/arm/reloc.h> |
bac542e6 A |
43 | #include <vector> |
44 | #include <set> | |
45 | ||
46 | #include "MachOFileAbstraction.hpp" | |
47 | #include "Architectures.hpp" | |
48 | #include "MachOLayout.hpp" | |
39a8cd10 | 49 | #include "MachOTrie.hpp" |
bac542e6 A |
50 | |
51 | ||
52 | ||
53 | class AbstractRebaser | |
54 | { | |
55 | public: | |
56 | virtual cpu_type_t getArchitecture() const = 0; | |
57 | virtual uint64_t getBaseAddress() const = 0; | |
58 | virtual uint64_t getVMSize() const = 0; | |
df9d6cf7 | 59 | virtual bool rebase(std::vector<void*>&) = 0; |
bac542e6 A |
60 | }; |
61 | ||
62 | ||
63 | template <typename A> | |
64 | class Rebaser : public AbstractRebaser | |
65 | { | |
66 | public: | |
67 | Rebaser(const MachOLayoutAbstraction&); | |
68 | virtual ~Rebaser() {} | |
69 | ||
70 | virtual cpu_type_t getArchitecture() const; | |
71 | virtual uint64_t getBaseAddress() const; | |
72 | virtual uint64_t getVMSize() const; | |
df9d6cf7 | 73 | virtual bool rebase(std::vector<void*>&); |
bac542e6 A |
74 | |
75 | protected: | |
76 | typedef typename A::P P; | |
77 | typedef typename A::P::E E; | |
78 | typedef typename A::P::uint_t pint_t; | |
79 | ||
80 | pint_t* mappedAddressForNewAddress(pint_t vmaddress); | |
81 | pint_t getSlideForNewAddress(pint_t newAddress); | |
df9d6cf7 | 82 | |
bac542e6 | 83 | private: |
bac542e6 A |
84 | void adjustLoadCommands(); |
85 | void adjustSymbolTable(); | |
86 | void adjustDATA(); | |
87 | void adjustCode(); | |
412ebb8e | 88 | void applyRebaseInfo(std::vector<void*>& pointersInData); |
df9d6cf7 A |
89 | void adjustReferencesUsingInfoV2(std::vector<void*>& pointersInData); |
90 | void adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide, | |
91 | uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector<void*>& pointersInData); | |
92 | bool adjustExportInfo(); | |
412ebb8e | 93 | void doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersInData); |
bac542e6 | 94 | pint_t getSlideForVMAddress(pint_t vmaddress); |
19894a12 | 95 | pint_t maskedVMAddress(pint_t vmaddress); |
bac542e6 | 96 | pint_t* mappedAddressForVMAddress(pint_t vmaddress); |
bac542e6 A |
97 | const uint8_t* doCodeUpdateForEachULEB128Address(const uint8_t* p, uint8_t kind, uint64_t orgBaseAddress, int64_t codeToDataDelta, int64_t codeToImportDelta); |
98 | void doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta); | |
99 | void doLocalRelocation(const macho_relocation_info<P>* reloc); | |
100 | bool unequalSlides() const; | |
101 | ||
102 | protected: | |
103 | const macho_header<P>* fHeader; | |
104 | uint8_t* fLinkEditBase; // add file offset to this to get linkedit content | |
105 | const MachOLayoutAbstraction& fLayout; | |
106 | private: | |
39a8cd10 A |
107 | const macho_symtab_command<P>* fSymbolTable; |
108 | const macho_dysymtab_command<P>* fDynamicSymbolTable; | |
109 | const macho_dyld_info_command<P>* fDyldInfo; | |
df9d6cf7 | 110 | const macho_linkedit_data_command<P>* fSplitSegInfo; |
bac542e6 | 111 | bool fSplittingSegments; |
df9d6cf7 A |
112 | bool fHasSplitSegInfoV2; |
113 | std::vector<uint64_t> fSectionOffsetsInSegment; | |
bac542e6 A |
114 | }; |
115 | ||
116 | ||
bac542e6 A |
117 | template <typename A> |
118 | Rebaser<A>::Rebaser(const MachOLayoutAbstraction& layout) | |
df9d6cf7 A |
119 | : fLayout(layout), fLinkEditBase(0), fSymbolTable(NULL), fDynamicSymbolTable(NULL), |
120 | fDyldInfo(NULL), fSplitSegInfo(NULL), fSplittingSegments(false), fHasSplitSegInfoV2(false) | |
bac542e6 A |
121 | { |
122 | fHeader = (const macho_header<P>*)fLayout.getSegments()[0].mappedAddress(); | |
123 | switch ( fHeader->filetype() ) { | |
124 | case MH_DYLIB: | |
125 | case MH_BUNDLE: | |
126 | break; | |
127 | default: | |
128 | throw "file is not a dylib or bundle"; | |
129 | } | |
130 | ||
131 | const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments(); | |
132 | for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) { | |
133 | const MachOLayoutAbstraction::Segment& seg = *it; | |
134 | if ( strcmp(seg.name(), "__LINKEDIT") == 0 ) { | |
135 | fLinkEditBase = (uint8_t*)seg.mappedAddress() - seg.fileOffset(); | |
136 | break; | |
137 | } | |
138 | } | |
139 | if ( fLinkEditBase == NULL ) | |
140 | throw "no __LINKEDIT segment"; | |
141 | ||
39a8cd10 A |
142 | // get symbol table info |
143 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
144 | const uint32_t cmd_count = fHeader->ncmds(); | |
145 | const macho_load_command<P>* cmd = cmds; | |
146 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
147 | switch (cmd->cmd()) { | |
148 | case LC_SYMTAB: | |
149 | fSymbolTable = (macho_symtab_command<P>*)cmd; | |
150 | break; | |
151 | case LC_DYSYMTAB: | |
152 | fDynamicSymbolTable = (macho_dysymtab_command<P>*)cmd; | |
153 | break; | |
154 | case LC_DYLD_INFO: | |
155 | case LC_DYLD_INFO_ONLY: | |
156 | fDyldInfo = (macho_dyld_info_command<P>*)cmd; | |
157 | break; | |
df9d6cf7 A |
158 | case LC_SEGMENT_SPLIT_INFO: |
159 | fSplitSegInfo = (macho_linkedit_data_command<P>*)cmd; | |
160 | break; | |
161 | case macho_segment_command<P>::CMD: { | |
162 | // update segment/section file offsets | |
163 | macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd; | |
164 | macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>)); | |
165 | macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()]; | |
166 | for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) { | |
167 | fSectionOffsetsInSegment.push_back(sect->addr() - segCmd->vmaddr()); | |
168 | } | |
169 | } | |
39a8cd10 A |
170 | } |
171 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
172 | } | |
173 | ||
df9d6cf7 A |
174 | if ( fDyldInfo == NULL ) |
175 | throw "no LC_DYLD_INFO load command"; | |
bac542e6 A |
176 | |
177 | fSplittingSegments = layout.hasSplitSegInfo() && this->unequalSlides(); | |
df9d6cf7 A |
178 | |
179 | if ( fSplitSegInfo != NULL ) { | |
180 | const uint8_t* infoStart = &fLinkEditBase[fSplitSegInfo->dataoff()]; | |
181 | fHasSplitSegInfoV2 = ( *infoStart == DYLD_CACHE_ADJ_V2_FORMAT ); | |
182 | } | |
bac542e6 A |
183 | } |
184 | ||
bac542e6 A |
185 | template <> cpu_type_t Rebaser<x86>::getArchitecture() const { return CPU_TYPE_I386; } |
186 | template <> cpu_type_t Rebaser<x86_64>::getArchitecture() const { return CPU_TYPE_X86_64; } | |
39a8cd10 | 187 | template <> cpu_type_t Rebaser<arm>::getArchitecture() const { return CPU_TYPE_ARM; } |
19894a12 | 188 | template <> cpu_type_t Rebaser<arm64>::getArchitecture() const { return CPU_TYPE_ARM64; } |
bac542e6 A |
189 | |
190 | template <typename A> | |
191 | bool Rebaser<A>::unequalSlides() const | |
192 | { | |
193 | const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments(); | |
194 | uint64_t slide = segments[0].newAddress() - segments[0].address(); | |
195 | for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) { | |
196 | const MachOLayoutAbstraction::Segment& seg = *it; | |
197 | if ( (seg.newAddress() - seg.address()) != slide ) | |
198 | return true; | |
199 | } | |
200 | return false; | |
201 | } | |
202 | ||
203 | template <typename A> | |
204 | uint64_t Rebaser<A>::getBaseAddress() const | |
205 | { | |
206 | return fLayout.getSegments()[0].address(); | |
207 | } | |
208 | ||
209 | template <typename A> | |
210 | uint64_t Rebaser<A>::getVMSize() const | |
211 | { | |
212 | uint64_t highestVMAddress = 0; | |
213 | const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments(); | |
214 | for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) { | |
215 | const MachOLayoutAbstraction::Segment& seg = *it; | |
216 | if ( seg.address() > highestVMAddress ) | |
217 | highestVMAddress = seg.address(); | |
218 | } | |
219 | return (((highestVMAddress - getBaseAddress()) + 4095) & (-4096)); | |
220 | } | |
221 | ||
222 | ||
223 | ||
224 | template <typename A> | |
df9d6cf7 A |
225 | bool Rebaser<A>::rebase(std::vector<void*>& pointersInData) |
226 | { | |
227 | if ( fHasSplitSegInfoV2 ) { | |
228 | this->adjustReferencesUsingInfoV2(pointersInData); | |
229 | } | |
230 | else { | |
231 | //fprintf(stderr, "warning: dylib with old split-seg info: %s\n", fLayout.getFilePath()); | |
232 | // update writable segments that have internal pointers | |
412ebb8e | 233 | this->applyRebaseInfo(pointersInData); |
bac542e6 | 234 | |
df9d6cf7 A |
235 | // if splitting segments, update code-to-data references |
236 | this->adjustCode(); | |
237 | } | |
238 | ||
bac542e6 A |
239 | // update load commands |
240 | this->adjustLoadCommands(); | |
df9d6cf7 | 241 | |
bac542e6 A |
242 | // update symbol table |
243 | this->adjustSymbolTable(); | |
bac542e6 | 244 | |
df9d6cf7 A |
245 | // update export info |
246 | return this->adjustExportInfo(); | |
bac542e6 A |
247 | } |
248 | ||
bac542e6 A |
249 | template <typename A> |
250 | void Rebaser<A>::adjustLoadCommands() | |
251 | { | |
252 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
253 | const uint32_t cmd_count = fHeader->ncmds(); | |
254 | const macho_load_command<P>* cmd = cmds; | |
255 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
256 | switch ( cmd->cmd() ) { | |
257 | case LC_ID_DYLIB: | |
258 | if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { | |
259 | // clear timestamp so that any prebound clients are invalidated | |
260 | macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd; | |
261 | dylib->set_timestamp(1); | |
262 | } | |
263 | break; | |
264 | case LC_LOAD_DYLIB: | |
265 | case LC_LOAD_WEAK_DYLIB: | |
266 | case LC_REEXPORT_DYLIB: | |
412ebb8e | 267 | case LC_LOAD_UPWARD_DYLIB: |
bac542e6 A |
268 | if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { |
269 | // clear expected timestamps so that this image will load with invalid prebinding | |
270 | macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd; | |
271 | dylib->set_timestamp(2); | |
272 | } | |
273 | break; | |
274 | case macho_routines_command<P>::CMD: | |
275 | // update -init command | |
276 | { | |
277 | struct macho_routines_command<P>* routines = (struct macho_routines_command<P>*)cmd; | |
278 | routines->set_init_address(routines->init_address() + this->getSlideForVMAddress(routines->init_address())); | |
279 | } | |
280 | break; | |
281 | case macho_segment_command<P>::CMD: | |
282 | // update segment commands | |
283 | { | |
284 | macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd; | |
bac542e6 A |
285 | pint_t slide = this->getSlideForVMAddress(seg->vmaddr()); |
286 | seg->set_vmaddr(seg->vmaddr() + slide); | |
287 | macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>)); | |
288 | macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()]; | |
289 | for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) { | |
290 | sect->set_addr(sect->addr() + slide); | |
291 | } | |
292 | } | |
293 | break; | |
294 | } | |
295 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
296 | } | |
297 | } | |
298 | ||
19894a12 A |
299 | template <> |
300 | uint64_t Rebaser<arm64>::maskedVMAddress(pint_t vmaddress) | |
301 | { | |
302 | return (vmaddress & 0x0FFFFFFFFFFFFFFF); | |
303 | } | |
304 | ||
305 | template <typename A> | |
306 | typename A::P::uint_t Rebaser<A>::maskedVMAddress(pint_t vmaddress) | |
307 | { | |
308 | return vmaddress; | |
309 | } | |
bac542e6 A |
310 | |
311 | ||
312 | template <typename A> | |
313 | typename A::P::uint_t Rebaser<A>::getSlideForVMAddress(pint_t vmaddress) | |
314 | { | |
19894a12 | 315 | pint_t vmaddr = this->maskedVMAddress(vmaddress); |
bac542e6 A |
316 | const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments(); |
317 | for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) { | |
318 | const MachOLayoutAbstraction::Segment& seg = *it; | |
19894a12 | 319 | if ( (seg.address() <= vmaddr) && (seg.size() != 0) && ((vmaddr < (seg.address()+seg.size())) || (seg.address() == vmaddr)) ) { |
bac542e6 A |
320 | return seg.newAddress() - seg.address(); |
321 | } | |
322 | } | |
19894a12 | 323 | throwf("vm address 0x%08llX not found", (uint64_t)vmaddr); |
bac542e6 A |
324 | } |
325 | ||
326 | ||
327 | template <typename A> | |
328 | typename A::P::uint_t* Rebaser<A>::mappedAddressForVMAddress(pint_t vmaddress) | |
329 | { | |
19894a12 | 330 | pint_t vmaddr = this->maskedVMAddress(vmaddress); |
bac542e6 A |
331 | const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments(); |
332 | for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) { | |
333 | const MachOLayoutAbstraction::Segment& seg = *it; | |
19894a12 A |
334 | if ( (seg.address() <= vmaddr) && (vmaddr < (seg.address()+seg.size())) ) { |
335 | return (pint_t*)((vmaddr - seg.address()) + (uint8_t*)seg.mappedAddress()); | |
bac542e6 A |
336 | } |
337 | } | |
19894a12 | 338 | throwf("mappedAddressForVMAddress(0x%08llX) not found", (uint64_t)vmaddr); |
bac542e6 A |
339 | } |
340 | ||
341 | template <typename A> | |
342 | typename A::P::uint_t* Rebaser<A>::mappedAddressForNewAddress(pint_t vmaddress) | |
343 | { | |
344 | const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments(); | |
345 | for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) { | |
346 | const MachOLayoutAbstraction::Segment& seg = *it; | |
347 | if ( (seg.newAddress() <= vmaddress) && (vmaddress < (seg.newAddress()+seg.size())) ) { | |
348 | return (pint_t*)((vmaddress - seg.newAddress()) + (uint8_t*)seg.mappedAddress()); | |
349 | } | |
350 | } | |
351 | throwf("mappedAddressForNewAddress(0x%08llX) not found", (uint64_t)vmaddress); | |
352 | } | |
353 | ||
354 | template <typename A> | |
355 | typename A::P::uint_t Rebaser<A>::getSlideForNewAddress(pint_t newAddress) | |
356 | { | |
357 | const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments(); | |
358 | for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) { | |
359 | const MachOLayoutAbstraction::Segment& seg = *it; | |
360 | if ( (seg.newAddress() <= newAddress) && (newAddress < (seg.newAddress()+seg.size())) ) { | |
361 | return seg.newAddress() - seg.address(); | |
362 | } | |
363 | } | |
364 | throwf("new address 0x%08llX not found", (uint64_t)newAddress); | |
365 | } | |
366 | ||
bac542e6 A |
367 | template <typename A> |
368 | void Rebaser<A>::adjustSymbolTable() | |
369 | { | |
39a8cd10 | 370 | macho_nlist<P>* symbolTable = (macho_nlist<P>*)(&fLinkEditBase[fSymbolTable->symoff()]); |
bac542e6 A |
371 | |
372 | // walk all exports and slide their n_value | |
39a8cd10 A |
373 | macho_nlist<P>* lastExport = &symbolTable[fDynamicSymbolTable->iextdefsym()+fDynamicSymbolTable->nextdefsym()]; |
374 | for (macho_nlist<P>* entry = &symbolTable[fDynamicSymbolTable->iextdefsym()]; entry < lastExport; ++entry) { | |
bac542e6 A |
375 | if ( (entry->n_type() & N_TYPE) == N_SECT ) |
376 | entry->set_n_value(entry->n_value() + this->getSlideForVMAddress(entry->n_value())); | |
377 | } | |
378 | ||
39a8cd10 A |
379 | // walk all local symbols and slide their n_value (don't adjust any stabs) |
380 | macho_nlist<P>* lastLocal = &symbolTable[fDynamicSymbolTable->ilocalsym()+fDynamicSymbolTable->nlocalsym()]; | |
381 | for (macho_nlist<P>* entry = &symbolTable[fDynamicSymbolTable->ilocalsym()]; entry < lastLocal; ++entry) { | |
bac542e6 A |
382 | if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) ) |
383 | entry->set_n_value(entry->n_value() + this->getSlideForVMAddress(entry->n_value())); | |
384 | } | |
385 | } | |
386 | ||
39a8cd10 | 387 | template <typename A> |
df9d6cf7 | 388 | bool Rebaser<A>::adjustExportInfo() |
39a8cd10 A |
389 | { |
390 | // if no export info, nothing to adjust | |
391 | if ( fDyldInfo->export_size() == 0 ) | |
df9d6cf7 | 392 | return true; |
39a8cd10 A |
393 | |
394 | // since export info addresses are offsets from mach_header, everything in __TEXT is fine | |
395 | // only __DATA addresses need to be updated | |
df9d6cf7 | 396 | const uint8_t* start = fLayout.getDyldInfoExports(); |
39a8cd10 A |
397 | const uint8_t* end = &start[fDyldInfo->export_size()]; |
398 | std::vector<mach_o::trie::Entry> originalExports; | |
399 | try { | |
400 | parseTrie(start, end, originalExports); | |
401 | } | |
402 | catch (const char* msg) { | |
403 | throwf("%s in %s", msg, fLayout.getFilePath()); | |
404 | } | |
405 | ||
406 | std::vector<mach_o::trie::Entry> newExports; | |
407 | newExports.reserve(originalExports.size()); | |
408 | pint_t baseAddress = this->getBaseAddress(); | |
409 | pint_t baseAddressSlide = this->getSlideForVMAddress(baseAddress); | |
410 | for (std::vector<mach_o::trie::Entry>::iterator it=originalExports.begin(); it != originalExports.end(); ++it) { | |
411 | // remove symbols used by the static linker only | |
412 | if ( (strncmp(it->name, "$ld$", 4) == 0) | |
413 | || (strncmp(it->name, ".objc_class_name",16) == 0) | |
414 | || (strncmp(it->name, ".objc_category_name",19) == 0) ) { | |
415 | //fprintf(stderr, "ignoring symbol %s\n", it->name); | |
416 | continue; | |
417 | } | |
418 | // adjust symbols in slid segments | |
419 | //uint32_t oldOffset = it->address; | |
420 | it->address += (this->getSlideForVMAddress(it->address + baseAddress) - baseAddressSlide); | |
421 | //fprintf(stderr, "orig=0x%08X, new=0x%08llX, sym=%s\n", oldOffset, it->address, it->name); | |
422 | newExports.push_back(*it); | |
423 | } | |
424 | ||
425 | // rebuild export trie | |
426 | std::vector<uint8_t> newExportTrieBytes; | |
427 | newExportTrieBytes.reserve(fDyldInfo->export_size()); | |
428 | mach_o::trie::makeTrie(newExports, newExportTrieBytes); | |
429 | // align | |
430 | while ( (newExportTrieBytes.size() % sizeof(pint_t)) != 0 ) | |
431 | newExportTrieBytes.push_back(0); | |
432 | ||
39a8cd10 | 433 | uint32_t newExportsSize = newExportTrieBytes.size(); |
df9d6cf7 A |
434 | if ( newExportsSize <= fDyldInfo->export_size() ) { |
435 | // override existing trie in place | |
436 | uint8_t *realStart = &fLinkEditBase[fDyldInfo->export_off()]; | |
437 | bzero(realStart, fDyldInfo->export_size()); | |
438 | memcpy(realStart, &newExportTrieBytes[0], newExportsSize); | |
439 | fLayout.setDyldInfoExports(realStart); | |
440 | return true; | |
441 | } | |
442 | else { | |
443 | // allocate new buffer and set export_off in layout object to use new buffer instead | |
444 | uint8_t* sideTrie = new uint8_t[newExportsSize]; | |
445 | memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize); | |
446 | fLayout.setDyldInfoExports(sideTrie); | |
447 | ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_off(0); // invalidate old trie | |
448 | ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize); | |
449 | return false; | |
450 | } | |
39a8cd10 | 451 | } |
bac542e6 A |
452 | |
453 | ||
454 | ||
455 | template <typename A> | |
456 | void Rebaser<A>::doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta) | |
457 | { | |
412ebb8e A |
458 | //fprintf(stderr, "doCodeUpdate(kind=%d, address=0x%0llX, dataDelta=0x%08llX, importDelta=0x%08llX, path=%s)\n", |
459 | // kind, address, codeToDataDelta, codeToImportDelta, fLayout.getFilePath()); | |
bac542e6 A |
460 | uint32_t* p; |
461 | uint32_t instruction; | |
462 | uint32_t value; | |
463 | uint64_t value64; | |
464 | switch (kind) { | |
465 | case 1: // 32-bit pointer | |
466 | p = (uint32_t*)mappedAddressForVMAddress(address); | |
467 | value = A::P::E::get32(*p); | |
468 | value += codeToDataDelta; | |
469 | A::P::E::set32(*p, value); | |
470 | break; | |
471 | case 2: // 64-bit pointer | |
472 | p = (uint32_t*)mappedAddressForVMAddress(address); | |
473 | value64 = A::P::E::get64(*(uint64_t*)p); | |
474 | value64 += codeToDataDelta; | |
475 | A::P::E::set64(*(uint64_t*)p, value64); | |
476 | break; | |
bac542e6 A |
477 | case 4: // only used for i386, a reference to something in the IMPORT segment |
478 | p = (uint32_t*)mappedAddressForVMAddress(address); | |
479 | value = A::P::E::get32(*p); | |
480 | value += codeToImportDelta; | |
481 | A::P::E::set32(*p, value); | |
832b6fce A |
482 | break; |
483 | case 5: // used by thumb2 movw | |
484 | p = (uint32_t*)mappedAddressForVMAddress(address); | |
485 | instruction = A::P::E::get32(*p); | |
486 | // codeToDataDelta is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting | |
487 | value = (instruction & 0x0000000F) + (codeToDataDelta >> 12); | |
488 | instruction = (instruction & 0xFFFFFFF0) | (value & 0x0000000F); | |
489 | A::P::E::set32(*p, instruction); | |
490 | break; | |
491 | case 6: // used by ARM movw | |
492 | p = (uint32_t*)mappedAddressForVMAddress(address); | |
493 | instruction = A::P::E::get32(*p); | |
494 | // codeToDataDelta is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting | |
495 | value = ((instruction & 0x000F0000) >> 16) + (codeToDataDelta >> 12); | |
496 | instruction = (instruction & 0xFFF0FFFF) | ((value <<16) & 0x000F0000); | |
497 | A::P::E::set32(*p, instruction); | |
bac542e6 | 498 | break; |
832b6fce A |
499 | case 0x10: |
500 | case 0x11: | |
501 | case 0x12: | |
502 | case 0x13: | |
503 | case 0x14: | |
504 | case 0x15: | |
505 | case 0x16: | |
506 | case 0x17: | |
507 | case 0x18: | |
508 | case 0x19: | |
509 | case 0x1A: | |
510 | case 0x1B: | |
511 | case 0x1C: | |
512 | case 0x1D: | |
513 | case 0x1E: | |
514 | case 0x1F: | |
515 | // used by thumb2 movt (low nibble of kind is high 4-bits of paired movw) | |
516 | { | |
517 | p = (uint32_t*)mappedAddressForVMAddress(address); | |
518 | instruction = A::P::E::get32(*p); | |
519 | // extract 16-bit value from instruction | |
520 | uint32_t i = ((instruction & 0x00000400) >> 10); | |
521 | uint32_t imm4 = (instruction & 0x0000000F); | |
522 | uint32_t imm3 = ((instruction & 0x70000000) >> 28); | |
523 | uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); | |
524 | uint32_t imm16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; | |
525 | // combine with codeToDataDelta and kind nibble | |
526 | uint32_t targetValue = (imm16 << 16) | ((kind & 0xF) << 12); | |
527 | uint32_t newTargetValue = targetValue + codeToDataDelta; | |
528 | // construct new bits slices | |
529 | uint32_t imm4_ = (newTargetValue & 0xF0000000) >> 28; | |
530 | uint32_t i_ = (newTargetValue & 0x08000000) >> 27; | |
531 | uint32_t imm3_ = (newTargetValue & 0x07000000) >> 24; | |
532 | uint32_t imm8_ = (newTargetValue & 0x00FF0000) >> 16; | |
533 | // update instruction to match codeToDataDelta | |
534 | uint32_t newInstruction = (instruction & 0x8F00FBF0) | imm4_ | (i_ << 10) | (imm3_ << 28) | (imm8_ << 16); | |
535 | A::P::E::set32(*p, newInstruction); | |
536 | } | |
537 | break; | |
538 | case 0x20: | |
539 | case 0x21: | |
540 | case 0x22: | |
541 | case 0x23: | |
542 | case 0x24: | |
543 | case 0x25: | |
544 | case 0x26: | |
545 | case 0x27: | |
546 | case 0x28: | |
547 | case 0x29: | |
548 | case 0x2A: | |
549 | case 0x2B: | |
550 | case 0x2C: | |
551 | case 0x2D: | |
552 | case 0x2E: | |
553 | case 0x2F: | |
554 | // used by arm movt (low nibble of kind is high 4-bits of paired movw) | |
555 | { | |
556 | p = (uint32_t*)mappedAddressForVMAddress(address); | |
557 | instruction = A::P::E::get32(*p); | |
558 | // extract 16-bit value from instruction | |
559 | uint32_t imm4 = ((instruction & 0x000F0000) >> 16); | |
560 | uint32_t imm12 = (instruction & 0x00000FFF); | |
561 | uint32_t imm16 = (imm4 << 12) | imm12; | |
562 | // combine with codeToDataDelta and kind nibble | |
563 | uint32_t targetValue = (imm16 << 16) | ((kind & 0xF) << 12); | |
564 | uint32_t newTargetValue = targetValue + codeToDataDelta; | |
565 | // construct new bits slices | |
566 | uint32_t imm4_ = (newTargetValue & 0xF0000000) >> 28; | |
567 | uint32_t imm12_ = (newTargetValue & 0x0FFF0000) >> 16; | |
568 | // update instruction to match codeToDataDelta | |
569 | uint32_t newInstruction = (instruction & 0xFFF0F000) | (imm4_ << 16) | imm12_; | |
570 | A::P::E::set32(*p, newInstruction); | |
571 | } | |
572 | break; | |
19894a12 A |
573 | case 3: // used for arm64 ADRP |
574 | p = (uint32_t*)mappedAddressForVMAddress(address); | |
575 | instruction = A::P::E::get32(*p); | |
576 | if ( (instruction & 0x9F000000) == 0x90000000 ) { | |
577 | // codeToDataDelta is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting | |
578 | value64 = ((instruction & 0x60000000) >> 17) | ((instruction & 0x00FFFFE0) << 9); | |
579 | value64 += codeToDataDelta; | |
580 | instruction = (instruction & 0x9F00001F) | ((value64 << 17) & 0x60000000) | ((value64 >> 9) & 0x00FFFFE0); | |
581 | A::P::E::set32(*p, instruction); | |
582 | } | |
583 | break; | |
bac542e6 A |
584 | default: |
585 | throwf("invalid kind=%d in split seg info", kind); | |
586 | } | |
587 | } | |
588 | ||
589 | template <typename A> | |
590 | const uint8_t* Rebaser<A>::doCodeUpdateForEachULEB128Address(const uint8_t* p, uint8_t kind, uint64_t orgBaseAddress, int64_t codeToDataDelta, int64_t codeToImportDelta) | |
591 | { | |
592 | uint64_t address = 0; | |
593 | uint64_t delta = 0; | |
594 | uint32_t shift = 0; | |
595 | bool more = true; | |
596 | do { | |
597 | uint8_t byte = *p++; | |
598 | delta |= ((byte & 0x7F) << shift); | |
599 | shift += 7; | |
600 | if ( byte < 0x80 ) { | |
601 | if ( delta != 0 ) { | |
602 | address += delta; | |
603 | doCodeUpdate(kind, address+orgBaseAddress, codeToDataDelta, codeToImportDelta); | |
604 | delta = 0; | |
605 | shift = 0; | |
606 | } | |
607 | else { | |
608 | more = false; | |
609 | } | |
610 | } | |
611 | } while (more); | |
612 | return p; | |
613 | } | |
614 | ||
615 | template <typename A> | |
616 | void Rebaser<A>::adjustCode() | |
617 | { | |
618 | if ( fSplittingSegments ) { | |
619 | // get uleb128 compressed runs of code addresses to update | |
df9d6cf7 A |
620 | const uint8_t* infoStart = &fLinkEditBase[fSplitSegInfo->dataoff()]; |
621 | const uint8_t* infoEnd = &infoStart[fSplitSegInfo->datasize()];; | |
bac542e6 A |
622 | // calculate how much we need to slide writable segments |
623 | const uint64_t orgBaseAddress = this->getBaseAddress(); | |
624 | int64_t codeToDataDelta = 0; | |
625 | int64_t codeToImportDelta = 0; | |
626 | const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments(); | |
627 | const MachOLayoutAbstraction::Segment& codeSeg = segments[0]; | |
628 | for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) { | |
629 | const MachOLayoutAbstraction::Segment& dataSeg = *it; | |
df9d6cf7 | 630 | if ( dataSeg.writable() ) { |
19894a12 A |
631 | if ( (strcmp(dataSeg.name(), "__DATA") != 0) && (strcmp(dataSeg.name(), "__OBJC") != 0) ) |
632 | throwf("only one rw segment named '__DATA' can be used in dylibs placed in the dyld shared cache (%s)", fLayout.getFilePath()); | |
bac542e6 | 633 | codeToDataDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address()); |
19894a12 | 634 | } |
bac542e6 A |
635 | } |
636 | // decompress and call doCodeUpdate() on each address | |
412ebb8e | 637 | for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) { |
bac542e6 A |
638 | uint8_t kind = *p++; |
639 | p = this->doCodeUpdateForEachULEB128Address(p, kind, orgBaseAddress, codeToDataDelta, codeToImportDelta); | |
640 | } | |
641 | } | |
642 | } | |
643 | ||
bac542e6 | 644 | template <typename A> |
412ebb8e | 645 | void Rebaser<A>::doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersInData) |
bac542e6 | 646 | { |
39a8cd10 A |
647 | const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments(); |
648 | if ( segIndex > segments.size() ) | |
649 | throw "bad segment index in rebase info"; | |
650 | const MachOLayoutAbstraction::Segment& seg = segments[segIndex]; | |
651 | uint8_t* mappedAddr = (uint8_t*)seg.mappedAddress() + segOffset; | |
652 | pint_t* mappedAddrP = (pint_t*)mappedAddr; | |
653 | uint32_t* mappedAddr32 = (uint32_t*)mappedAddr; | |
654 | pint_t valueP; | |
655 | pint_t valuePnew; | |
656 | uint32_t value32; | |
657 | int32_t svalue32; | |
658 | int32_t svalue32new; | |
659 | switch ( type ) { | |
660 | case REBASE_TYPE_POINTER: | |
661 | valueP= P::getP(*mappedAddrP); | |
832b6fce A |
662 | try { |
663 | P::setP(*mappedAddrP, valueP + this->getSlideForVMAddress(valueP)); | |
664 | } | |
665 | catch (const char* msg) { | |
666 | throwf("at offset=0x%08llX in seg=%s, pointer cannot be rebased because it does not point to __TEXT or __DATA. %s\n", | |
667 | segOffset, seg.name(), msg); | |
668 | } | |
39a8cd10 A |
669 | break; |
670 | ||
671 | case REBASE_TYPE_TEXT_ABSOLUTE32: | |
672 | value32 = E::get32(*mappedAddr32); | |
673 | E::set32(*mappedAddr32, value32 + this->getSlideForVMAddress(value32)); | |
674 | break; | |
675 | ||
676 | case REBASE_TYPE_TEXT_PCREL32: | |
677 | svalue32 = E::get32(*mappedAddr32); | |
678 | valueP = seg.address() + segOffset + 4 + svalue32; | |
679 | valuePnew = valueP + this->getSlideForVMAddress(valueP); | |
680 | svalue32new = seg.address() + segOffset + 4 - valuePnew; | |
681 | E::set32(*mappedAddr32, svalue32new); | |
682 | break; | |
683 | ||
684 | default: | |
685 | throw "bad rebase type"; | |
686 | } | |
412ebb8e | 687 | pointersInData.push_back(mappedAddr); |
39a8cd10 | 688 | } |
bac542e6 | 689 | |
39a8cd10 A |
690 | |
691 | template <typename A> | |
412ebb8e | 692 | void Rebaser<A>::applyRebaseInfo(std::vector<void*>& pointersInData) |
39a8cd10 A |
693 | { |
694 | const uint8_t* p = &fLinkEditBase[fDyldInfo->rebase_off()]; | |
695 | const uint8_t* end = &p[fDyldInfo->rebase_size()]; | |
696 | ||
697 | uint8_t type = 0; | |
698 | int segIndex; | |
699 | uint64_t segOffset = 0; | |
700 | uint32_t count; | |
701 | uint32_t skip; | |
702 | bool done = false; | |
703 | while ( !done && (p < end) ) { | |
704 | uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; | |
705 | uint8_t opcode = *p & REBASE_OPCODE_MASK; | |
706 | ++p; | |
707 | switch (opcode) { | |
708 | case REBASE_OPCODE_DONE: | |
709 | done = true; | |
710 | break; | |
711 | case REBASE_OPCODE_SET_TYPE_IMM: | |
712 | type = immediate; | |
bac542e6 | 713 | break; |
39a8cd10 A |
714 | case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: |
715 | segIndex = immediate; | |
716 | segOffset = read_uleb128(p, end); | |
717 | break; | |
718 | case REBASE_OPCODE_ADD_ADDR_ULEB: | |
719 | segOffset += read_uleb128(p, end); | |
720 | break; | |
721 | case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: | |
722 | segOffset += immediate*sizeof(pint_t); | |
723 | break; | |
724 | case REBASE_OPCODE_DO_REBASE_IMM_TIMES: | |
725 | for (int i=0; i < immediate; ++i) { | |
412ebb8e | 726 | doRebase(segIndex, segOffset, type, pointersInData); |
39a8cd10 A |
727 | segOffset += sizeof(pint_t); |
728 | } | |
729 | break; | |
730 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: | |
731 | count = read_uleb128(p, end); | |
732 | for (uint32_t i=0; i < count; ++i) { | |
412ebb8e | 733 | doRebase(segIndex, segOffset, type, pointersInData); |
39a8cd10 A |
734 | segOffset += sizeof(pint_t); |
735 | } | |
736 | break; | |
737 | case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: | |
412ebb8e | 738 | doRebase(segIndex, segOffset, type, pointersInData); |
39a8cd10 A |
739 | segOffset += read_uleb128(p, end) + sizeof(pint_t); |
740 | break; | |
741 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: | |
742 | count = read_uleb128(p, end); | |
743 | skip = read_uleb128(p, end); | |
744 | for (uint32_t i=0; i < count; ++i) { | |
412ebb8e | 745 | doRebase(segIndex, segOffset, type, pointersInData); |
39a8cd10 A |
746 | segOffset += skip + sizeof(pint_t); |
747 | } | |
748 | break; | |
749 | default: | |
750 | throwf("bad rebase opcode %d", *p); | |
bac542e6 | 751 | } |
bac542e6 | 752 | } |
39a8cd10 | 753 | } |
bac542e6 | 754 | |
df9d6cf7 A |
755 | template <> |
756 | void Rebaser<arm64>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide, | |
757 | uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector<void*>& pointersInData) | |
39a8cd10 | 758 | { |
df9d6cf7 A |
759 | uint64_t value64; |
760 | uint64_t* mappedAddr64; | |
761 | uint32_t value32; | |
762 | uint32_t* mappedAddr32; | |
763 | uint32_t instruction; | |
764 | int64_t offsetAdjust; | |
765 | switch ( kind ) { | |
766 | case DYLD_CACHE_ADJ_V2_DELTA_32: | |
767 | mappedAddr32 = (uint32_t*)mappedAddr; | |
768 | value32 = arm64::P::E::get32(*mappedAddr32); | |
769 | E::set32(*mappedAddr32, value32 + adjust); | |
770 | break; | |
771 | case DYLD_CACHE_ADJ_V2_POINTER_64: | |
772 | mappedAddr64 = (uint64_t*)mappedAddr; | |
773 | if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) ) | |
774 | throwf("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX\n", fromNewAddress); | |
775 | E::set64(*mappedAddr64, toNewAddress); | |
776 | pointersInData.push_back(mappedAddr); | |
777 | break; | |
778 | case DYLD_CACHE_ADJ_V2_DELTA_64: | |
779 | mappedAddr64 = (uint64_t*)mappedAddr; | |
780 | value64 = arm64::P::E::get64(*mappedAddr64); | |
781 | E::set64(*mappedAddr64, value64 + adjust); | |
782 | break; | |
783 | case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32: | |
784 | mappedAddr32 = (uint32_t*)mappedAddr; | |
785 | value64 = toNewAddress - imageStartAddress; | |
786 | E::set32(*mappedAddr32, (uint32_t)value64); | |
787 | break; | |
788 | case DYLD_CACHE_ADJ_V2_ARM64_ADRP: | |
789 | mappedAddr32 = (uint32_t*)mappedAddr; | |
790 | instruction = arm64::P::E::get32(*mappedAddr32); | |
791 | if ( (instruction & 0x9F000000) == 0x90000000 ) { | |
792 | //value64 = ((instruction & 0x60000000) >> 17) | ((instruction & 0x00FFFFE0) << 9); | |
793 | uint32_t newPage21 = ((toNewAddress & ~0xFFF) - (fromNewAddress & ~0xFFF)) >> 12; | |
794 | instruction = (instruction & 0x9F00001F) | ((newPage21 << 29) & 0x60000000) | ((newPage21 << 3) & 0x00FFFFE0); | |
795 | arm64::P::E::set32(*mappedAddr32, instruction); | |
796 | } | |
797 | else { | |
798 | // ADRP instructions are sometimes optimized to other instructions (e.g. ADR) after the split-seg-info is generated | |
799 | } | |
800 | break; | |
801 | case DYLD_CACHE_ADJ_V2_ARM64_OFF12: | |
802 | mappedAddr32 = (uint32_t*)mappedAddr; | |
803 | instruction = arm64::P::E::get32(*mappedAddr32); | |
804 | offsetAdjust = (adjust & 0xFFF); | |
805 | if ( offsetAdjust == 0 ) | |
806 | break; | |
807 | if ( (instruction & 0x3B000000) == 0x39000000 ) { | |
808 | // LDR/STR imm12 | |
809 | if ( offsetAdjust != 0 ) { | |
810 | uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10); | |
811 | uint32_t newAddend; | |
812 | switch ( instruction & 0xC0000000 ) { | |
813 | case 0x00000000: | |
814 | if ( (instruction & 0x04800000) == 0x04800000 ) { | |
815 | if ( offsetAdjust & 0xF ) | |
816 | throwf("can't adjust off12 scale=16 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr); | |
817 | if ( encodedAddend*16 >= 4096 ) | |
818 | throwf("off12 scale=16 instruction points outside its page at mapped address=%p", mappedAddr); | |
819 | newAddend = (encodedAddend + offsetAdjust/16) % 256; | |
820 | } | |
821 | else { | |
822 | // scale=1 | |
823 | newAddend = encodedAddend + offsetAdjust; | |
824 | } | |
825 | break; | |
826 | case 0x40000000: | |
827 | if ( offsetAdjust & 1 ) | |
828 | throwf("can't adjust off12 scale=2 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr); | |
829 | if ( encodedAddend*2 >= 4096 ) | |
830 | throwf("off12 scale=2 instruction points outside its page at mapped address=%p", mappedAddr); | |
831 | newAddend = (encodedAddend + offsetAdjust/2) % 2048; | |
832 | break; | |
833 | case 0x80000000: | |
834 | if ( offsetAdjust & 3 ) | |
835 | throwf("can't adjust off12 scale=4 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr); | |
836 | if ( encodedAddend*4 >= 4096 ) | |
837 | throwf("off12 scale=4 instruction points outside its page at mapped address=%p", mappedAddr); | |
838 | newAddend = (encodedAddend + offsetAdjust/4) % 1024; | |
839 | break; | |
840 | case 0xC0000000: | |
841 | if ( offsetAdjust & 7 ) | |
842 | throwf("can't adjust off12 scale=8 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr); | |
843 | if ( encodedAddend*8 >= 4096 ) | |
844 | throwf("off12 scale=8 instruction points outside its page at mapped address=%p", mappedAddr); | |
845 | newAddend = (encodedAddend + offsetAdjust/8) % 512; | |
846 | break; | |
bac542e6 | 847 | } |
df9d6cf7 A |
848 | uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10); |
849 | arm64::P::E::set32(*mappedAddr32, newInstruction); | |
bac542e6 A |
850 | } |
851 | } | |
df9d6cf7 A |
852 | else if ( (instruction & 0xFFC00000) == 0x91000000 ) { |
853 | // ADD imm12 | |
854 | if ( instruction & 0x00C00000 ) | |
855 | throwf("ADD off12 uses shift at mapped address=%p", mappedAddr); | |
856 | uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10); | |
857 | uint32_t newAddend = (encodedAddend + offsetAdjust) & 0xFFF; | |
858 | uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10); | |
859 | arm64::P::E::set32(*mappedAddr32, newInstruction); | |
bac542e6 | 860 | } |
df9d6cf7 A |
861 | else if ( instruction != 0xD503201F ) { |
862 | // ignore imm12 instructions optimized into a NOP, but warn about others | |
863 | fprintf(stderr, "unknown off12 instruction 0x%08X at 0x%0llX in %s\n", instruction, fromNewAddress, fLayout.getFilePath()); | |
864 | } | |
865 | break; | |
866 | case DYLD_CACHE_ADJ_V2_ARM64_BR26: | |
867 | // nothing to do with calls to stubs | |
868 | break; | |
869 | default: | |
870 | throwf("unknown split seg kind=%d", kind); | |
bac542e6 A |
871 | } |
872 | } | |
873 | ||
df9d6cf7 | 874 | static bool isThumbMovw(uint32_t instruction) |
bac542e6 | 875 | { |
df9d6cf7 | 876 | return ( (instruction & 0x8000FBF0) == 0x0000F240 ); |
bac542e6 A |
877 | } |
878 | ||
df9d6cf7 | 879 | static bool isThumbMovt(uint32_t instruction) |
bac542e6 | 880 | { |
df9d6cf7 | 881 | return ( (instruction & 0x8000FBF0) == 0x0000F2C0 ); |
bac542e6 A |
882 | } |
883 | ||
df9d6cf7 | 884 | static uint16_t getThumbWord(uint32_t instruction) |
bac542e6 | 885 | { |
df9d6cf7 A |
886 | uint32_t i = ((instruction & 0x00000400) >> 10); |
887 | uint32_t imm4 = (instruction & 0x0000000F); | |
888 | uint32_t imm3 = ((instruction & 0x70000000) >> 28); | |
889 | uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); | |
890 | return ((imm4 << 12) | (i << 11) | (imm3 << 8) | imm8); | |
bac542e6 A |
891 | } |
892 | ||
df9d6cf7 A |
893 | static uint32_t setThumbWord(uint32_t instruction, uint16_t word) { |
894 | uint32_t imm4 = (word & 0xF000) >> 12; | |
895 | uint32_t i = (word & 0x0800) >> 11; | |
896 | uint32_t imm3 = (word & 0x0700) >> 8; | |
897 | uint32_t imm8 = word & 0x00FF; | |
898 | return (instruction & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); | |
bac542e6 A |
899 | } |
900 | ||
df9d6cf7 | 901 | static bool isArmMovw(uint32_t instruction) |
bac542e6 | 902 | { |
df9d6cf7 | 903 | return (instruction & 0x0FF00000) == 0x03000000; |
bac542e6 A |
904 | } |
905 | ||
df9d6cf7 A |
906 | static bool isArmMovt(uint32_t instruction) |
907 | { | |
908 | return (instruction & 0x0FF00000) == 0x03400000; | |
909 | } | |
bac542e6 | 910 | |
df9d6cf7 | 911 | static uint16_t getArmWord(uint32_t instruction) |
bac542e6 | 912 | { |
df9d6cf7 A |
913 | uint32_t imm4 = ((instruction & 0x000F0000) >> 16); |
914 | uint32_t imm12 = (instruction & 0x00000FFF); | |
915 | return (imm4 << 12) | imm12; | |
916 | } | |
917 | ||
918 | static uint32_t setArmWord(uint32_t instruction, uint16_t word) { | |
919 | uint32_t imm4 = (word & 0xF000) >> 12; | |
920 | uint32_t imm12 = word & 0x0FFF; | |
921 | return (instruction & 0xFFF0F000) | (imm4 << 16) | imm12; | |
bac542e6 A |
922 | } |
923 | ||
924 | ||
df9d6cf7 A |
925 | template <> |
926 | void Rebaser<arm>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide, | |
927 | uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector<void*>& pointersInData) | |
bac542e6 | 928 | { |
df9d6cf7 A |
929 | uint32_t value32; |
930 | uint32_t* mappedAddr32 = (uint32_t*)mappedAddr; | |
931 | static uint32_t* lastMappedAddr32 = NULL; | |
932 | static uint32_t lastKind = 0; | |
933 | static uint32_t lastToNewAddress = 0; | |
934 | switch ( kind ) { | |
935 | case DYLD_CACHE_ADJ_V2_DELTA_32: | |
936 | value32 = arm64::P::E::get32(*mappedAddr32); | |
937 | E::set32(*mappedAddr32, value32 + adjust); | |
938 | break; | |
939 | case DYLD_CACHE_ADJ_V2_POINTER_32: | |
940 | if ( toNewAddress != (E::get32(*mappedAddr32) + targetSlide) ) | |
941 | throwf("bad DYLD_CACHE_ADJ_V2_POINTER_32 value not as expected at address 0x%llX\n", fromNewAddress); | |
942 | E::set32(*mappedAddr32, toNewAddress); | |
943 | pointersInData.push_back( mappedAddr); | |
944 | break; | |
945 | case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32: | |
946 | mappedAddr32 = (uint32_t*)mappedAddr; | |
947 | value32 = (uint32_t)(toNewAddress - imageStartAddress); | |
948 | E::set32(*mappedAddr32, value32); | |
949 | break; | |
950 | case DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT: | |
951 | // to update a movw/movt pair we need to extract the 32-bit they will make, | |
952 | // add the adjust and write back the new movw/movt pair. | |
953 | if ( lastKind == kind ) { | |
954 | if ( lastToNewAddress == toNewAddress ) { | |
955 | uint32_t instruction1 = E::get32(*lastMappedAddr32); | |
956 | uint32_t instruction2 = E::get32(*mappedAddr32); | |
957 | if ( isThumbMovw(instruction1) && isThumbMovt(instruction2) ) { | |
958 | uint16_t high = getThumbWord(instruction2); | |
959 | uint16_t low = getThumbWord(instruction1); | |
960 | uint32_t full = high << 16 | low; | |
961 | full += adjust; | |
962 | instruction1 = setThumbWord(instruction1, full & 0xFFFF); | |
963 | instruction2 = setThumbWord(instruction2, full >> 16); | |
964 | } | |
965 | else if ( isThumbMovt(instruction1) && isThumbMovw(instruction2) ) { | |
966 | uint16_t high = getThumbWord(instruction1); | |
967 | uint16_t low = getThumbWord(instruction2); | |
968 | uint32_t full = high << 16 | low; | |
969 | full += adjust; | |
970 | instruction2 = setThumbWord(instruction2, full & 0xFFFF); | |
971 | instruction1 = setThumbWord(instruction1, full >> 16); | |
bac542e6 | 972 | } |
df9d6cf7 A |
973 | else { |
974 | throw "two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not"; | |
bac542e6 | 975 | } |
df9d6cf7 A |
976 | E::set32(*lastMappedAddr32, instruction1); |
977 | E::set32(*mappedAddr32, instruction2); | |
978 | kind = 0; | |
979 | } | |
980 | else { | |
981 | throw "two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses"; | |
bac542e6 A |
982 | } |
983 | } | |
df9d6cf7 A |
984 | break; |
985 | case DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT: | |
986 | // to update a movw/movt pair we need to extract the 32-bit they will make, | |
987 | // add the adjust and write back the new movw/movt pair. | |
988 | if ( lastKind == kind ) { | |
989 | if ( lastToNewAddress == toNewAddress ) { | |
990 | uint32_t instruction1 = E::get32(*lastMappedAddr32); | |
991 | uint32_t instruction2 = E::get32(*mappedAddr32); | |
992 | if ( isArmMovw(instruction1) && isArmMovt(instruction2) ) { | |
993 | uint16_t high = getArmWord(instruction2); | |
994 | uint16_t low = getArmWord(instruction1); | |
995 | uint32_t full = high << 16 | low; | |
996 | full += adjust; | |
997 | instruction1 = setArmWord(instruction1, full & 0xFFFF); | |
998 | instruction2 = setArmWord(instruction2, full >> 16); | |
bac542e6 | 999 | } |
df9d6cf7 A |
1000 | else if ( isArmMovt(instruction1) && isArmMovw(instruction2) ) { |
1001 | uint16_t high = getArmWord(instruction1); | |
1002 | uint16_t low = getArmWord(instruction2); | |
1003 | uint32_t full = high << 16 | low; | |
1004 | full += adjust; | |
1005 | instruction2 = setArmWord(instruction2, full & 0xFFFF); | |
1006 | instruction1 = setArmWord(instruction1, full >> 16); | |
39a8cd10 | 1007 | } |
bac542e6 | 1008 | else { |
df9d6cf7 | 1009 | throw "two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not"; |
bac542e6 | 1010 | } |
df9d6cf7 A |
1011 | E::set32(*lastMappedAddr32, instruction1); |
1012 | E::set32(*mappedAddr32, instruction2); | |
1013 | kind = 0; | |
bac542e6 | 1014 | } |
df9d6cf7 A |
1015 | else { |
1016 | throw "two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses"; | |
bac542e6 A |
1017 | } |
1018 | } | |
df9d6cf7 A |
1019 | break; |
1020 | case DYLD_CACHE_ADJ_V2_ARM_BR24: | |
1021 | case DYLD_CACHE_ADJ_V2_THUMB_BR22: | |
1022 | // nothing to do with calls to stubs | |
1023 | break; | |
1024 | default: | |
1025 | throwf("v2 split seg info kind (%d) not supported yet", kind); | |
1026 | } | |
1027 | lastKind = kind; | |
1028 | lastToNewAddress = toNewAddress; | |
1029 | lastMappedAddr32 = mappedAddr32; | |
1030 | } | |
bac542e6 A |
1031 | |
1032 | ||
df9d6cf7 A |
1033 | template <typename A> |
1034 | void Rebaser<A>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide, | |
1035 | uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector<void*>& pointersInData) | |
1036 | { | |
1037 | throw "v2 split seg info not supported yet"; | |
1038 | } | |
bac542e6 | 1039 | |
bac542e6 | 1040 | |
df9d6cf7 A |
1041 | template <typename A> |
1042 | void Rebaser<A>::adjustReferencesUsingInfoV2(std::vector<void*>& pointersInData) | |
1043 | { | |
1044 | static const bool log = false; | |
1045 | ||
1046 | const uint8_t* infoStart = &fLinkEditBase[fSplitSegInfo->dataoff()]; | |
1047 | const uint8_t* infoEnd = &infoStart[fSplitSegInfo->datasize()];; | |
1048 | if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) | |
1049 | throw "malformed split seg info"; | |
1050 | ||
1051 | // build section arrays of slide and mapped address for each section | |
1052 | std::vector<uint64_t> sectionSlides; | |
1053 | std::vector<uint64_t> sectionNewAddress; | |
1054 | std::vector<uint8_t*> sectionMappedAddress; | |
1055 | sectionSlides.reserve(16); | |
1056 | sectionNewAddress.reserve(16); | |
1057 | sectionMappedAddress.reserve(16); | |
1058 | // section index 0 refers to mach_header | |
1059 | const MachOLayoutAbstraction::Segment& textSeg = fLayout.getSegments().front(); | |
1060 | sectionMappedAddress.push_back((uint8_t*)textSeg.mappedAddress()); | |
1061 | sectionSlides.push_back(textSeg.newAddress() - textSeg.address()); | |
1062 | sectionNewAddress.push_back(textSeg.newAddress()); | |
1063 | // section 1 and later refer to real sections | |
1064 | unsigned sectionIndex = 0; | |
1065 | for (const MachOLayoutAbstraction::Segment& seg : fLayout.getSegments()) { | |
1066 | uint64_t segSlide = seg.newAddress() - seg.address(); | |
1067 | for (uint32_t i=0; i < seg.sectionCount(); ++i) { | |
1068 | if (log) fprintf(stderr, "seg=%s, sectIndex=%d, mapped at=%p, offsetInSeg=0x%08llX\n", seg.name(), sectionIndex, seg.mappedAddress(), fSectionOffsetsInSegment[sectionIndex]); | |
1069 | sectionMappedAddress.push_back((uint8_t*)seg.mappedAddress() + fSectionOffsetsInSegment[sectionIndex]); | |
1070 | sectionSlides.push_back(segSlide); | |
1071 | sectionNewAddress.push_back(seg.newAddress() + fSectionOffsetsInSegment[sectionIndex]); | |
1072 | ++sectionIndex; | |
1073 | } | |
1074 | } | |
bac542e6 | 1075 | |
df9d6cf7 A |
1076 | // Whole :== <count> FromToSection+ |
1077 | // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+ | |
1078 | // ToOffset :== <to-sect-offset-delta> <count> FromOffset+ | |
1079 | // FromOffset :== <kind> <count> <from-sect-offset-delta> | |
1080 | const uint8_t* p = infoStart; | |
1081 | uint64_t sectionCount = read_uleb128(p, infoEnd); | |
1082 | for (uint64_t i=0; i < sectionCount; ++i) { | |
1083 | uint64_t fromSectionIndex = read_uleb128(p, infoEnd); | |
1084 | uint64_t toSectionIndex = read_uleb128(p, infoEnd); | |
1085 | uint64_t toOffsetCount = read_uleb128(p, infoEnd); | |
1086 | uint64_t fromSectionSlide = sectionSlides[fromSectionIndex]; | |
1087 | uint64_t fromSectionNewAddress = sectionNewAddress[fromSectionIndex]; | |
1088 | uint8_t* fromSectionMappedAddress = sectionMappedAddress[fromSectionIndex]; | |
1089 | uint64_t toSectionSlide = sectionSlides[toSectionIndex]; | |
1090 | uint64_t toSectionNewAddress = sectionNewAddress[toSectionIndex]; | |
1091 | if (log) printf("from sect=%lld (mapped=%p), to sect=%lld (new addr=0x%llX):\n", fromSectionIndex, fromSectionMappedAddress, toSectionIndex, toSectionNewAddress); | |
1092 | uint64_t toSectionOffset = 0; | |
1093 | for (uint64_t j=0; j < toOffsetCount; ++j) { | |
1094 | uint64_t toSectionDelta = read_uleb128(p, infoEnd); | |
1095 | uint64_t fromOffsetCount = read_uleb128(p, infoEnd); | |
1096 | toSectionOffset += toSectionDelta; | |
1097 | for (uint64_t k=0; k < fromOffsetCount; ++k) { | |
1098 | uint64_t kind = read_uleb128(p, infoEnd); | |
1099 | uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd); | |
1100 | uint64_t fromSectionOffset = 0; | |
1101 | for (uint64_t l=0; l < fromSectDeltaCount; ++l) { | |
1102 | uint64_t delta = read_uleb128(p, infoEnd); | |
1103 | fromSectionOffset += delta; | |
1104 | int64_t deltaAdjust = toSectionSlide - fromSectionSlide; | |
1105 | if (log) printf(" kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n", kind, fromSectionOffset, toSectionOffset, deltaAdjust, toSectionSlide); | |
1106 | uint8_t* fromMappedAddr = fromSectionMappedAddress + fromSectionOffset; | |
1107 | uint64_t toNewAddress = toSectionNewAddress + toSectionOffset; | |
1108 | uint64_t fromNewAddress = fromSectionNewAddress + fromSectionOffset; | |
1109 | adjustReference(kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, toSectionSlide, sectionNewAddress.front(), sectionNewAddress.back(), pointersInData); | |
1110 | } | |
1111 | } | |
1112 | } | |
1113 | } | |
1114 | ||
1115 | ||
1116 | } | |
bac542e6 A |
1117 | |
1118 | #endif // __MACHO_REBASER__ | |
1119 | ||
1120 | ||
1121 | ||
1122 |