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 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
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]; }
81 const ld::Atom
* _target
;
89 class NoPICThumb2ToArmShimAtom
: public ld::Atom
{
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)),
97 _fixup1(8, ld::Fixup::k1of1
, ld::Fixup::kindStoreTargetAddressLittleEndian32
, target
)
98 { asprintf((char**)&_name
, "%s$shim", target
->name()); }
100 virtual const ld::File
* file() const { return NULL
; }
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
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]; }
120 const ld::Atom
* _target
;
125 class Thumb1ToArmShimAtom
: public ld::Atom
{
127 Thumb1ToArmShimAtom(const ld::Atom
* target
, const ld::Section
& inSect
)
128 : ld::Atom(inSect
, ld::Atom::definitionRegular
, ld::Atom::combineNever
,
129 ld::Atom::scopeLinkageUnit
, ld::Atom::typeUnclassified
,
130 ld::Atom::symbolTableIn
, false, true, false, ld::Atom::Alignment(2)),
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()); }
139 virtual const ld::File
* file() const { return NULL
; }
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
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]; }
161 const ld::Atom
* _target
;
171 class ARMtoThumbShimAtom
: public ld::Atom
{
173 ARMtoThumbShimAtom(const ld::Atom
* target
, const ld::Section
& inSect
)
174 : ld::Atom(inSect
, ld::Atom::definitionRegular
, ld::Atom::combineNever
,
175 ld::Atom::scopeLinkageUnit
, ld::Atom::typeUnclassified
,
176 ld::Atom::symbolTableIn
, false, false, false, ld::Atom::Alignment(2)),
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()); }
185 virtual const ld::File
* file() const { return NULL
; }
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
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]; }
204 const ld::Atom
* _target
;
212 class NoPICARMtoThumbShimAtom
: public ld::Atom
{
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)),
220 _fixup1(8, ld::Fixup::k1of1
, ld::Fixup::kindStoreTargetAddressLittleEndian32
, target
)
221 { asprintf((char**)&_name
, "%s$shim", target
->name()); }
223 virtual const ld::File
* file() const { return NULL
; }
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
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]; }
240 const ld::Atom
* _target
;
249 static void extractTarget(ld::Fixup::iterator fixup
, ld::Internal
& state
, const ld::Atom
** target
)
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
;
260 case ld::Fixup::bindingsIndirectlyBound
:
261 *target
= state
.indirectBindingTable
[fixup
->u
.bindingIndex
];
269 // The tail-call optimzation may result in a function ending in a jump (b)
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.
277 void doPass(const Options
& opts
, ld::Internal
& state
)
279 // only make branch shims in final linked images
280 if ( opts
.outputKind() == Options::kObjectFile
)
283 // only ARM need branch islands
284 if ( opts
.architecture() != CPU_TYPE_ARM
)
287 const bool makingKextBundle
= (opts
.outputKind() == Options::kKextBundle
);
290 for (std::vector
<ld::Internal::FinalSection
*>::iterator sit
=state
.sections
.begin(); sit
!= state
.sections
.end(); ++sit
) {
291 ld::Internal::FinalSection
* sect
= *sit
;
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
;
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
)
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
321 if ( makingKextBundle
&& opts
.allowTextRelocs() )
322 shim
= new NoPICThumb2ToArmShimAtom(target
, *sect
);
324 shim
= new Thumb2ToArmShimAtom(target
, *sect
);
327 shim
= new Thumb1ToArmShimAtom(target
, *sect
);
329 shims
.push_back(shim
);
330 thumbToAtomMap
[target
] = shim
;
331 state
.atomToSection
[shim
] = sect
;
336 fit
->binding
= ld::Fixup::bindingDirectlyBound
;
337 fit
->u
.target
= shim
;
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
)
349 fixUpLocation
+= fit
->offsetInAtom
;
350 uint32_t instruction
= *((uint32_t*)fixUpLocation
);
351 bool is_b
= ((instruction
& 0x0F000000) == 0x0A000000) && ((instruction
& 0xF0000000) != 0xF0000000);
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
359 if ( makingKextBundle
&& opts
.allowTextRelocs() )
360 shim
= new NoPICARMtoThumbShimAtom(target
, *sect
);
362 shim
= new ARMtoThumbShimAtom(target
, *sect
);
363 shims
.push_back(shim
);
364 atomToThumbMap
[target
] = shim
;
365 state
.atomToSection
[shim
] = sect
;
370 fit
->binding
= ld::Fixup::bindingDirectlyBound
;
371 fit
->u
.target
= shim
;
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.
388 // append all new shims to end of __text
389 sect
->atoms
.insert(sect
->atoms
.end(), shims
.begin(), shims
.end());
394 } // namespace branch_shim
395 } // namespace passes