1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2010 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;
45 static ld::Section
_s_text_section("__TEXT", "__text", ld::Section::typeCode
);
49 class Thumb2ToArmShimAtom
: public ld::Atom
{
51 Thumb2ToArmShimAtom(const ld::Atom
* target
)
52 : ld::Atom(_s_text_section
, ld::Atom::definitionRegular
, ld::Atom::combineNever
,
53 ld::Atom::scopeLinkageUnit
, ld::Atom::typeUnclassified
,
54 ld::Atom::symbolTableIn
, false, true, false, ld::Atom::Alignment(1)),
57 _fixup1(8, ld::Fixup::k1of4
, ld::Fixup::kindSetTargetAddress
, target
),
58 _fixup2(8, ld::Fixup::k2of4
, ld::Fixup::kindSubtractTargetAddress
, this),
59 _fixup3(8, ld::Fixup::k3of4
, ld::Fixup::kindSubtractAddend
, 8),
60 _fixup4(8, ld::Fixup::k4of4
, ld::Fixup::kindStoreLittleEndian32
)
61 { asprintf((char**)&_name
, "%s$shim", target
->name()); }
63 virtual const ld::File
* file() const { return NULL
; }
64 virtual bool translationUnitSource(const char** dir
, const char**) const
66 virtual const char* name() const { return _name
; }
67 virtual uint64_t size() const { return 12; }
68 virtual uint64_t objectAddress() const { return 0; }
69 virtual void copyRawContent(uint8_t buffer
[]) const {
70 // Use ARM instructions that can jump to thumb.
71 assert( ! _target
->isThumb() );
72 if (_s_log
) fprintf(stderr
, "3 Thumb2 instruction shim to jump to %s\n", _target
->name());
73 OSWriteLittleInt32(&buffer
[0], 0, 0xc004f8df); // ldr ip, pc + 4
74 OSWriteLittleInt16(&buffer
[4], 0, 0x44fc); // add ip, pc, ip
75 OSWriteLittleInt16(&buffer
[6], 0, 0x4760); // bx ip
76 OSWriteLittleInt32(&buffer
[8], 0, 0x00000000); // .long target-this
79 virtual void setScope(Scope
) { }
80 virtual ld::Fixup::iterator
fixupsBegin() const { return (ld::Fixup
*)&_fixup1
; }
81 virtual ld::Fixup::iterator
fixupsEnd() const { return &((ld::Fixup
*)&_fixup4
)[1]; }
85 const ld::Atom
* _target
;
94 class Thumb1ToArmShimAtom
: public ld::Atom
{
96 Thumb1ToArmShimAtom(const ld::Atom
* target
)
97 : ld::Atom(_s_text_section
, ld::Atom::definitionRegular
, ld::Atom::combineNever
,
98 ld::Atom::scopeLinkageUnit
, ld::Atom::typeUnclassified
,
99 ld::Atom::symbolTableIn
, false, true, false, ld::Atom::Alignment(1)),
102 _fixup1(12, ld::Fixup::k1of4
, ld::Fixup::kindSetTargetAddress
, target
),
103 _fixup2(12, ld::Fixup::k2of4
, ld::Fixup::kindSubtractTargetAddress
, this),
104 _fixup3(12, ld::Fixup::k3of4
, ld::Fixup::kindSubtractAddend
, 8),
105 _fixup4(12, ld::Fixup::k4of4
, ld::Fixup::kindStoreLittleEndian32
)
106 { asprintf((char**)&_name
, "%s$shim", target
->name()); }
108 virtual const ld::File
* file() const { return NULL
; }
109 virtual bool translationUnitSource(const char** dir
, const char**) const
111 virtual const char* name() const { return _name
; }
112 virtual uint64_t size() const { return 16; }
113 virtual uint64_t objectAddress() const { return 0; }
114 virtual void copyRawContent(uint8_t buffer
[]) const {
115 // Use ARM instructions that can jump to thumb.
116 assert( ! _target
->isThumb() );
117 if (_s_log
) fprintf(stderr
, "6 Thumb1 instruction shim to jump to %s\n", _target
->name());
118 OSWriteLittleInt16(&buffer
[ 0], 0, 0xb402); // push {r1}
119 OSWriteLittleInt16(&buffer
[ 2], 0, 0x4902); // ldr r1, [pc, #8]
120 OSWriteLittleInt16(&buffer
[ 4], 0, 0x4479); // add r1, pc
121 OSWriteLittleInt16(&buffer
[ 6], 0, 0x468c); // mov ip, r1
122 OSWriteLittleInt16(&buffer
[ 8], 0, 0xbc02); // pop {r1}
123 OSWriteLittleInt16(&buffer
[10], 0, 0x4760); // bx ip
124 OSWriteLittleInt32(&buffer
[12], 0, 0x00000000); // .long target-this
126 virtual void setScope(Scope
) { }
127 virtual ld::Fixup::iterator
fixupsBegin() const { return (ld::Fixup
*)&_fixup1
; }
128 virtual ld::Fixup::iterator
fixupsEnd() const { return &((ld::Fixup
*)&_fixup4
)[1]; }
132 const ld::Atom
* _target
;
142 class ARMtoThumbShimAtom
: public ld::Atom
{
144 ARMtoThumbShimAtom(const ld::Atom
* target
)
145 : ld::Atom(_s_text_section
, ld::Atom::definitionRegular
, ld::Atom::combineNever
,
146 ld::Atom::scopeLinkageUnit
, ld::Atom::typeUnclassified
,
147 ld::Atom::symbolTableIn
, false, false, false, ld::Atom::Alignment(2)),
150 _fixup1(12, ld::Fixup::k1of4
, ld::Fixup::kindSetTargetAddress
, target
),
151 _fixup2(12, ld::Fixup::k2of4
, ld::Fixup::kindSubtractTargetAddress
, this),
152 _fixup3(12, ld::Fixup::k3of4
, ld::Fixup::kindSubtractAddend
, 12),
153 _fixup4(12, ld::Fixup::k4of4
, ld::Fixup::kindStoreLittleEndian32
)
154 { asprintf((char**)&_name
, "%s$shim", target
->name()); }
156 virtual const ld::File
* file() const { return NULL
; }
157 virtual bool translationUnitSource(const char** dir
, const char**) const
159 virtual const char* name() const { return _name
; }
160 virtual uint64_t size() const { return 16; }
161 virtual uint64_t objectAddress() const { return 0; }
162 virtual void copyRawContent(uint8_t buffer
[]) const {
163 // Use ARM instructions that can jump to thumb.
164 assert( _target
->isThumb() );
165 if (_s_log
) fprintf(stderr
, "4 ARM instruction shim to jump to %s\n", _target
->name());
166 OSWriteLittleInt32(&buffer
[ 0], 0, 0xe59fc004); // ldr ip, pc + 4
167 OSWriteLittleInt32(&buffer
[ 4], 0, 0xe08fc00c); // add ip, pc, ip
168 OSWriteLittleInt32(&buffer
[ 8], 0, 0xe12fff1c); // bx ip
169 OSWriteLittleInt32(&buffer
[12], 0, 0); // .long target-this
171 virtual void setScope(Scope
) { }
172 virtual ld::Fixup::iterator
fixupsBegin() const { return (ld::Fixup
*)&_fixup1
; }
173 virtual ld::Fixup::iterator
fixupsEnd() const { return &((ld::Fixup
*)&_fixup4
)[1]; }
177 const ld::Atom
* _target
;
189 static void extractTarget(ld::Fixup::iterator fixup
, ld::Internal
& state
, const ld::Atom
** target
)
191 switch ( fixup
->binding
) {
192 case ld::Fixup::bindingNone
:
193 throw "unexpected bindingNone";
194 case ld::Fixup::bindingByNameUnbound
:
195 throw "unexpected bindingByNameUnbound";
196 case ld::Fixup::bindingByContentBound
:
197 case ld::Fixup::bindingDirectlyBound
:
198 *target
= fixup
->u
.target
;
200 case ld::Fixup::bindingsIndirectlyBound
:
201 *target
= state
.indirectBindingTable
[fixup
->u
.bindingIndex
];
209 // The tail-call optimzation may result in a function ending in a jump (b)
210 // to another functions. At compile time the compiler does not know
211 // if the target of the jump will be in the same mode (arm vs thumb).
212 // The arm/thumb instruction set has a way to change modes in a bl(x)
213 // insruction, but no instruction to change mode in a jump (b) instruction.
214 // In those rare cases, the linker needs to insert a shim of code to
215 // make the mode switch.
217 void doPass(const Options
& opts
, ld::Internal
& state
)
219 std::map
<const Atom
*, const Atom
*> atomToThumbMap
;
220 std::map
<const Atom
*, const Atom
*> thumbToAtomMap
;
221 std::vector
<const Atom
*> shims
;
223 // only make branch shims in final linked images
224 if ( opts
.outputKind() == Options::kObjectFile
)
227 // only ARM need branch islands
228 if ( opts
.architecture() != CPU_TYPE_ARM
)
231 // scan to find __text section
232 ld::Internal::FinalSection
* textSection
= NULL
;
233 for (std::vector
<ld::Internal::FinalSection
*>::iterator sit
=state
.sections
.begin(); sit
!= state
.sections
.end(); ++sit
) {
234 ld::Internal::FinalSection
* sect
= *sit
;
235 if ( strcmp(sect
->sectionName(), "__text") == 0 )
238 if ( textSection
== NULL
)
241 // scan __text section for branch instructions that need to switch mode
242 for (std::vector
<const ld::Atom
*>::iterator ait
=textSection
->atoms
.begin(); ait
!= textSection
->atoms
.end(); ++ait
) {
243 const ld::Atom
* atom
= *ait
;
244 const ld::Atom
* target
;
245 for (ld::Fixup::iterator fit
= atom
->fixupsBegin(), end
=atom
->fixupsEnd(); fit
!= end
; ++fit
) {
246 switch ( fit
->kind
) {
247 case ld::Fixup::kindStoreTargetAddressThumbBranch22
:
248 extractTarget(fit
, state
, &target
);
249 if ( ! target
->isThumb() ) {
250 const uint8_t* fixUpLocation
= atom
->rawContentPointer() + fit
->offsetInAtom
;
251 uint32_t instruction
= *((uint32_t*)fixUpLocation
);
252 bool is_b
= ((instruction
& 0xD000F800) == 0x9000F000);
254 fprintf(stderr
, "need to add thumb->arm instr=0x%08X shim to %s for %s\n", instruction
, target
->name(), atom
->name());
255 const Atom
* shim
= NULL
;
256 std::map
<const Atom
*, const Atom
*>::iterator pos
= thumbToAtomMap
.find(target
);
257 if ( pos
== thumbToAtomMap
.end() ) {
258 if ( opts
.subArchitecture() == CPU_SUBTYPE_ARM_V7
)
259 shim
= new Thumb2ToArmShimAtom(target
);
261 shim
= new Thumb1ToArmShimAtom(target
);
262 shims
.push_back(shim
);
263 thumbToAtomMap
[target
] = shim
;
268 fit
->binding
= ld::Fixup::bindingDirectlyBound
;
269 fit
->u
.target
= shim
;
273 case ld::Fixup::kindStoreTargetAddressARMBranch24
:
274 extractTarget(fit
, state
, &target
);
275 if ( target
->isThumb() ) {
276 const uint8_t* fixUpLocation
= atom
->rawContentPointer() + fit
->offsetInAtom
;
277 uint32_t instruction
= *((uint32_t*)fixUpLocation
);
278 bool is_b
= ((instruction
& 0x0F000000) == 0x0A000000);
280 fprintf(stderr
, "need to add arm->thumb instr=0x%08X shim to %s for %s\n", instruction
, target
->name(), atom
->name());
281 const Atom
* shim
= NULL
;
282 std::map
<const Atom
*, const Atom
*>::iterator pos
= atomToThumbMap
.find(target
);
283 if ( pos
== atomToThumbMap
.end() ) {
284 shim
= new ARMtoThumbShimAtom(target
);
285 shims
.push_back(shim
);
286 atomToThumbMap
[target
] = shim
;
291 fit
->binding
= ld::Fixup::bindingDirectlyBound
;
292 fit
->u
.target
= shim
;
297 case ld::Fixup::kindStoreARMBranch24
:
298 case ld::Fixup::kindStoreThumbBranch22
:
299 fprintf(stderr
, "found branch-22 without store in %s\n", atom
->name());
307 // append all new shims to end of __text
308 textSection
->atoms
.insert(textSection
->atoms
.end(), shims
.begin(), shims
.end());
312 } // namespace branch_shim
313 } // namespace passes