blob: cbdeaa7d72238d8dcf9204fc9adfb1351f8c8e30 [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!");
Jim Grosbachba8297e2011-06-24 23:44:37 +0000299 }
300
301 // If this is a difference or a defined symbol plus an offset, then we need a
302 // scattered relocation entry. Differences always require scattered
303 // relocations.
304 if (Target.getSymB()) {
305 if (RelocType == macho::RIT_ARM_Half ||
306 RelocType == macho::RIT_ARM_HalfDifference)
307 return RecordARMMovwMovtRelocation(Writer, Asm, Layout, Fragment, Fixup,
308 Target, FixedValue);
309 return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
310 Target, Log2Size, FixedValue);
311 }
312
313 // Get the symbol data, if any.
314 MCSymbolData *SD = 0;
315 if (Target.getSymA())
316 SD = &Asm.getSymbolData(Target.getSymA()->getSymbol());
317
318 // FIXME: For other platforms, we need to use scattered relocations for
319 // internal relocations with offsets. If this is an internal relocation with
320 // an offset, it also needs a scattered relocation entry.
321 //
322 // Is this right for ARM?
323 uint32_t Offset = Target.getConstant();
324 if (IsPCRel && RelocType == macho::RIT_Vanilla)
325 Offset += 1 << Log2Size;
326 if (Offset && SD && !Writer->doesSymbolRequireExternRelocation(SD))
327 return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
328 Target, Log2Size, FixedValue);
329
330 // See <reloc.h>.
331 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
332 unsigned Index = 0;
333 unsigned IsExtern = 0;
334 unsigned Type = 0;
335
336 if (Target.isAbsolute()) { // constant
337 // FIXME!
338 report_fatal_error("FIXME: relocations to absolute targets "
339 "not yet implemented");
340 } else {
341 // Resolve constant variables.
342 if (SD->getSymbol().isVariable()) {
343 int64_t Res;
344 if (SD->getSymbol().getVariableValue()->EvaluateAsAbsolute(
345 Res, Layout, Writer->getSectionAddressMap())) {
346 FixedValue = Res;
347 return;
348 }
349 }
350
351 // Check whether we need an external or internal relocation.
352 if (Writer->doesSymbolRequireExternRelocation(SD)) {
353 IsExtern = 1;
354 Index = SD->getIndex();
355
356 // For external relocations, make sure to offset the fixup value to
357 // compensate for the addend of the symbol address, if it was
358 // undefined. This occurs with weak definitions, for example.
359 if (!SD->Symbol->isUndefined())
360 FixedValue -= Layout.getSymbolOffset(SD);
361 } else {
362 // The index is the section ordinal (1-based).
363 const MCSectionData &SymSD = Asm.getSectionData(
364 SD->getSymbol().getSection());
365 Index = SymSD.getOrdinal() + 1;
366 FixedValue += Writer->getSectionAddress(&SymSD);
367 }
368 if (IsPCRel)
369 FixedValue -= Writer->getSectionAddress(Fragment->getParent());
370
371 // The type is determined by the fixup kind.
372 Type = RelocType;
373 }
374
375 // struct relocation_info (8 bytes)
376 macho::RelocationEntry MRE;
377 MRE.Word0 = FixupOffset;
378 MRE.Word1 = ((Index << 0) |
379 (IsPCRel << 24) |
380 (Log2Size << 25) |
381 (IsExtern << 27) |
382 (Type << 28));
383 Writer->addRelocation(Fragment->getParent(), MRE);
384}
385
Jim Grosbach2fc68982011-06-22 20:14:52 +0000386MCObjectWriter *llvm::createARMMachObjectWriter(raw_ostream &OS,
387 bool Is64Bit,
388 uint32_t CPUType,
389 uint32_t CPUSubtype) {
390 return createMachObjectWriter(new ARMMachObjectWriter(Is64Bit,
391 CPUType,
392 CPUSubtype),
393 OS, /*IsLittleEndian=*/true);
394}