]> git.saurik.com Git - apple/ld64.git/blob - src/ld/passes/branch_shim.cpp
ld64-133.3.tar.gz
[apple/ld64.git] / src / ld / passes / branch_shim.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2010-2011 Apple 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
26 #include <stdint.h>
27 #include <math.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include <vector>
32 #include <map>
33
34 #include "MachOFileAbstraction.hpp"
35 #include "ld.hpp"
36 #include "branch_shim.h"
37
38 namespace ld {
39 namespace passes {
40 namespace branch_shim {
41
42
43
44 static bool _s_log = false;
45
46
47 class Thumb2ToArmShimAtom : public ld::Atom {
48 public:
49 Thumb2ToArmShimAtom(const ld::Atom* target, const ld::Section& inSect)
50 : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
51 ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
52 ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(2)),
53 _name(NULL),
54 _target(target),
55 _fixup1(8, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target),
56 _fixup2(8, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this),
57 _fixup3(8, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8),
58 _fixup4(8, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32)
59 { asprintf((char**)&_name, "%s$shim", target->name()); }
60
61 virtual const ld::File* file() const { return NULL; }
62 virtual bool translationUnitSource(const char** dir, const char**) const
63 { return false; }
64 virtual const char* name() const { return _name; }
65 virtual uint64_t size() const { return 12; }
66 virtual uint64_t objectAddress() const { return 0; }
67 virtual void copyRawContent(uint8_t buffer[]) const {
68 // Use ARM instructions that can jump to thumb.
69 assert( ! _target->isThumb() );
70 if (_s_log) fprintf(stderr, "3 Thumb2 instruction shim to jump to %s\n", _target->name());
71 OSWriteLittleInt32(&buffer[0], 0, 0xc004f8df); // ldr ip, pc + 4
72 OSWriteLittleInt16(&buffer[4], 0, 0x44fc); // add ip, pc, ip
73 OSWriteLittleInt16(&buffer[6], 0, 0x4760); // bx ip
74 OSWriteLittleInt32(&buffer[8], 0, 0x00000000); // .long target-this
75 }
76
77 virtual void setScope(Scope) { }
78 virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
79 virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; }
80
81 private:
82 const char* _name;
83 const ld::Atom* _target;
84 ld::Fixup _fixup1;
85 ld::Fixup _fixup2;
86 ld::Fixup _fixup3;
87 ld::Fixup _fixup4;
88 };
89
90
91 class NoPICThumb2ToArmShimAtom : public ld::Atom {
92 public:
93 NoPICThumb2ToArmShimAtom(const ld::Atom* target, const ld::Section& inSect)
94 : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
95 ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
96 ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(2)),
97 _name(NULL),
98 _target(target),
99 _fixup1(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, target)
100 { asprintf((char**)&_name, "%s$shim", target->name()); }
101
102 virtual const ld::File* file() const { return NULL; }
103 virtual bool translationUnitSource(const char** dir, const char**) const
104 { return false; }
105 virtual const char* name() const { return _name; }
106 virtual uint64_t size() const { return 12; }
107 virtual uint64_t objectAddress() const { return 0; }
108 virtual void copyRawContent(uint8_t buffer[]) const {
109 // Use ARM instructions that can jump to thumb.
110 assert( ! _target->isThumb() );
111 if (_s_log) fprintf(stderr, "3 Thumb2 instruction shim to jump to %s\n", _target->name());
112 OSWriteLittleInt32(&buffer[0], 0, 0xc004f8df); // ldr ip, pc + 4
113 OSWriteLittleInt16(&buffer[4], 0, 0x4760); // bx ip
114 OSWriteLittleInt16(&buffer[6], 0, 0x46C0); // nop
115 OSWriteLittleInt32(&buffer[8], 0, 0x00000000); // .long target
116 }
117
118 virtual void setScope(Scope) { }
119 virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
120 virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; }
121
122 private:
123 const char* _name;
124 const ld::Atom* _target;
125 ld::Fixup _fixup1;
126 };
127
128
129 class Thumb1ToArmShimAtom : public ld::Atom {
130 public:
131 Thumb1ToArmShimAtom(const ld::Atom* target, const ld::Section& inSect)
132 : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
133 ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
134 ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(2)),
135 _name(NULL),
136 _target(target),
137 _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target),
138 _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this),
139 _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8),
140 _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32)
141 { asprintf((char**)&_name, "%s$shim", target->name()); }
142
143 virtual const ld::File* file() const { return NULL; }
144 virtual bool translationUnitSource(const char** dir, const char**) const
145 { return false; }
146 virtual const char* name() const { return _name; }
147 virtual uint64_t size() const { return 16; }
148 virtual uint64_t objectAddress() const { return 0; }
149 virtual void copyRawContent(uint8_t buffer[]) const {
150 // Use ARM instructions that can jump to thumb.
151 assert( ! _target->isThumb() );
152 if (_s_log) fprintf(stderr, "6 Thumb1 instruction shim to jump to %s\n", _target->name());
153 OSWriteLittleInt16(&buffer[ 0], 0, 0xb402); // push {r1}
154 OSWriteLittleInt16(&buffer[ 2], 0, 0x4902); // ldr r1, [pc, #8]
155 OSWriteLittleInt16(&buffer[ 4], 0, 0x4479); // add r1, pc
156 OSWriteLittleInt16(&buffer[ 6], 0, 0x468c); // mov ip, r1
157 OSWriteLittleInt16(&buffer[ 8], 0, 0xbc02); // pop {r1}
158 OSWriteLittleInt16(&buffer[10], 0, 0x4760); // bx ip
159 OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long target-this
160 }
161 virtual void setScope(Scope) { }
162 virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
163 virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; }
164
165 private:
166 const char* _name;
167 const ld::Atom* _target;
168 ld::Fixup _fixup1;
169 ld::Fixup _fixup2;
170 ld::Fixup _fixup3;
171 ld::Fixup _fixup4;
172 };
173
174
175
176
177 class ARMtoThumbShimAtom : public ld::Atom {
178 public:
179 ARMtoThumbShimAtom(const ld::Atom* target, const ld::Section& inSect)
180 : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
181 ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
182 ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)),
183 _name(NULL),
184 _target(target),
185 _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target),
186 _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this),
187 _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12),
188 _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32)
189 { asprintf((char**)&_name, "%s$shim", target->name()); }
190
191 virtual const ld::File* file() const { return NULL; }
192 virtual bool translationUnitSource(const char** dir, const char**) const
193 { return false; }
194 virtual const char* name() const { return _name; }
195 virtual uint64_t size() const { return 16; }
196 virtual uint64_t objectAddress() const { return 0; }
197 virtual void copyRawContent(uint8_t buffer[]) const {
198 // Use ARM instructions that can jump to thumb.
199 assert( _target->isThumb() );
200 if (_s_log) fprintf(stderr, "4 ARM instruction shim to jump to %s\n", _target->name());
201 OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 4
202 OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip
203 OSWriteLittleInt32(&buffer[ 8], 0, 0xe12fff1c); // bx ip
204 OSWriteLittleInt32(&buffer[12], 0, 0); // .long target-this
205 }
206 virtual void setScope(Scope) { }
207 virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
208 virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; }
209
210 private:
211 const char* _name;
212 const ld::Atom* _target;
213 ld::Fixup _fixup1;
214 ld::Fixup _fixup2;
215 ld::Fixup _fixup3;
216 ld::Fixup _fixup4;
217 };
218
219
220 class NoPICARMtoThumbShimAtom : public ld::Atom {
221 public:
222 NoPICARMtoThumbShimAtom(const ld::Atom* target, const ld::Section& inSect)
223 : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
224 ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
225 ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)),
226 _name(NULL),
227 _target(target),
228 _fixup1(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, target)
229 { asprintf((char**)&_name, "%s$shim", target->name()); }
230
231 virtual const ld::File* file() const { return NULL; }
232 virtual bool translationUnitSource(const char** dir, const char**) const
233 { return false; }
234 virtual const char* name() const { return _name; }
235 virtual uint64_t size() const { return 12; }
236 virtual uint64_t objectAddress() const { return 0; }
237 virtual void copyRawContent(uint8_t buffer[]) const {
238 // Use ARM instructions that can jump to thumb.
239 if (_s_log) fprintf(stderr, "3 ARM instruction shim to jump to %s\n", _target->name());
240 OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, pc + 4
241 OSWriteLittleInt32(&buffer[ 4], 0, 0xe12fff1c); // bx ip
242 OSWriteLittleInt32(&buffer[ 8], 0, 0); // .long target
243 }
244 virtual void setScope(Scope) { }
245 virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
246 virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; }
247
248 private:
249 const char* _name;
250 const ld::Atom* _target;
251 ld::Fixup _fixup1;
252 };
253
254
255
256
257
258
259 static void extractTarget(ld::Fixup::iterator fixup, ld::Internal& state, const ld::Atom** target)
260 {
261 switch ( fixup->binding ) {
262 case ld::Fixup::bindingNone:
263 throw "unexpected bindingNone";
264 case ld::Fixup::bindingByNameUnbound:
265 throw "unexpected bindingByNameUnbound";
266 case ld::Fixup::bindingByContentBound:
267 case ld::Fixup::bindingDirectlyBound:
268 *target = fixup->u.target;
269 break;
270 case ld::Fixup::bindingsIndirectlyBound:
271 *target = state.indirectBindingTable[fixup->u.bindingIndex];
272 break;
273 }
274 }
275
276
277
278 //
279 // The tail-call optimzation may result in a function ending in a jump (b)
280 // to another functions. At compile time the compiler does not know
281 // if the target of the jump will be in the same mode (arm vs thumb).
282 // The arm/thumb instruction set has a way to change modes in a bl(x)
283 // insruction, but no instruction to change mode in a jump (b) instruction.
284 // In those rare cases, the linker needs to insert a shim of code to
285 // make the mode switch.
286 //
287 void doPass(const Options& opts, ld::Internal& state)
288 {
289 // only make branch shims in final linked images
290 if ( opts.outputKind() == Options::kObjectFile )
291 return;
292
293 // only ARM need branch islands
294 if ( opts.architecture() != CPU_TYPE_ARM )
295 return;
296
297 const bool makingKextBundle = (opts.outputKind() == Options::kKextBundle);
298
299 // scan all sections
300 for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
301 ld::Internal::FinalSection* sect = *sit;
302 std::map<const Atom*, const Atom*> atomToThumbMap;
303 std::map<const Atom*, const Atom*> thumbToAtomMap;
304 std::vector<const Atom*> shims;
305 // scan section for branch instructions that need to switch mode
306 for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
307 const ld::Atom* atom = *ait;
308 const ld::Atom* target = NULL;
309 bool targetIsProxy;
310 for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
311 switch ( fit->kind ) {
312 case ld::Fixup::kindStoreTargetAddressThumbBranch22:
313 extractTarget(fit, state, &target);
314 targetIsProxy = (target->definition() == ld::Atom::definitionProxy);
315 if ( ! target->isThumb() ) {
316 const uint8_t* fixUpLocation = atom->rawContentPointer();
317 // <rdar://problem/9544194> don't try to scan atom for branches if atom unwilling to supply raw content
318 if ( fixUpLocation == NULL )
319 break;
320 fixUpLocation += fit->offsetInAtom;
321 uint32_t instruction = *((uint32_t*)fixUpLocation);
322 bool is_b = ((instruction & 0xD000F800) == 0x9000F000);
323 // need shim for branch from thumb to arm, or for call to function outside kext
324 if ( is_b || (targetIsProxy && makingKextBundle) ) {
325 if ( _s_log ) fprintf(stderr, "need to add thumb->arm instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name());
326 const Atom* shim = NULL;
327 std::map<const Atom*, const Atom*>::iterator pos = thumbToAtomMap.find(target);
328 if ( pos == thumbToAtomMap.end() ) {
329 if ( opts.archSupportsThumb2() ) {
330 // <rdar://problem/9116044> make long-branch style shims for arm kexts
331 if ( makingKextBundle && opts.allowTextRelocs() )
332 shim = new NoPICThumb2ToArmShimAtom(target, *sect);
333 else
334 shim = new Thumb2ToArmShimAtom(target, *sect);
335 }
336 else {
337 shim = new Thumb1ToArmShimAtom(target, *sect);
338 }
339 shims.push_back(shim);
340 thumbToAtomMap[target] = shim;
341 }
342 else {
343 shim = pos->second;
344 }
345 fit->binding = ld::Fixup::bindingDirectlyBound;
346 fit->u.target = shim;
347 }
348 }
349 break;
350 case ld::Fixup::kindStoreTargetAddressARMBranch24:
351 extractTarget(fit, state, &target);
352 targetIsProxy = (target->definition() == ld::Atom::definitionProxy);
353 if ( target->isThumb() || (targetIsProxy && makingKextBundle) ) {
354 const uint8_t* fixUpLocation = atom->rawContentPointer();
355 // <rdar://problem/9544194> don't try to scan atom for branches if atom unwilling to supply raw content
356 if ( fixUpLocation == NULL )
357 break;
358 fixUpLocation += fit->offsetInAtom;
359 uint32_t instruction = *((uint32_t*)fixUpLocation);
360 bool is_b = ((instruction & 0x0F000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000);
361 // need shim for branch from arm to thumb, or for call to function outside kext
362 if ( is_b || (targetIsProxy && makingKextBundle) ) {
363 if ( _s_log ) fprintf(stderr, "need to add arm->thumb instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name());
364 const Atom* shim = NULL;
365 std::map<const Atom*, const Atom*>::iterator pos = atomToThumbMap.find(target);
366 if ( pos == atomToThumbMap.end() ) {
367 // <rdar://problem/9116044> make long-branch style shims for arm kexts
368 if ( makingKextBundle && opts.allowTextRelocs() )
369 shim = new NoPICARMtoThumbShimAtom(target, *sect);
370 else
371 shim = new ARMtoThumbShimAtom(target, *sect);
372 shims.push_back(shim);
373 atomToThumbMap[target] = shim;
374 }
375 else {
376 shim = pos->second;
377 }
378 fit->binding = ld::Fixup::bindingDirectlyBound;
379 fit->u.target = shim;
380 }
381 }
382 break;
383
384 //case ld::Fixup::kindStoreARMBranch24:
385 //case ld::Fixup::kindStoreThumbBranch22:
386 // Note: these fixups will only be seen if the the b/bl is to a symbol plus addend
387 // for now we don't handle making shims. If a shim is needed there will
388 // be an error later.
389 // break;
390 default:
391 break;
392 }
393 }
394 }
395
396 // append all new shims to end of __text
397 sect->atoms.insert(sect->atoms.end(), shims.begin(), shims.end());
398 }
399 }
400
401
402 } // namespace branch_shim
403 } // namespace passes
404 } // namespace ld