blob: f394b4f37551fab4431f0423861f40dfd575d8b7 [file] [log] [blame]
Jim Grosbach2fc68982011-06-22 20:14:52 +00001//===-- ARMMachObjectWriter.cpp - ARM Mach Object Writer ------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Evan Chengbe740292011-07-23 00:00:19 +000010#include "MCTargetDesc/ARMBaseInfo.h"
11#include "MCTargetDesc/ARMFixupKinds.h"
Jim Grosbachba8297e2011-06-24 23:44:37 +000012#include "llvm/ADT/Twine.h"
13#include "llvm/MC/MCAssembler.h"
14#include "llvm/MC/MCAsmLayout.h"
Jim Grosbach2fc68982011-06-22 20:14:52 +000015#include "llvm/MC/MCMachObjectWriter.h"
Jim Grosbachba8297e2011-06-24 23:44:37 +000016#include "llvm/MC/MCExpr.h"
17#include "llvm/MC/MCFixup.h"
18#include "llvm/MC/MCFixupKindInfo.h"
Jim Grosbach62006112011-11-29 01:15:25 +000019#include "llvm/MC/MCMachOSymbolFlags.h"
Jim Grosbachba8297e2011-06-24 23:44:37 +000020#include "llvm/MC/MCValue.h"
21#include "llvm/Object/MachOFormat.h"
22#include "llvm/Support/ErrorHandling.h"
Jim Grosbach2fc68982011-06-22 20:14:52 +000023using namespace llvm;
Jim Grosbachba8297e2011-06-24 23:44:37 +000024using namespace llvm::object;
Jim Grosbach2fc68982011-06-22 20:14:52 +000025
26namespace {
27class ARMMachObjectWriter : public MCMachObjectTargetWriter {
Jim Grosbachba8297e2011-06-24 23:44:37 +000028 void RecordARMScatteredRelocation(MachObjectWriter *Writer,
29 const MCAssembler &Asm,
30 const MCAsmLayout &Layout,
31 const MCFragment *Fragment,
32 const MCFixup &Fixup,
33 MCValue Target,
34 unsigned Log2Size,
35 uint64_t &FixedValue);
36 void RecordARMMovwMovtRelocation(MachObjectWriter *Writer,
37 const MCAssembler &Asm,
38 const MCAsmLayout &Layout,
39 const MCFragment *Fragment,
40 const MCFixup &Fixup, MCValue Target,
41 uint64_t &FixedValue);
42
Jim Grosbach2fc68982011-06-22 20:14:52 +000043public:
44 ARMMachObjectWriter(bool Is64Bit, uint32_t CPUType,
45 uint32_t CPUSubtype)
46 : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype,
47 /*UseAggressiveSymbolFolding=*/true) {}
Jim Grosbachba8297e2011-06-24 23:44:37 +000048
49 void RecordRelocation(MachObjectWriter *Writer,
50 const MCAssembler &Asm, const MCAsmLayout &Layout,
51 const MCFragment *Fragment, const MCFixup &Fixup,
52 MCValue Target, uint64_t &FixedValue);
Jim Grosbach2fc68982011-06-22 20:14:52 +000053};
54}
55
Jim Grosbachba8297e2011-06-24 23:44:37 +000056static bool getARMFixupKindMachOInfo(unsigned Kind, unsigned &RelocType,
57 unsigned &Log2Size) {
58 RelocType = unsigned(macho::RIT_Vanilla);
59 Log2Size = ~0U;
60
61 switch (Kind) {
62 default:
63 return false;
64
65 case FK_Data_1:
66 Log2Size = llvm::Log2_32(1);
67 return true;
68 case FK_Data_2:
69 Log2Size = llvm::Log2_32(2);
70 return true;
71 case FK_Data_4:
72 Log2Size = llvm::Log2_32(4);
73 return true;
74 case FK_Data_8:
75 Log2Size = llvm::Log2_32(8);
76 return true;
77
78 // Handle 24-bit branch kinds.
79 case ARM::fixup_arm_ldst_pcrel_12:
80 case ARM::fixup_arm_pcrel_10:
81 case ARM::fixup_arm_adr_pcrel_12:
82 case ARM::fixup_arm_condbranch:
83 case ARM::fixup_arm_uncondbranch:
84 RelocType = unsigned(macho::RIT_ARM_Branch24Bit);
85 // Report as 'long', even though that is not quite accurate.
86 Log2Size = llvm::Log2_32(4);
87 return true;
88
89 // Handle Thumb branches.
90 case ARM::fixup_arm_thumb_br:
91 RelocType = unsigned(macho::RIT_ARM_ThumbBranch22Bit);
92 Log2Size = llvm::Log2_32(2);
93 return true;
94
95 case ARM::fixup_t2_uncondbranch:
96 case ARM::fixup_arm_thumb_bl:
97 case ARM::fixup_arm_thumb_blx:
98 RelocType = unsigned(macho::RIT_ARM_ThumbBranch22Bit);
99 Log2Size = llvm::Log2_32(4);
100 return true;
101
102 case ARM::fixup_arm_movt_hi16:
103 case ARM::fixup_arm_movt_hi16_pcrel:
104 case ARM::fixup_t2_movt_hi16:
105 case ARM::fixup_t2_movt_hi16_pcrel:
106 RelocType = unsigned(macho::RIT_ARM_HalfDifference);
107 // Report as 'long', even though that is not quite accurate.
108 Log2Size = llvm::Log2_32(4);
109 return true;
110
111 case ARM::fixup_arm_movw_lo16:
112 case ARM::fixup_arm_movw_lo16_pcrel:
113 case ARM::fixup_t2_movw_lo16:
114 case ARM::fixup_t2_movw_lo16_pcrel:
115 RelocType = unsigned(macho::RIT_ARM_Half);
116 // Report as 'long', even though that is not quite accurate.
117 Log2Size = llvm::Log2_32(4);
118 return true;
119 }
120}
121
122void ARMMachObjectWriter::
123RecordARMMovwMovtRelocation(MachObjectWriter *Writer,
124 const MCAssembler &Asm,
125 const MCAsmLayout &Layout,
126 const MCFragment *Fragment,
127 const MCFixup &Fixup,
128 MCValue Target,
129 uint64_t &FixedValue) {
130 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
131 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
132 unsigned Type = macho::RIT_ARM_Half;
133
134 // See <reloc.h>.
135 const MCSymbol *A = &Target.getSymA()->getSymbol();
136 MCSymbolData *A_SD = &Asm.getSymbolData(*A);
137
138 if (!A_SD->getFragment())
139 report_fatal_error("symbol '" + A->getName() +
140 "' can not be undefined in a subtraction expression");
141
142 uint32_t Value = Writer->getSymbolAddress(A_SD, Layout);
143 uint32_t Value2 = 0;
144 uint64_t SecAddr =
145 Writer->getSectionAddress(A_SD->getFragment()->getParent());
146 FixedValue += SecAddr;
147
148 if (const MCSymbolRefExpr *B = Target.getSymB()) {
149 MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol());
150
151 if (!B_SD->getFragment())
152 report_fatal_error("symbol '" + B->getSymbol().getName() +
153 "' can not be undefined in a subtraction expression");
154
155 // Select the appropriate difference relocation type.
156 Type = macho::RIT_ARM_HalfDifference;
157 Value2 = Writer->getSymbolAddress(B_SD, Layout);
158 FixedValue -= Writer->getSectionAddress(B_SD->getFragment()->getParent());
159 }
160
161 // Relocations are written out in reverse order, so the PAIR comes first.
162 // ARM_RELOC_HALF and ARM_RELOC_HALF_SECTDIFF abuse the r_length field:
163 //
164 // For these two r_type relocations they always have a pair following them and
165 // the r_length bits are used differently. The encoding of the r_length is as
166 // follows:
167 // low bit of r_length:
168 // 0 - :lower16: for movw instructions
169 // 1 - :upper16: for movt instructions
170 // high bit of r_length:
171 // 0 - arm instructions
172 // 1 - thumb instructions
173 // the other half of the relocated expression is in the following pair
174 // relocation entry in the the low 16 bits of r_address field.
175 unsigned ThumbBit = 0;
176 unsigned MovtBit = 0;
177 switch ((unsigned)Fixup.getKind()) {
178 default: break;
179 case ARM::fixup_arm_movt_hi16:
180 case ARM::fixup_arm_movt_hi16_pcrel:
181 MovtBit = 1;
Jim Grosbach62006112011-11-29 01:15:25 +0000182 // The thumb bit shouldn't be set in the 'other-half' bit of the
183 // relocation, but it will be set in FixedValue if the base symbol
184 // is a thumb function. Clear it out here.
185 if (A_SD->getFlags() & SF_ThumbFunc)
186 FixedValue &= 0xfffffffe;
Jim Grosbachba8297e2011-06-24 23:44:37 +0000187 break;
188 case ARM::fixup_t2_movt_hi16:
189 case ARM::fixup_t2_movt_hi16_pcrel:
Jim Grosbach62006112011-11-29 01:15:25 +0000190 if (A_SD->getFlags() & SF_ThumbFunc)
191 FixedValue &= 0xfffffffe;
Jim Grosbachba8297e2011-06-24 23:44:37 +0000192 MovtBit = 1;
193 // Fallthrough
194 case ARM::fixup_t2_movw_lo16:
195 case ARM::fixup_t2_movw_lo16_pcrel:
196 ThumbBit = 1;
197 break;
198 }
199
Jim Grosbachba8297e2011-06-24 23:44:37 +0000200 if (Type == macho::RIT_ARM_HalfDifference) {
201 uint32_t OtherHalf = MovtBit
202 ? (FixedValue & 0xffff) : ((FixedValue & 0xffff0000) >> 16);
203
204 macho::RelocationEntry MRE;
205 MRE.Word0 = ((OtherHalf << 0) |
206 (macho::RIT_Pair << 24) |
207 (MovtBit << 28) |
208 (ThumbBit << 29) |
209 (IsPCRel << 30) |
210 macho::RF_Scattered);
211 MRE.Word1 = Value2;
212 Writer->addRelocation(Fragment->getParent(), MRE);
213 }
214
215 macho::RelocationEntry MRE;
216 MRE.Word0 = ((FixupOffset << 0) |
217 (Type << 24) |
218 (MovtBit << 28) |
219 (ThumbBit << 29) |
220 (IsPCRel << 30) |
221 macho::RF_Scattered);
222 MRE.Word1 = Value;
223 Writer->addRelocation(Fragment->getParent(), MRE);
224}
225
226void ARMMachObjectWriter::RecordARMScatteredRelocation(MachObjectWriter *Writer,
227 const MCAssembler &Asm,
228 const MCAsmLayout &Layout,
229 const MCFragment *Fragment,
230 const MCFixup &Fixup,
231 MCValue Target,
232 unsigned Log2Size,
233 uint64_t &FixedValue) {
234 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
235 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
236 unsigned Type = macho::RIT_Vanilla;
237
238 // See <reloc.h>.
239 const MCSymbol *A = &Target.getSymA()->getSymbol();
240 MCSymbolData *A_SD = &Asm.getSymbolData(*A);
241
242 if (!A_SD->getFragment())
243 report_fatal_error("symbol '" + A->getName() +
244 "' can not be undefined in a subtraction expression");
245
246 uint32_t Value = Writer->getSymbolAddress(A_SD, Layout);
247 uint64_t SecAddr = Writer->getSectionAddress(A_SD->getFragment()->getParent());
248 FixedValue += SecAddr;
249 uint32_t Value2 = 0;
250
251 if (const MCSymbolRefExpr *B = Target.getSymB()) {
252 MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol());
253
254 if (!B_SD->getFragment())
255 report_fatal_error("symbol '" + B->getSymbol().getName() +
256 "' can not be undefined in a subtraction expression");
257
258 // Select the appropriate difference relocation type.
259 Type = macho::RIT_Difference;
260 Value2 = Writer->getSymbolAddress(B_SD, Layout);
261 FixedValue -= Writer->getSectionAddress(B_SD->getFragment()->getParent());
262 }
263
264 // Relocations are written out in reverse order, so the PAIR comes first.
265 if (Type == macho::RIT_Difference ||
266 Type == macho::RIT_Generic_LocalDifference) {
267 macho::RelocationEntry MRE;
268 MRE.Word0 = ((0 << 0) |
269 (macho::RIT_Pair << 24) |
270 (Log2Size << 28) |
271 (IsPCRel << 30) |
272 macho::RF_Scattered);
273 MRE.Word1 = Value2;
274 Writer->addRelocation(Fragment->getParent(), MRE);
275 }
276
277 macho::RelocationEntry MRE;
278 MRE.Word0 = ((FixupOffset << 0) |
279 (Type << 24) |
280 (Log2Size << 28) |
281 (IsPCRel << 30) |
282 macho::RF_Scattered);
283 MRE.Word1 = Value;
284 Writer->addRelocation(Fragment->getParent(), MRE);
285}
286
287void ARMMachObjectWriter::RecordRelocation(MachObjectWriter *Writer,
288 const MCAssembler &Asm,
289 const MCAsmLayout &Layout,
290 const MCFragment *Fragment,
291 const MCFixup &Fixup,
292 MCValue Target,
293 uint64_t &FixedValue) {
294 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
295 unsigned Log2Size;
296 unsigned RelocType = macho::RIT_Vanilla;
297 if (!getARMFixupKindMachOInfo(Fixup.getKind(), RelocType, Log2Size)) {
298 report_fatal_error("unknown ARM fixup kind!");
299 return;
300 }
301
302 // If this is a difference or a defined symbol plus an offset, then we need a
303 // scattered relocation entry. Differences always require scattered
304 // relocations.
305 if (Target.getSymB()) {
306 if (RelocType == macho::RIT_ARM_Half ||
307 RelocType == macho::RIT_ARM_HalfDifference)
308 return RecordARMMovwMovtRelocation(Writer, Asm, Layout, Fragment, Fixup,
309 Target, FixedValue);
310 return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
311 Target, Log2Size, FixedValue);
312 }
313
314 // Get the symbol data, if any.
315 MCSymbolData *SD = 0;
316 if (Target.getSymA())
317 SD = &Asm.getSymbolData(Target.getSymA()->getSymbol());
318
319 // FIXME: For other platforms, we need to use scattered relocations for
320 // internal relocations with offsets. If this is an internal relocation with
321 // an offset, it also needs a scattered relocation entry.
322 //
323 // Is this right for ARM?
324 uint32_t Offset = Target.getConstant();
325 if (IsPCRel && RelocType == macho::RIT_Vanilla)
326 Offset += 1 << Log2Size;
327 if (Offset && SD && !Writer->doesSymbolRequireExternRelocation(SD))
328 return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
329 Target, Log2Size, FixedValue);
330
331 // See <reloc.h>.
332 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
333 unsigned Index = 0;
334 unsigned IsExtern = 0;
335 unsigned Type = 0;
336
337 if (Target.isAbsolute()) { // constant
338 // FIXME!
339 report_fatal_error("FIXME: relocations to absolute targets "
340 "not yet implemented");
341 } else {
342 // Resolve constant variables.
343 if (SD->getSymbol().isVariable()) {
344 int64_t Res;
345 if (SD->getSymbol().getVariableValue()->EvaluateAsAbsolute(
346 Res, Layout, Writer->getSectionAddressMap())) {
347 FixedValue = Res;
348 return;
349 }
350 }
351
352 // Check whether we need an external or internal relocation.
353 if (Writer->doesSymbolRequireExternRelocation(SD)) {
354 IsExtern = 1;
355 Index = SD->getIndex();
356
357 // For external relocations, make sure to offset the fixup value to
358 // compensate for the addend of the symbol address, if it was
359 // undefined. This occurs with weak definitions, for example.
360 if (!SD->Symbol->isUndefined())
361 FixedValue -= Layout.getSymbolOffset(SD);
362 } else {
363 // The index is the section ordinal (1-based).
364 const MCSectionData &SymSD = Asm.getSectionData(
365 SD->getSymbol().getSection());
366 Index = SymSD.getOrdinal() + 1;
367 FixedValue += Writer->getSectionAddress(&SymSD);
368 }
369 if (IsPCRel)
370 FixedValue -= Writer->getSectionAddress(Fragment->getParent());
371
372 // The type is determined by the fixup kind.
373 Type = RelocType;
374 }
375
376 // struct relocation_info (8 bytes)
377 macho::RelocationEntry MRE;
378 MRE.Word0 = FixupOffset;
379 MRE.Word1 = ((Index << 0) |
380 (IsPCRel << 24) |
381 (Log2Size << 25) |
382 (IsExtern << 27) |
383 (Type << 28));
384 Writer->addRelocation(Fragment->getParent(), MRE);
385}
386
Jim Grosbach2fc68982011-06-22 20:14:52 +0000387MCObjectWriter *llvm::createARMMachObjectWriter(raw_ostream &OS,
388 bool Is64Bit,
389 uint32_t CPUType,
390 uint32_t CPUSubtype) {
391 return createMachObjectWriter(new ARMMachObjectWriter(Is64Bit,
392 CPUType,
393 CPUSubtype),
394 OS, /*IsLittleEndian=*/true);
395}