]> git.saurik.com Git - apple/ld64.git/blame - src/ld/passes/branch_shim.cpp
ld64-123.2.tar.gz
[apple/ld64.git] / src / ld / passes / branch_shim.cpp
CommitLineData
a645023d
A
1/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2010 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
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.
13 *
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.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25
26#include <stdint.h>
27#include <math.h>
28#include <string.h>
29#include <unistd.h>
30
31#include <vector>
32#include <map>
33
34#include "MachOFileAbstraction.hpp"
35#include "ld.hpp"
36#include "branch_shim.h"
37
38namespace ld {
39namespace passes {
40namespace branch_shim {
41
42
43
44static bool _s_log = false;
45static ld::Section _s_text_section("__TEXT", "__text", ld::Section::typeCode);
46
47
48
49class Thumb2ToArmShimAtom : public ld::Atom {
50public:
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)),
55 _name(NULL),
56 _target(target),
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()); }
62
63 virtual const ld::File* file() const { return NULL; }
64 virtual bool translationUnitSource(const char** dir, const char**) const
65 { return false; }
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
77 }
78
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]; }
82
83private:
84 const char* _name;
85 const ld::Atom* _target;
86 ld::Fixup _fixup1;
87 ld::Fixup _fixup2;
88 ld::Fixup _fixup3;
89 ld::Fixup _fixup4;
90};
91
92
93
94class Thumb1ToArmShimAtom : public ld::Atom {
95public:
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)),
100 _name(NULL),
101 _target(target),
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()); }
107
108 virtual const ld::File* file() const { return NULL; }
109 virtual bool translationUnitSource(const char** dir, const char**) const
110 { return false; }
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
125 }
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]; }
129
130private:
131 const char* _name;
132 const ld::Atom* _target;
133 ld::Fixup _fixup1;
134 ld::Fixup _fixup2;
135 ld::Fixup _fixup3;
136 ld::Fixup _fixup4;
137};
138
139
140
141
142class ARMtoThumbShimAtom : public ld::Atom {
143public:
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)),
148 _name(NULL),
149 _target(target),
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()); }
155
156 virtual const ld::File* file() const { return NULL; }
157 virtual bool translationUnitSource(const char** dir, const char**) const
158 { return false; }
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
170 }
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]; }
174
175private:
176 const char* _name;
177 const ld::Atom* _target;
178 ld::Fixup _fixup1;
179 ld::Fixup _fixup2;
180 ld::Fixup _fixup3;
181 ld::Fixup _fixup4;
182};
183
184
185
186
187
188
189static void extractTarget(ld::Fixup::iterator fixup, ld::Internal& state, const ld::Atom** target)
190{
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;
199 break;
200 case ld::Fixup::bindingsIndirectlyBound:
201 *target = state.indirectBindingTable[fixup->u.bindingIndex];
202 break;
203 }
204}
205
206
207
208//
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.
216//
217void doPass(const Options& opts, ld::Internal& state)
218{
219 std::map<const Atom*, const Atom*> atomToThumbMap;
220 std::map<const Atom*, const Atom*> thumbToAtomMap;
221 std::vector<const Atom*> shims;
222
223 // only make branch shims in final linked images
224 if ( opts.outputKind() == Options::kObjectFile )
225 return;
226
227 // only ARM need branch islands
228 if ( opts.architecture() != CPU_TYPE_ARM )
229 return;
230
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 )
236 textSection = sect;
237 }
238 if ( textSection == NULL )
239 return;
240
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);
253 if ( is_b ) {
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);
260 else
261 shim = new Thumb1ToArmShimAtom(target);
262 shims.push_back(shim);
263 thumbToAtomMap[target] = shim;
264 }
265 else {
266 shim = pos->second;
267 }
268 fit->binding = ld::Fixup::bindingDirectlyBound;
269 fit->u.target = shim;
270 }
271 }
272 break;
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);
279 if ( is_b ) {
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;
287 }
288 else {
289 shim = pos->second;
290 }
291 fit->binding = ld::Fixup::bindingDirectlyBound;
292 fit->u.target = shim;
293 }
294 }
295 break;
296
297 case ld::Fixup::kindStoreARMBranch24:
298 case ld::Fixup::kindStoreThumbBranch22:
299 fprintf(stderr, "found branch-22 without store in %s\n", atom->name());
300 break;
301 default:
302 break;
303 }
304 }
305 }
306
307 // append all new shims to end of __text
308 textSection->atoms.insert(textSection->atoms.end(), shims.begin(), shims.end());
309}
310
311
312} // namespace branch_shim
313} // namespace passes
314} // namespace ld