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