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@
34 #include "MachOFileAbstraction.hpp"
36 #include "branch_shim.h"
40 namespace branch_shim
{
44 static bool _s_log
= false;
47 class Thumb2ToArmShimAtom
: public ld::Atom
{
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)),
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()); }
61 virtual const ld::File
* file() const { return NULL
; }
62 virtual bool translationUnitSource(const char** dir
, const char**) const
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
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]; }
83 const ld::Atom
* _target
;
91 class NoPICThumb2ToArmShimAtom
: public ld::Atom
{
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)),
99 _fixup1(8, ld::Fixup::k1of1
, ld::Fixup::kindStoreTargetAddressLittleEndian32
, target
)
100 { asprintf((char**)&_name
, "%s$shim", target
->name()); }
102 virtual const ld::File
* file() const { return NULL
; }
103 virtual bool translationUnitSource(const char** dir
, const char**) const
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
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]; }
124 const ld::Atom
* _target
;
129 class Thumb1ToArmShimAtom
: public ld::Atom
{
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)),
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()); }
143 virtual const ld::File
* file() const { return NULL
; }
144 virtual bool translationUnitSource(const char** dir
, const char**) const
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
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]; }
167 const ld::Atom
* _target
;
177 class ARMtoThumbShimAtom
: public ld::Atom
{
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)),
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()); }
191 virtual const ld::File
* file() const { return NULL
; }
192 virtual bool translationUnitSource(const char** dir
, const char**) const
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
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]; }
212 const ld::Atom
* _target
;
220 class NoPICARMtoThumbShimAtom
: public ld::Atom
{
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)),
228 _fixup1(8, ld::Fixup::k1of1
, ld::Fixup::kindStoreTargetAddressLittleEndian32
, target
)
229 { asprintf((char**)&_name
, "%s$shim", target
->name()); }
231 virtual const ld::File
* file() const { return NULL
; }
232 virtual bool translationUnitSource(const char** dir
, const char**) const
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
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]; }
250 const ld::Atom
* _target
;
259 static void extractTarget(ld::Fixup::iterator fixup
, ld::Internal
& state
, const ld::Atom
** target
)
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
;
270 case ld::Fixup::bindingsIndirectlyBound
:
271 *target
= state
.indirectBindingTable
[fixup
->u
.bindingIndex
];
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.
287 void doPass(const Options
& opts
, ld::Internal
& state
)
289 // only make branch shims in final linked images
290 if ( opts
.outputKind() == Options::kObjectFile
)
293 // only ARM need branch islands
294 if ( opts
.architecture() != CPU_TYPE_ARM
)
297 const bool makingKextBundle
= (opts
.outputKind() == Options::kKextBundle
);
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
;
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
)
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
);
334 shim
= new Thumb2ToArmShimAtom(target
, *sect
);
337 shim
= new Thumb1ToArmShimAtom(target
, *sect
);
339 shims
.push_back(shim
);
340 thumbToAtomMap
[target
] = shim
;
345 fit
->binding
= ld::Fixup::bindingDirectlyBound
;
346 fit
->u
.target
= shim
;
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
)
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
);
371 shim
= new ARMtoThumbShimAtom(target
, *sect
);
372 shims
.push_back(shim
);
373 atomToThumbMap
[target
] = shim
;
378 fit
->binding
= ld::Fixup::bindingDirectlyBound
;
379 fit
->u
.target
= shim
;
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.
396 // append all new shims to end of __text
397 sect
->atoms
.insert(sect
->atoms
.end(), shims
.begin(), shims
.end());
402 } // namespace branch_shim
403 } // namespace passes