LLVM  9.0.0svn
ARMMachObjectWriter.cpp
Go to the documentation of this file.
1 //===-- ARMMachObjectWriter.cpp - ARM Mach Object Writer ------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
12 #include "llvm/ADT/Twine.h"
14 #include "llvm/MC/MCAsmLayout.h"
15 #include "llvm/MC/MCAssembler.h"
16 #include "llvm/MC/MCContext.h"
17 #include "llvm/MC/MCExpr.h"
18 #include "llvm/MC/MCFixup.h"
21 #include "llvm/MC/MCSection.h"
22 #include "llvm/MC/MCValue.h"
25 
26 using namespace llvm;
27 
28 namespace {
29 class ARMMachObjectWriter : public MCMachObjectTargetWriter {
30  void RecordARMScatteredRelocation(MachObjectWriter *Writer,
31  const MCAssembler &Asm,
32  const MCAsmLayout &Layout,
33  const MCFragment *Fragment,
34  const MCFixup &Fixup,
36  unsigned Type,
37  unsigned Log2Size,
38  uint64_t &FixedValue);
39  void RecordARMScatteredHalfRelocation(MachObjectWriter *Writer,
40  const MCAssembler &Asm,
41  const MCAsmLayout &Layout,
42  const MCFragment *Fragment,
43  const MCFixup &Fixup, MCValue Target,
44  uint64_t &FixedValue);
45 
46  bool requiresExternRelocation(MachObjectWriter *Writer,
47  const MCAssembler &Asm,
48  const MCFragment &Fragment, unsigned RelocType,
49  const MCSymbol &S, uint64_t FixedValue);
50 
51 public:
52  ARMMachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype)
53  : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype) {}
54 
55  void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm,
56  const MCAsmLayout &Layout, const MCFragment *Fragment,
57  const MCFixup &Fixup, MCValue Target,
58  uint64_t &FixedValue) override;
59 };
60 }
61 
62 static bool getARMFixupKindMachOInfo(unsigned Kind, unsigned &RelocType,
63  unsigned &Log2Size) {
65  Log2Size = ~0U;
66 
67  switch (Kind) {
68  default:
69  return false;
70 
71  case FK_Data_1:
72  Log2Size = llvm::Log2_32(1);
73  return true;
74  case FK_Data_2:
75  Log2Size = llvm::Log2_32(2);
76  return true;
77  case FK_Data_4:
78  Log2Size = llvm::Log2_32(4);
79  return true;
80  case FK_Data_8:
81  Log2Size = llvm::Log2_32(8);
82  return true;
83 
84  // These fixups are expected to always be resolvable at assembly time and
85  // have no relocations supported.
90  return false;
91 
92  // Handle 24-bit branch kinds.
97  case ARM::fixup_arm_blx:
98  RelocType = unsigned(MachO::ARM_RELOC_BR24);
99  // Report as 'long', even though that is not quite accurate.
100  Log2Size = llvm::Log2_32(4);
101  return true;
102 
107  Log2Size = llvm::Log2_32(4);
108  return true;
109 
110  // For movw/movt r_type relocations they always have a pair following them and
111  // the r_length bits are used differently. The encoding of the r_length is as
112  // follows:
113  // low bit of r_length:
114  // 0 - :lower16: for movw instructions
115  // 1 - :upper16: for movt instructions
116  // high bit of r_length:
117  // 0 - arm instructions
118  // 1 - thumb instructions
120  RelocType = unsigned(MachO::ARM_RELOC_HALF);
121  Log2Size = 1;
122  return true;
124  RelocType = unsigned(MachO::ARM_RELOC_HALF);
125  Log2Size = 3;
126  return true;
127 
129  RelocType = unsigned(MachO::ARM_RELOC_HALF);
130  Log2Size = 0;
131  return true;
133  RelocType = unsigned(MachO::ARM_RELOC_HALF);
134  Log2Size = 2;
135  return true;
136  }
137 }
138 
139 void ARMMachObjectWriter::
140 RecordARMScatteredHalfRelocation(MachObjectWriter *Writer,
141  const MCAssembler &Asm,
142  const MCAsmLayout &Layout,
143  const MCFragment *Fragment,
144  const MCFixup &Fixup,
145  MCValue Target,
146  uint64_t &FixedValue) {
147  uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
148 
149  if (FixupOffset & 0xff000000) {
150  Asm.getContext().reportError(Fixup.getLoc(),
151  "can not encode offset '0x" +
152  to_hexString(FixupOffset) +
153  "' in resulting scattered relocation.");
154  return;
155  }
156 
157  unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
158  unsigned Type = MachO::ARM_RELOC_HALF;
159 
160  // See <reloc.h>.
161  const MCSymbol *A = &Target.getSymA()->getSymbol();
162 
163  if (!A->getFragment()) {
164  Asm.getContext().reportError(Fixup.getLoc(),
165  "symbol '" + A->getName() +
166  "' can not be undefined in a subtraction expression");
167  return;
168  }
169 
170  uint32_t Value = Writer->getSymbolAddress(*A, Layout);
171  uint32_t Value2 = 0;
172  uint64_t SecAddr = Writer->getSectionAddress(A->getFragment()->getParent());
173  FixedValue += SecAddr;
174 
175  if (const MCSymbolRefExpr *B = Target.getSymB()) {
176  const MCSymbol *SB = &B->getSymbol();
177 
178  if (!SB->getFragment()) {
179  Asm.getContext().reportError(Fixup.getLoc(),
180  "symbol '" + B->getSymbol().getName() +
181  "' can not be undefined in a subtraction expression");
182  return;
183  }
184 
185  // Select the appropriate difference relocation type.
187  Value2 = Writer->getSymbolAddress(B->getSymbol(), Layout);
188  FixedValue -= Writer->getSectionAddress(SB->getFragment()->getParent());
189  }
190 
191  // Relocations are written out in reverse order, so the PAIR comes first.
192  // ARM_RELOC_HALF and ARM_RELOC_HALF_SECTDIFF abuse the r_length field:
193  //
194  // For these two r_type relocations they always have a pair following them and
195  // the r_length bits are used differently. The encoding of the r_length is as
196  // follows:
197  // low bit of r_length:
198  // 0 - :lower16: for movw instructions
199  // 1 - :upper16: for movt instructions
200  // high bit of r_length:
201  // 0 - arm instructions
202  // 1 - thumb instructions
203  // the other half of the relocated expression is in the following pair
204  // relocation entry in the low 16 bits of r_address field.
205  unsigned ThumbBit = 0;
206  unsigned MovtBit = 0;
207  switch ((unsigned)Fixup.getKind()) {
208  default: break;
210  MovtBit = 1;
211  // The thumb bit shouldn't be set in the 'other-half' bit of the
212  // relocation, but it will be set in FixedValue if the base symbol
213  // is a thumb function. Clear it out here.
214  if (Asm.isThumbFunc(A))
215  FixedValue &= 0xfffffffe;
216  break;
218  if (Asm.isThumbFunc(A))
219  FixedValue &= 0xfffffffe;
220  MovtBit = 1;
223  ThumbBit = 1;
224  break;
225  }
226 
227  if (Type == MachO::ARM_RELOC_HALF_SECTDIFF) {
228  uint32_t OtherHalf = MovtBit
229  ? (FixedValue & 0xffff) : ((FixedValue & 0xffff0000) >> 16);
230 
232  MRE.r_word0 = ((OtherHalf << 0) |
233  (MachO::ARM_RELOC_PAIR << 24) |
234  (MovtBit << 28) |
235  (ThumbBit << 29) |
236  (IsPCRel << 30) |
238  MRE.r_word1 = Value2;
239  Writer->addRelocation(nullptr, Fragment->getParent(), MRE);
240  }
241 
243  MRE.r_word0 = ((FixupOffset << 0) |
244  (Type << 24) |
245  (MovtBit << 28) |
246  (ThumbBit << 29) |
247  (IsPCRel << 30) |
249  MRE.r_word1 = Value;
250  Writer->addRelocation(nullptr, Fragment->getParent(), MRE);
251 }
252 
253 void ARMMachObjectWriter::RecordARMScatteredRelocation(MachObjectWriter *Writer,
254  const MCAssembler &Asm,
255  const MCAsmLayout &Layout,
256  const MCFragment *Fragment,
257  const MCFixup &Fixup,
258  MCValue Target,
259  unsigned Type,
260  unsigned Log2Size,
261  uint64_t &FixedValue) {
262  uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
263 
264  if (FixupOffset & 0xff000000) {
265  Asm.getContext().reportError(Fixup.getLoc(),
266  "can not encode offset '0x" +
267  to_hexString(FixupOffset) +
268  "' in resulting scattered relocation.");
269  return;
270  }
271 
272  unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
273 
274  // See <reloc.h>.
275  const MCSymbol *A = &Target.getSymA()->getSymbol();
276 
277  if (!A->getFragment()) {
278  Asm.getContext().reportError(Fixup.getLoc(),
279  "symbol '" + A->getName() +
280  "' can not be undefined in a subtraction expression");
281  return;
282  }
283 
284  uint32_t Value = Writer->getSymbolAddress(*A, Layout);
285  uint64_t SecAddr = Writer->getSectionAddress(A->getFragment()->getParent());
286  FixedValue += SecAddr;
287  uint32_t Value2 = 0;
288 
289  if (const MCSymbolRefExpr *B = Target.getSymB()) {
290  assert(Type == MachO::ARM_RELOC_VANILLA && "invalid reloc for 2 symbols");
291  const MCSymbol *SB = &B->getSymbol();
292 
293  if (!SB->getFragment()) {
294  Asm.getContext().reportError(Fixup.getLoc(),
295  "symbol '" + B->getSymbol().getName() +
296  "' can not be undefined in a subtraction expression");
297  return;
298  }
299 
300  // Select the appropriate difference relocation type.
302  Value2 = Writer->getSymbolAddress(B->getSymbol(), Layout);
303  FixedValue -= Writer->getSectionAddress(SB->getFragment()->getParent());
304  }
305 
306  // Relocations are written out in reverse order, so the PAIR comes first.
307  if (Type == MachO::ARM_RELOC_SECTDIFF ||
310  MRE.r_word0 = ((0 << 0) |
311  (MachO::ARM_RELOC_PAIR << 24) |
312  (Log2Size << 28) |
313  (IsPCRel << 30) |
315  MRE.r_word1 = Value2;
316  Writer->addRelocation(nullptr, Fragment->getParent(), MRE);
317  }
318 
320  MRE.r_word0 = ((FixupOffset << 0) |
321  (Type << 24) |
322  (Log2Size << 28) |
323  (IsPCRel << 30) |
325  MRE.r_word1 = Value;
326  Writer->addRelocation(nullptr, Fragment->getParent(), MRE);
327 }
328 
329 bool ARMMachObjectWriter::requiresExternRelocation(MachObjectWriter *Writer,
330  const MCAssembler &Asm,
331  const MCFragment &Fragment,
332  unsigned RelocType,
333  const MCSymbol &S,
334  uint64_t FixedValue) {
335  // Most cases can be identified purely from the symbol.
336  if (Writer->doesSymbolRequireExternRelocation(S))
337  return true;
338  int64_t Value = (int64_t)FixedValue; // The displacement is signed.
339  int64_t Range;
340  switch (RelocType) {
341  default:
342  return false;
344  // An ARM call might be to a Thumb function, in which case the offset may
345  // not be encodable in the instruction and we must use an external
346  // relocation that explicitly mentions the function. Not a problem if it's
347  // to a temporary "Lwhatever" symbol though, and in fact trying to use an
348  // external relocation there causes more issues.
349  if (!S.isTemporary())
350  return true;
351 
352  // PC pre-adjustment of 8 for these instructions.
353  Value -= 8;
354  // ARM BL/BLX has a 25-bit offset.
355  Range = 0x1ffffff;
356  break;
358  // PC pre-adjustment of 4 for these instructions.
359  Value -= 4;
360  // Thumb BL/BLX has a 24-bit offset.
361  Range = 0xffffff;
362  }
363  // BL/BLX also use external relocations when an internal relocation
364  // would result in the target being out of range. This gives the linker
365  // enough information to generate a branch island.
366  Value += Writer->getSectionAddress(&S.getSection());
367  Value -= Writer->getSectionAddress(Fragment.getParent());
368  // If the resultant value would be out of range for an internal relocation,
369  // use an external instead.
370  if (Value > Range || Value < -(Range + 1))
371  return true;
372  return false;
373 }
374 
375 void ARMMachObjectWriter::recordRelocation(MachObjectWriter *Writer,
376  MCAssembler &Asm,
377  const MCAsmLayout &Layout,
378  const MCFragment *Fragment,
379  const MCFixup &Fixup, MCValue Target,
380  uint64_t &FixedValue) {
381  unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
382  unsigned Log2Size;
383  unsigned RelocType = MachO::ARM_RELOC_VANILLA;
384  if (!getARMFixupKindMachOInfo(Fixup.getKind(), RelocType, Log2Size)) {
385  // If we failed to get fixup kind info, it's because there's no legal
386  // relocation type for the fixup kind. This happens when it's a fixup that's
387  // expected to always be resolvable at assembly time and not have any
388  // relocations needed.
389  Asm.getContext().reportError(Fixup.getLoc(),
390  "unsupported relocation on symbol");
391  return;
392  }
393 
394  // If this is a difference or a defined symbol plus an offset, then we need a
395  // scattered relocation entry. Differences always require scattered
396  // relocations.
397  if (Target.getSymB()) {
398  if (RelocType == MachO::ARM_RELOC_HALF)
399  return RecordARMScatteredHalfRelocation(Writer, Asm, Layout, Fragment,
400  Fixup, Target, FixedValue);
401  return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
402  Target, RelocType, Log2Size,
403  FixedValue);
404  }
405 
406  // Get the symbol data, if any.
407  const MCSymbol *A = nullptr;
408  if (Target.getSymA())
409  A = &Target.getSymA()->getSymbol();
410 
411  // FIXME: For other platforms, we need to use scattered relocations for
412  // internal relocations with offsets. If this is an internal relocation with
413  // an offset, it also needs a scattered relocation entry.
414  //
415  // Is this right for ARM?
416  uint32_t Offset = Target.getConstant();
417  if (IsPCRel && RelocType == MachO::ARM_RELOC_VANILLA)
418  Offset += 1 << Log2Size;
419  if (Offset && A && !Writer->doesSymbolRequireExternRelocation(*A) &&
420  RelocType != MachO::ARM_RELOC_HALF)
421  return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
422  Target, RelocType, Log2Size,
423  FixedValue);
424 
425  // See <reloc.h>.
426  uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
427  unsigned Index = 0;
428  unsigned Type = 0;
429  const MCSymbol *RelSymbol = nullptr;
430 
431  if (Target.isAbsolute()) { // constant
432  // FIXME!
433  report_fatal_error("FIXME: relocations to absolute targets "
434  "not yet implemented");
435  } else {
436  // Resolve constant variables.
437  if (A->isVariable()) {
438  int64_t Res;
439  if (A->getVariableValue()->evaluateAsAbsolute(
440  Res, Layout, Writer->getSectionAddressMap())) {
441  FixedValue = Res;
442  return;
443  }
444  }
445 
446  // Check whether we need an external or internal relocation.
447  if (requiresExternRelocation(Writer, Asm, *Fragment, RelocType, *A,
448  FixedValue)) {
449  RelSymbol = A;
450 
451  // For external relocations, make sure to offset the fixup value to
452  // compensate for the addend of the symbol address, if it was
453  // undefined. This occurs with weak definitions, for example.
454  if (!A->isUndefined())
455  FixedValue -= Layout.getSymbolOffset(*A);
456  } else {
457  // The index is the section ordinal (1-based).
458  const MCSection &Sec = A->getSection();
459  Index = Sec.getOrdinal() + 1;
460  FixedValue += Writer->getSectionAddress(&Sec);
461  }
462  if (IsPCRel)
463  FixedValue -= Writer->getSectionAddress(Fragment->getParent());
464 
465  // The type is determined by the fixup kind.
466  Type = RelocType;
467  }
468 
469  // struct relocation_info (8 bytes)
471  MRE.r_word0 = FixupOffset;
472  MRE.r_word1 =
473  (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
474 
475  // Even when it's not a scattered relocation, movw/movt always uses
476  // a PAIR relocation.
477  if (Type == MachO::ARM_RELOC_HALF) {
478  // The entire addend is needed to correctly apply a relocation. One half is
479  // extracted from the instruction itself, the other comes from this
480  // PAIR. I.e. it's correct that we insert the high bits of the addend in the
481  // MOVW case here. relocation entries.
482  uint32_t Value = 0;
483  switch ((unsigned)Fixup.getKind()) {
484  default: break;
487  Value = (FixedValue >> 16) & 0xffff;
488  break;
491  Value = FixedValue & 0xffff;
492  break;
493  }
495  MREPair.r_word0 = Value;
496  MREPair.r_word1 = ((0xffffff << 0) |
497  (Log2Size << 25) |
498  (MachO::ARM_RELOC_PAIR << 28));
499 
500  Writer->addRelocation(nullptr, Fragment->getParent(), MREPair);
501  }
502 
503  Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
504 }
505 
506 std::unique_ptr<MCObjectTargetWriter>
508  uint32_t CPUSubtype) {
509  return llvm::make_unique<ARMMachObjectWriter>(Is64Bit, CPUType, CPUSubtype);
510 }
Instances of this class represent a uniqued identifier for a section in the current translation unit...
Definition: MCSection.h:38
uint64_t getSymbolAddress(const MCSymbol &S, const MCAsmLayout &Layout) const
bool doesSymbolRequireExternRelocation(const MCSymbol &S)
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
Definition: Error.cpp:139
This class represents lattice values for constants.
Definition: AllocatorList.h:23
bool isVariable() const
isVariable - Check if this is a variable symbol.
Definition: MCSymbol.h:297
This represents an "assembler immediate".
Definition: MCValue.h:39
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
Definition: MCSymbol.h:41
bool isAbsolute() const
Is this an absolute (as opposed to relocatable) value.
Definition: MCValue.h:52
unsigned getOrdinal() const
Definition: MCSection.h:123
Encode information on a single operation to perform on a byte sequence (e.g., an encoded instruction)...
Definition: MCFixup.h:74
MCContext & getContext() const
Definition: MCAssembler.h:284
int64_t getConstant() const
Definition: MCValue.h:46
const MCSymbolRefExpr * getSymB() const
Definition: MCValue.h:48
Encapsulates the layout of an assembly file at a particular point in time.
Definition: MCAsmLayout.h:28
Represent a reference to a symbol from inside an expression.
Definition: MCExpr.h:165
A four-byte fixup.
Definition: MCFixup.h:26
bool isFixupKindPCRel(const MCAssembler &Asm, unsigned Kind)
void addRelocation(const MCSymbol *RelSymbol, const MCSection *Sec, MachO::any_relocation_info &MRE)
SectionAddrMap & getSectionAddressMap()
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
The instances of the Type class are immutable: once they are created, they are never changed...
Definition: Type.h:45
static bool getARMFixupKindMachOInfo(unsigned Kind, unsigned &RelocType, unsigned &Log2Size)
bool getSymbolOffset(const MCSymbol &S, uint64_t &Val) const
Get the offset of the given symbol, as computed in the current layout.
Definition: MCFragment.cpp:129
bool isTemporary() const
isTemporary - Check if this is an assembler temporary symbol.
Definition: MCSymbol.h:221
const MCSymbolRefExpr * getSymA() const
Definition: MCValue.h:47
void reportError(SMLoc L, const Twine &Msg)
Definition: MCContext.cpp:644
uint32_t getOffset() const
Definition: MCFixup.h:125
A one-byte fixup.
Definition: MCFixup.h:24
uint64_t getFragmentOffset(const MCFragment *F) const
Get the offset of the given fragment inside its containing section.
Definition: MCFragment.cpp:77
PowerPC TLS Dynamic Call Fixup
SMLoc getLoc() const
Definition: MCFixup.h:166
const MCSymbol & getSymbol() const
Definition: MCExpr.h:336
bool isUndefined(bool SetUsed=true) const
isUndefined - Check if this symbol undefined (i.e., implicitly defined).
Definition: MCSymbol.h:258
MCFragment * getFragment(bool SetUsed=true) const
Definition: MCSymbol.h:396
unsigned Log2_32(uint32_t Value)
Return the floor log base 2 of the specified value, -1 if the value is zero.
Definition: MathExtras.h:538
Target - Wrapper for Target specific information.
bool isThumbFunc(const MCSymbol *Func) const
Check whether a given symbol has been flagged with .thumb_func.
MCSection * getParent() const
Definition: MCFragment.h:98
const std::string to_hexString(uint64_t Value, bool UpperCase=true)
MCSection & getSection() const
Get the section associated with a defined, non-absolute symbol.
Definition: MCSymbol.h:268
std::unique_ptr< MCObjectTargetWriter > createARMMachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype)
Construct an ARM Mach-O object writer.
A eight-byte fixup.
Definition: MCFixup.h:27
uint64_t getSectionAddress(const MCSection *Sec) const
StringRef getName() const
getName - Get the symbol name.
Definition: MCSymbol.h:204
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
const MCExpr * getVariableValue(bool SetUsed=true) const
getVariableValue - Get the value for variable symbols.
Definition: MCSymbol.h:302
LLVM Value Representation.
Definition: Value.h:72
#define LLVM_FALLTHROUGH
LLVM_FALLTHROUGH - Mark fallthrough cases in switch statements.
Definition: Compiler.h:250
A two-byte fixup.
Definition: MCFixup.h:25
MCFixupKind getKind() const
Definition: MCFixup.h:123