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
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@
35 #include "MachOFileAbstraction.hpp"
37 #include "branch_shim.h"
41 namespace branch_shim
{
45 static bool _s_log
= false;
48 class Thumb2ToArmShimAtom
: public ld::Atom
{
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)),
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]; }
82 const ld::Atom
* _target
;
90 class NoPICThumb2ToArmShimAtom
: public ld::Atom
{
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)),
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]; }
121 const ld::Atom
* _target
;
126 class Thumb1ToArmShimAtom
: public ld::Atom
{
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)),
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]; }
162 const ld::Atom
* _target
;
172 class ARMtoThumbShimAtom
: public ld::Atom
{
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)),
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]; }
205 const ld::Atom
* _target
;
213 class NoPICARMtoThumbShimAtom
: public ld::Atom
{
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)),
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]; }
241 const ld::Atom
* _target
;
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
;
261 case ld::Fixup::bindingsIndirectlyBound
:
262 *target
= state
.indirectBindingTable
[fixup
->u
.bindingIndex
];
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
)
284 // only ARM need branch islands
285 if ( opts
.architecture() != CPU_TYPE_ARM
)
288 const bool makingKextBundle
= (opts
.outputKind() == Options::kKextBundle
);
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
;
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
)
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
);
325 shim
= new Thumb2ToArmShimAtom(target
, *sect
);
328 shim
= new Thumb1ToArmShimAtom(target
, *sect
);
330 shims
.push_back(shim
);
331 thumbToAtomMap
[target
] = shim
;
332 state
.atomToSection
[shim
] = sect
;
337 fit
->binding
= ld::Fixup::bindingDirectlyBound
;
338 fit
->u
.target
= shim
;
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
)
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
);
363 shim
= new ARMtoThumbShimAtom(target
, *sect
);
364 shims
.push_back(shim
);
365 atomToThumbMap
[target
] = shim
;
366 state
.atomToSection
[shim
] = sect
;
371 fit
->binding
= ld::Fixup::bindingDirectlyBound
;
372 fit
->u
.target
= shim
;
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.
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