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