ArchHandler_arm.cpp revision 280461
1280461Sdim//===- lib/FileFormat/MachO/ArchHandler_arm.cpp ---------------------------===//
2280461Sdim//
3280461Sdim//                             The LLVM Linker
4280461Sdim//
5280461Sdim// This file is distributed under the University of Illinois Open Source
6280461Sdim// License. See LICENSE.TXT for details.
7280461Sdim//
8280461Sdim//===----------------------------------------------------------------------===//
9280461Sdim
10280461Sdim#include "ArchHandler.h"
11280461Sdim#include "Atoms.h"
12280461Sdim#include "MachONormalizedFileBinaryUtils.h"
13280461Sdim#include "llvm/ADT/StringRef.h"
14280461Sdim#include "llvm/ADT/StringSwitch.h"
15280461Sdim#include "llvm/ADT/Triple.h"
16280461Sdim#include "llvm/Support/Endian.h"
17280461Sdim#include "llvm/Support/ErrorHandling.h"
18280461Sdim
19280461Sdimusing namespace llvm::MachO;
20280461Sdimusing namespace lld::mach_o::normalized;
21280461Sdim
22280461Sdimnamespace lld {
23280461Sdimnamespace mach_o {
24280461Sdim
25280461Sdimusing llvm::support::ulittle32_t;
26280461Sdimusing llvm::support::little32_t;
27280461Sdim
28280461Sdim
29280461Sdimclass ArchHandler_arm : public ArchHandler {
30280461Sdimpublic:
31280461Sdim           ArchHandler_arm();
32280461Sdim  virtual ~ArchHandler_arm();
33280461Sdim
34280461Sdim  const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
35280461Sdim
36280461Sdim  Reference::KindArch kindArch() override { return Reference::KindArch::ARM; }
37280461Sdim
38280461Sdim  const ArchHandler::StubInfo &stubInfo() override;
39280461Sdim  bool isCallSite(const Reference &) override;
40280461Sdim  bool isPointer(const Reference &) override;
41280461Sdim  bool isPairedReloc(const normalized::Relocation &) override;
42280461Sdim  bool isNonCallBranch(const Reference &) override;
43280461Sdim
44280461Sdim  bool needsCompactUnwind() override {
45280461Sdim    return false;
46280461Sdim  }
47280461Sdim  Reference::KindValue imageOffsetKind() override {
48280461Sdim    return invalid;
49280461Sdim  }
50280461Sdim  Reference::KindValue imageOffsetKindIndirect() override {
51280461Sdim    return invalid;
52280461Sdim  }
53280461Sdim
54280461Sdim  Reference::KindValue unwindRefToCIEKind() override {
55280461Sdim    return invalid;
56280461Sdim  }
57280461Sdim
58280461Sdim  Reference::KindValue unwindRefToFunctionKind() override {
59280461Sdim    return invalid;
60280461Sdim  }
61280461Sdim
62280461Sdim  Reference::KindValue unwindRefToEhFrameKind() override {
63280461Sdim    return invalid;
64280461Sdim  }
65280461Sdim
66280461Sdim  uint32_t dwarfCompactUnwindType() override {
67280461Sdim    // FIXME
68280461Sdim    return -1;
69280461Sdim  }
70280461Sdim
71280461Sdim  std::error_code getReferenceInfo(const normalized::Relocation &reloc,
72280461Sdim                                   const DefinedAtom *inAtom,
73280461Sdim                                   uint32_t offsetInAtom,
74280461Sdim                                   uint64_t fixupAddress, bool swap,
75280461Sdim                                   FindAtomBySectionAndAddress atomFromAddress,
76280461Sdim                                   FindAtomBySymbolIndex atomFromSymbolIndex,
77280461Sdim                                   Reference::KindValue *kind,
78280461Sdim                                   const lld::Atom **target,
79280461Sdim                                   Reference::Addend *addend) override;
80280461Sdim  std::error_code
81280461Sdim      getPairReferenceInfo(const normalized::Relocation &reloc1,
82280461Sdim                           const normalized::Relocation &reloc2,
83280461Sdim                           const DefinedAtom *inAtom,
84280461Sdim                           uint32_t offsetInAtom,
85280461Sdim                           uint64_t fixupAddress, bool swap, bool scatterable,
86280461Sdim                           FindAtomBySectionAndAddress atomFromAddress,
87280461Sdim                           FindAtomBySymbolIndex atomFromSymbolIndex,
88280461Sdim                           Reference::KindValue *kind,
89280461Sdim                           const lld::Atom **target,
90280461Sdim                           Reference::Addend *addend) override;
91280461Sdim
92280461Sdim  void generateAtomContent(const DefinedAtom &atom, bool relocatable,
93280461Sdim                           FindAddressForAtom findAddress,
94280461Sdim                           FindAddressForAtom findSectionAddress,
95280461Sdim                           uint64_t imageBaseAddress,
96280461Sdim                           uint8_t *atomContentBuffer) override;
97280461Sdim
98280461Sdim  void appendSectionRelocations(const DefinedAtom &atom,
99280461Sdim                                uint64_t atomSectionOffset,
100280461Sdim                                const Reference &ref,
101280461Sdim                                FindSymbolIndexForAtom,
102280461Sdim                                FindSectionIndexForAtom,
103280461Sdim                                FindAddressForAtom,
104280461Sdim                                normalized::Relocations &) override;
105280461Sdim
106280461Sdim  void addAdditionalReferences(MachODefinedAtom &atom) override;
107280461Sdim
108280461Sdim  bool isDataInCodeTransition(Reference::KindValue refKind) override {
109280461Sdim    switch (refKind) {
110280461Sdim    case modeThumbCode:
111280461Sdim    case modeArmCode:
112280461Sdim    case modeData:
113280461Sdim      return true;
114280461Sdim    default:
115280461Sdim      return false;
116280461Sdim      break;
117280461Sdim    }
118280461Sdim  }
119280461Sdim
120280461Sdim  Reference::KindValue dataInCodeTransitionStart(
121280461Sdim                                        const MachODefinedAtom &atom) override {
122280461Sdim    return modeData;
123280461Sdim  }
124280461Sdim
125280461Sdim  Reference::KindValue dataInCodeTransitionEnd(
126280461Sdim                                        const MachODefinedAtom &atom) override {
127280461Sdim    return atom.isThumb() ? modeThumbCode : modeArmCode;
128280461Sdim  }
129280461Sdim
130280461Sdim  bool isThumbFunction(const DefinedAtom &atom) override;
131280461Sdim  const DefinedAtom *createShim(MachOFile &file, bool thumbToArm,
132280461Sdim                                const DefinedAtom &) override;
133280461Sdim
134280461Sdimprivate:
135280461Sdim  friend class Thumb2ToArmShimAtom;
136280461Sdim  friend class ArmToThumbShimAtom;
137280461Sdim
138280461Sdim  static const Registry::KindStrings _sKindStrings[];
139280461Sdim  static const StubInfo              _sStubInfoArmPIC;
140280461Sdim
141280461Sdim  enum ArmKind : Reference::KindValue {
142280461Sdim    invalid,               /// for error condition
143280461Sdim
144280461Sdim    modeThumbCode,         /// Content starting at this offset is thumb.
145280461Sdim    modeArmCode,           /// Content starting at this offset is arm.
146280461Sdim    modeData,              /// Content starting at this offset is data.
147280461Sdim
148280461Sdim    // Kinds found in mach-o .o files:
149280461Sdim    thumb_bl22,            /// ex: bl _foo
150280461Sdim    thumb_b22,             /// ex: b _foo
151280461Sdim    thumb_movw,            /// ex: movw	r1, :lower16:_foo
152280461Sdim    thumb_movt,            /// ex: movt	r1, :lower16:_foo
153280461Sdim    thumb_movw_funcRel,    /// ex: movw	r1, :lower16:(_foo-(L1+4))
154280461Sdim    thumb_movt_funcRel,    /// ex: movt r1, :upper16:(_foo-(L1+4))
155280461Sdim    arm_bl24,              /// ex: bl _foo
156280461Sdim    arm_b24,               /// ex: b _foo
157280461Sdim    arm_movw,              /// ex: movw	r1, :lower16:_foo
158280461Sdim    arm_movt,              /// ex: movt	r1, :lower16:_foo
159280461Sdim    arm_movw_funcRel,      /// ex: movw	r1, :lower16:(_foo-(L1+4))
160280461Sdim    arm_movt_funcRel,      /// ex: movt r1, :upper16:(_foo-(L1+4))
161280461Sdim    pointer32,             /// ex: .long _foo
162280461Sdim    delta32,               /// ex: .long _foo - .
163280461Sdim
164280461Sdim    // Kinds introduced by Passes:
165280461Sdim    lazyPointer,           /// Location contains a lazy pointer.
166280461Sdim    lazyImmediateLocation, /// Location contains immediate value used in stub.
167280461Sdim  };
168280461Sdim
169280461Sdim  // Utility functions for inspecting/updating instructions.
170280461Sdim  static bool isThumbMovw(uint32_t instruction);
171280461Sdim  static bool isThumbMovt(uint32_t instruction);
172280461Sdim  static bool isArmMovw(uint32_t instruction);
173280461Sdim  static bool isArmMovt(uint32_t instruction);
174280461Sdim  static int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t);
175280461Sdim  static int32_t getDisplacementFromArmBranch(uint32_t instruction);
176280461Sdim  static uint16_t getWordFromThumbMov(uint32_t instruction);
177280461Sdim  static uint16_t getWordFromArmMov(uint32_t instruction);
178280461Sdim  static uint32_t clearThumbBit(uint32_t value, const Atom *target);
179280461Sdim  static uint32_t setDisplacementInArmBranch(uint32_t instr, int32_t disp,
180280461Sdim                                             bool targetIsThumb);
181280461Sdim  static uint32_t setDisplacementInThumbBranch(uint32_t instr, uint32_t ia,
182280461Sdim                                               int32_t disp, bool targetThumb);
183280461Sdim  static uint32_t setWordFromThumbMov(uint32_t instruction, uint16_t word);
184280461Sdim  static uint32_t setWordFromArmMov(uint32_t instruction, uint16_t word);
185280461Sdim
186280461Sdim  StringRef stubName(const DefinedAtom &);
187280461Sdim  bool useExternalRelocationTo(const Atom &target);
188280461Sdim
189280461Sdim  void applyFixupFinal(const Reference &ref, uint8_t *location,
190280461Sdim                       uint64_t fixupAddress, uint64_t targetAddress,
191280461Sdim                       uint64_t inAtomAddress, bool &thumbMode,
192280461Sdim                       bool targetIsThumb);
193280461Sdim
194280461Sdim  void applyFixupRelocatable(const Reference &ref, uint8_t *location,
195280461Sdim                             uint64_t fixupAddress,
196280461Sdim                             uint64_t targetAddress,
197280461Sdim                             uint64_t inAtomAddress, bool &thumbMode,
198280461Sdim                             bool targetIsThumb);
199280461Sdim};
200280461Sdim
201280461Sdim//===----------------------------------------------------------------------===//
202280461Sdim//  ArchHandler_arm
203280461Sdim//===----------------------------------------------------------------------===//
204280461Sdim
205280461SdimArchHandler_arm::ArchHandler_arm() { }
206280461Sdim
207280461SdimArchHandler_arm::~ArchHandler_arm() { }
208280461Sdim
209280461Sdimconst Registry::KindStrings ArchHandler_arm::_sKindStrings[] = {
210280461Sdim  LLD_KIND_STRING_ENTRY(invalid),
211280461Sdim  LLD_KIND_STRING_ENTRY(modeThumbCode),
212280461Sdim  LLD_KIND_STRING_ENTRY(modeArmCode),
213280461Sdim  LLD_KIND_STRING_ENTRY(modeData),
214280461Sdim  LLD_KIND_STRING_ENTRY(thumb_bl22),
215280461Sdim  LLD_KIND_STRING_ENTRY(thumb_b22),
216280461Sdim  LLD_KIND_STRING_ENTRY(thumb_movw),
217280461Sdim  LLD_KIND_STRING_ENTRY(thumb_movt),
218280461Sdim  LLD_KIND_STRING_ENTRY(thumb_movw_funcRel),
219280461Sdim  LLD_KIND_STRING_ENTRY(thumb_movt_funcRel),
220280461Sdim  LLD_KIND_STRING_ENTRY(arm_bl24),
221280461Sdim  LLD_KIND_STRING_ENTRY(arm_b24),
222280461Sdim  LLD_KIND_STRING_ENTRY(arm_movw),
223280461Sdim  LLD_KIND_STRING_ENTRY(arm_movt),
224280461Sdim  LLD_KIND_STRING_ENTRY(arm_movw_funcRel),
225280461Sdim  LLD_KIND_STRING_ENTRY(arm_movt_funcRel),
226280461Sdim  LLD_KIND_STRING_ENTRY(pointer32),
227280461Sdim  LLD_KIND_STRING_ENTRY(delta32),
228280461Sdim  LLD_KIND_STRING_ENTRY(lazyPointer),
229280461Sdim  LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
230280461Sdim  LLD_KIND_STRING_END
231280461Sdim};
232280461Sdim
233280461Sdimconst ArchHandler::StubInfo ArchHandler_arm::_sStubInfoArmPIC = {
234280461Sdim  "dyld_stub_binder",
235280461Sdim
236280461Sdim  // References in lazy pointer
237280461Sdim  { Reference::KindArch::ARM, pointer32, 0, 0 },
238280461Sdim  { Reference::KindArch::ARM, lazyPointer, 0, 0 },
239280461Sdim
240280461Sdim  // GOT pointer to dyld_stub_binder
241280461Sdim  { Reference::KindArch::ARM, pointer32, 0, 0 },
242280461Sdim
243280461Sdim  // arm code alignment 2^2
244280461Sdim  2,
245280461Sdim
246280461Sdim  // Stub size and code
247280461Sdim  16,
248280461Sdim  { 0x04, 0xC0, 0x9F, 0xE5,       // 	ldr ip, pc + 12
249280461Sdim    0x0C, 0xC0, 0x8F, 0xE0,       //  add ip, pc, ip
250280461Sdim    0x00, 0xF0, 0x9C, 0xE5,       // 	ldr pc, [ip]
251280461Sdim    0x00, 0x00, 0x00, 0x00 },     // 	.long L_foo$lazy_ptr - (L1$scv + 8)
252280461Sdim  { Reference::KindArch::ARM, delta32, 12, 0 },
253280461Sdim  { false, 0, 0, 0 },
254280461Sdim
255280461Sdim  // Stub Helper size and code
256280461Sdim  12,
257280461Sdim  { 0x00, 0xC0, 0x9F, 0xE5,       // ldr   ip, [pc, #0]
258280461Sdim    0x00, 0x00, 0x00, 0xEA,       // b	     _helperhelper
259280461Sdim    0x00, 0x00, 0x00, 0x00 },     // .long  lazy-info-offset
260280461Sdim  { Reference::KindArch::ARM, lazyImmediateLocation, 8, 0 },
261280461Sdim  { Reference::KindArch::ARM, arm_b24, 4, 0 },
262280461Sdim
263280461Sdim  // Stub Helper-Common size and code
264280461Sdim  36,
265280461Sdim	{ // push lazy-info-offset
266280461Sdim    0x04, 0xC0, 0x2D, 0xE5,       // str ip, [sp, #-4]!
267280461Sdim		// push address of dyld_mageLoaderCache
268280461Sdim    0x10, 0xC0, 0x9F, 0xE5,       // ldr	ip, L1
269280461Sdim    0x0C, 0xC0, 0x8F, 0xE0,       // add	ip, pc, ip
270280461Sdim    0x04, 0xC0, 0x2D, 0xE5,       // str ip, [sp, #-4]!
271280461Sdim		// jump through dyld_stub_binder
272280461Sdim    0x08, 0xC0, 0x9F, 0xE5,       // ldr	ip, L2
273280461Sdim    0x0C, 0xC0, 0x8F, 0xE0,       // add	ip, pc, ip
274280461Sdim    0x00, 0xF0, 0x9C, 0xE5,       // ldr	pc, [ip]
275280461Sdim    0x00, 0x00, 0x00, 0x00,       // L1: .long fFastStubGOTAtom - (helper+16)
276280461Sdim    0x00, 0x00, 0x00, 0x00 },     // L2: .long dyld_stub_binder - (helper+28)
277280461Sdim  { Reference::KindArch::ARM, delta32, 28, 0xC },
278280461Sdim  { false, 0, 0, 0 },
279280461Sdim  { Reference::KindArch::ARM, delta32, 32, 0x04 },
280280461Sdim  { false, 0, 0, 0 }
281280461Sdim};
282280461Sdim
283280461Sdimconst ArchHandler::StubInfo &ArchHandler_arm::stubInfo() {
284280461Sdim  // If multiple kinds of stubs are supported, select which StubInfo here.
285280461Sdim  return _sStubInfoArmPIC;
286280461Sdim}
287280461Sdim
288280461Sdimbool ArchHandler_arm::isCallSite(const Reference &ref) {
289280461Sdim  switch (ref.kindValue()) {
290280461Sdim  case thumb_b22:
291280461Sdim  case thumb_bl22:
292280461Sdim  case arm_b24:
293280461Sdim  case arm_bl24:
294280461Sdim    return true;
295280461Sdim  default:
296280461Sdim    return false;
297280461Sdim  }
298280461Sdim}
299280461Sdim
300280461Sdimbool ArchHandler_arm::isPointer(const Reference &ref) {
301280461Sdim  return (ref.kindValue() == pointer32);
302280461Sdim}
303280461Sdim
304280461Sdimbool ArchHandler_arm::isNonCallBranch(const Reference &ref) {
305280461Sdim  switch (ref.kindValue()) {
306280461Sdim  case thumb_b22:
307280461Sdim  case arm_b24:
308280461Sdim    return true;
309280461Sdim  default:
310280461Sdim    return false;
311280461Sdim  }
312280461Sdim}
313280461Sdim
314280461Sdimbool ArchHandler_arm::isPairedReloc(const Relocation &reloc) {
315280461Sdim  switch (reloc.type) {
316280461Sdim  case ARM_RELOC_SECTDIFF:
317280461Sdim  case ARM_RELOC_LOCAL_SECTDIFF:
318280461Sdim  case ARM_RELOC_HALF_SECTDIFF:
319280461Sdim  case ARM_RELOC_HALF:
320280461Sdim    return true;
321280461Sdim  default:
322280461Sdim    return false;
323280461Sdim  }
324280461Sdim}
325280461Sdim
326280461Sdim/// Trace references from stub atom to lazy pointer to target and get its name.
327280461SdimStringRef ArchHandler_arm::stubName(const DefinedAtom &stubAtom) {
328280461Sdim  assert(stubAtom.contentType() == DefinedAtom::typeStub);
329280461Sdim  for (const Reference *ref : stubAtom) {
330280461Sdim    if (const DefinedAtom* lp = dyn_cast<DefinedAtom>(ref->target())) {
331280461Sdim      if (lp->contentType() != DefinedAtom::typeLazyPointer)
332280461Sdim        continue;
333280461Sdim      for (const Reference *ref2 : *lp) {
334280461Sdim        if (ref2->kindValue() != lazyPointer)
335280461Sdim          continue;
336280461Sdim        return ref2->target()->name();
337280461Sdim      }
338280461Sdim    }
339280461Sdim  }
340280461Sdim  return "stub";
341280461Sdim}
342280461Sdim
343280461Sdim/// Extract displacement from an ARM b/bl/blx instruction.
344280461Sdimint32_t ArchHandler_arm::getDisplacementFromArmBranch(uint32_t instruction) {
345280461Sdim  // Sign-extend imm24
346280461Sdim  int32_t displacement = (instruction & 0x00FFFFFF) << 2;
347280461Sdim  if ((displacement & 0x02000000) != 0)
348280461Sdim    displacement |= 0xFC000000;
349280461Sdim  // If this is BLX and H bit set, add 2.
350280461Sdim  if ((instruction & 0xFF000000) == 0xFB000000)
351280461Sdim    displacement += 2;
352280461Sdim  return displacement;
353280461Sdim}
354280461Sdim
355280461Sdim/// Update an ARM b/bl/blx instruction, switching bl <-> blx as needed.
356280461Sdimuint32_t ArchHandler_arm::setDisplacementInArmBranch(uint32_t instruction,
357280461Sdim                                                     int32_t displacement,
358280461Sdim                                                     bool targetIsThumb) {
359280461Sdim  assert((displacement <= 33554428) && (displacement > (-33554432))
360280461Sdim                                              && "arm branch out of range");
361280461Sdim  bool is_blx = ((instruction & 0xF0000000) == 0xF0000000);
362280461Sdim  uint32_t newInstruction = (instruction & 0xFF000000);
363280461Sdim  uint32_t h = 0;
364280461Sdim  if (targetIsThumb) {
365280461Sdim    // Force use of BLX.
366280461Sdim    newInstruction = 0xFA000000;
367280461Sdim    if (!is_blx) {
368280461Sdim      assert(((instruction & 0xF0000000) == 0xE0000000)
369280461Sdim                                                   && "no conditional arm blx");
370280461Sdim      assert(((instruction & 0xFF000000) == 0xEB000000)
371280461Sdim                                             && "no arm pc-rel BX instruction");
372280461Sdim    }
373280461Sdim    if (displacement & 2)
374280461Sdim      h = 1;
375280461Sdim  }
376280461Sdim  else {
377280461Sdim    // Force use of B/BL.
378280461Sdim    if (is_blx)
379280461Sdim      newInstruction = 0xEB000000;
380280461Sdim  }
381280461Sdim  newInstruction |= (h << 24) | ((displacement >> 2) & 0x00FFFFFF);
382280461Sdim  return newInstruction;
383280461Sdim}
384280461Sdim
385280461Sdim/// Extract displacement from a thumb b/bl/blx instruction.
386280461Sdimint32_t ArchHandler_arm::getDisplacementFromThumbBranch(uint32_t instruction,
387280461Sdim                                                        uint32_t instrAddr) {
388280461Sdim  bool is_blx = ((instruction & 0xD000F800) == 0xC000F000);
389280461Sdim  uint32_t s = (instruction >> 10) & 0x1;
390280461Sdim  uint32_t j1 = (instruction >> 29) & 0x1;
391280461Sdim  uint32_t j2 = (instruction >> 27) & 0x1;
392280461Sdim  uint32_t imm10 = instruction & 0x3FF;
393280461Sdim  uint32_t imm11 = (instruction >> 16) & 0x7FF;
394280461Sdim  uint32_t i1 = (j1 == s);
395280461Sdim  uint32_t i2 = (j2 == s);
396280461Sdim  uint32_t dis =
397280461Sdim      (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);
398280461Sdim  int32_t sdis = dis;
399280461Sdim  int32_t result = s ? (sdis | 0xFE000000) : sdis;
400280461Sdim  if (is_blx && (instrAddr & 0x2)) {
401280461Sdim    // The thumb blx instruction always has low bit of imm11 as zero.  The way
402280461Sdim    // a 2-byte aligned blx can branch to a 4-byte aligned ARM target is that
403280461Sdim    // the blx instruction always 4-byte aligns the pc before adding the
404280461Sdim    // displacement from the blx.  We must emulate that when decoding this.
405280461Sdim    result -= 2;
406280461Sdim  }
407280461Sdim  return result;
408280461Sdim}
409280461Sdim
410280461Sdim/// Update a thumb b/bl/blx instruction, switching bl <-> blx as needed.
411280461Sdimuint32_t ArchHandler_arm::setDisplacementInThumbBranch(uint32_t instruction,
412280461Sdim                                                       uint32_t instrAddr,
413280461Sdim                                                       int32_t displacement,
414280461Sdim                                                       bool targetIsThumb) {
415280461Sdim  assert((displacement <= 16777214) && (displacement > (-16777216))
416280461Sdim                                              && "thumb branch out of range");
417280461Sdim	bool is_bl = ((instruction & 0xD000F800) == 0xD000F000);
418280461Sdim	bool is_blx = ((instruction & 0xD000F800) == 0xC000F000);
419280461Sdim	bool is_b = ((instruction & 0xD000F800) == 0x9000F000);
420280461Sdim  uint32_t newInstruction = (instruction & 0xD000F800);
421280461Sdim  if (is_bl || is_blx) {
422280461Sdim    if (targetIsThumb) {
423280461Sdim      newInstruction = 0xD000F000; // Use bl
424280461Sdim    } else {
425280461Sdim      newInstruction = 0xC000F000; // Use blx
426280461Sdim      // See note in getDisplacementFromThumbBranch() about blx.
427280461Sdim      if (instrAddr & 0x2)
428280461Sdim        displacement += 2;
429280461Sdim    }
430280461Sdim  } else if (is_b) {
431280461Sdim    assert(targetIsThumb && "no pc-rel thumb branch instruction that "
432280461Sdim                             "switches to arm mode");
433280461Sdim  }
434280461Sdim  else {
435280461Sdim    llvm_unreachable("thumb branch22 reloc on a non-branch instruction");
436280461Sdim  }
437280461Sdim  uint32_t s = (uint32_t)(displacement >> 24) & 0x1;
438280461Sdim  uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1;
439280461Sdim  uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1;
440280461Sdim  uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF;
441280461Sdim  uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF;
442280461Sdim  uint32_t j1 = (i1 == s);
443280461Sdim  uint32_t j2 = (i2 == s);
444280461Sdim  uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11;
445280461Sdim  uint32_t firstDisp = (s << 10) | imm10;
446280461Sdim  newInstruction |= (nextDisp << 16) | firstDisp;
447280461Sdim  return newInstruction;
448280461Sdim}
449280461Sdim
450280461Sdimbool ArchHandler_arm::isThumbMovw(uint32_t instruction) {
451280461Sdim  return (instruction & 0x8000FBF0) == 0x0000F240;
452280461Sdim}
453280461Sdim
454280461Sdimbool ArchHandler_arm::isThumbMovt(uint32_t instruction) {
455280461Sdim  return (instruction & 0x8000FBF0) == 0x0000F2C0;
456280461Sdim}
457280461Sdim
458280461Sdimbool ArchHandler_arm::isArmMovw(uint32_t instruction) {
459280461Sdim  return (instruction & 0x0FF00000) == 0x03000000;
460280461Sdim}
461280461Sdim
462280461Sdimbool ArchHandler_arm::isArmMovt(uint32_t instruction) {
463280461Sdim  return (instruction & 0x0FF00000) == 0x03400000;
464280461Sdim}
465280461Sdim
466280461Sdim
467280461Sdimuint16_t ArchHandler_arm::getWordFromThumbMov(uint32_t instruction) {
468280461Sdim  assert(isThumbMovw(instruction) || isThumbMovt(instruction));
469280461Sdim  uint32_t i = ((instruction & 0x00000400) >> 10);
470280461Sdim  uint32_t imm4 = (instruction & 0x0000000F);
471280461Sdim  uint32_t imm3 = ((instruction & 0x70000000) >> 28);
472280461Sdim  uint32_t imm8 = ((instruction & 0x00FF0000) >> 16);
473280461Sdim  return (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8;
474280461Sdim}
475280461Sdim
476280461Sdimuint16_t ArchHandler_arm::getWordFromArmMov(uint32_t instruction) {
477280461Sdim  assert(isArmMovw(instruction) || isArmMovt(instruction));
478280461Sdim  uint32_t imm4 = ((instruction & 0x000F0000) >> 16);
479280461Sdim  uint32_t imm12 = (instruction & 0x00000FFF);
480280461Sdim  return (imm4 << 12) | imm12;
481280461Sdim}
482280461Sdim
483280461Sdim
484280461Sdimuint32_t ArchHandler_arm::setWordFromThumbMov(uint32_t instr, uint16_t word) {
485280461Sdim  assert(isThumbMovw(instr) || isThumbMovt(instr));
486280461Sdim  uint32_t imm4 = (word & 0xF000) >> 12;
487280461Sdim  uint32_t i =    (word & 0x0800) >> 11;
488280461Sdim  uint32_t imm3 = (word & 0x0700) >> 8;
489280461Sdim  uint32_t imm8 =  word & 0x00FF;
490280461Sdim	return (instr & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16);
491280461Sdim}
492280461Sdim
493280461Sdimuint32_t ArchHandler_arm::setWordFromArmMov(uint32_t instr, uint16_t word) {
494280461Sdim  assert(isArmMovw(instr) || isArmMovt(instr));
495280461Sdim  uint32_t imm4 = (word & 0xF000) >> 12;
496280461Sdim  uint32_t imm12 = word & 0x0FFF;
497280461Sdim  return (instr & 0xFFF0F000) | (imm4 << 16) | imm12;
498280461Sdim}
499280461Sdim
500280461Sdim
501280461Sdimuint32_t ArchHandler_arm::clearThumbBit(uint32_t value, const Atom *target) {
502280461Sdim  // The assembler often adds one to the address of a thumb function.
503280461Sdim  // We need to undo that so it does not look like an addend.
504280461Sdim  if (value & 1) {
505280461Sdim    if (isa<DefinedAtom>(target)) {
506280461Sdim      const MachODefinedAtom *machoTarget =
507280461Sdim          reinterpret_cast<const MachODefinedAtom *>(target);
508280461Sdim      if (machoTarget->isThumb())
509280461Sdim        value &= -2; // mask off thumb-bit
510280461Sdim    }
511280461Sdim  }
512280461Sdim  return value;
513280461Sdim}
514280461Sdim
515280461Sdimstd::error_code ArchHandler_arm::getReferenceInfo(
516280461Sdim    const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom,
517280461Sdim    uint64_t fixupAddress, bool isBig,
518280461Sdim    FindAtomBySectionAndAddress atomFromAddress,
519280461Sdim    FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind,
520280461Sdim    const lld::Atom **target, Reference::Addend *addend) {
521280461Sdim  typedef std::error_code E;
522280461Sdim  const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
523280461Sdim  uint64_t targetAddress;
524280461Sdim  uint32_t instruction = *(const ulittle32_t *)fixupContent;
525280461Sdim  int32_t displacement;
526280461Sdim  switch (relocPattern(reloc)) {
527280461Sdim  case ARM_THUMB_RELOC_BR22 | rPcRel | rExtern | rLength4:
528280461Sdim    // ex: bl _foo (and _foo is undefined)
529280461Sdim    if ((instruction & 0xD000F800) == 0x9000F000)
530280461Sdim      *kind = thumb_b22;
531280461Sdim    else
532280461Sdim      *kind = thumb_bl22;
533280461Sdim    if (E ec = atomFromSymbolIndex(reloc.symbol, target))
534280461Sdim      return ec;
535280461Sdim    // Instruction contains branch to addend.
536280461Sdim    displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
537280461Sdim    *addend = fixupAddress + 4 + displacement;
538280461Sdim    return std::error_code();
539280461Sdim  case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4:
540280461Sdim    // ex: bl _foo (and _foo is defined)
541280461Sdim    if ((instruction & 0xD000F800) == 0x9000F000)
542280461Sdim      *kind = thumb_b22;
543280461Sdim    else
544280461Sdim      *kind = thumb_bl22;
545280461Sdim    displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
546280461Sdim    targetAddress = fixupAddress + 4 + displacement;
547280461Sdim    return atomFromAddress(reloc.symbol, targetAddress, target, addend);
548280461Sdim  case ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4:
549280461Sdim    // ex: bl _foo+4 (and _foo is defined)
550280461Sdim    if ((instruction & 0xD000F800) == 0x9000F000)
551280461Sdim      *kind = thumb_b22;
552280461Sdim    else
553280461Sdim      *kind = thumb_bl22;
554280461Sdim    displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
555280461Sdim    targetAddress = fixupAddress + 4 + displacement;
556280461Sdim    if (E ec = atomFromAddress(0, reloc.value, target, addend))
557280461Sdim      return ec;
558280461Sdim    // reloc.value is target atom's address.  Instruction contains branch
559280461Sdim    // to atom+addend.
560280461Sdim    *addend += (targetAddress - reloc.value);
561280461Sdim    return std::error_code();
562280461Sdim  case ARM_RELOC_BR24 | rPcRel | rExtern | rLength4:
563280461Sdim    // ex: bl _foo (and _foo is undefined)
564280461Sdim    if (((instruction & 0x0F000000) == 0x0A000000)
565280461Sdim        && ((instruction & 0xF0000000) != 0xF0000000))
566280461Sdim      *kind = arm_b24;
567280461Sdim    else
568280461Sdim      *kind = arm_bl24;
569280461Sdim    if (E ec = atomFromSymbolIndex(reloc.symbol, target))
570280461Sdim      return ec;
571280461Sdim    // Instruction contains branch to addend.
572280461Sdim    displacement = getDisplacementFromArmBranch(instruction);
573280461Sdim    *addend = fixupAddress + 8 + displacement;
574280461Sdim    return std::error_code();
575280461Sdim  case ARM_RELOC_BR24 | rPcRel | rLength4:
576280461Sdim    // ex: bl _foo (and _foo is defined)
577280461Sdim    if (((instruction & 0x0F000000) == 0x0A000000)
578280461Sdim        && ((instruction & 0xF0000000) != 0xF0000000))
579280461Sdim      *kind = arm_b24;
580280461Sdim    else
581280461Sdim      *kind = arm_bl24;
582280461Sdim    displacement = getDisplacementFromArmBranch(instruction);
583280461Sdim    targetAddress = fixupAddress + 8 + displacement;
584280461Sdim    return atomFromAddress(reloc.symbol, targetAddress, target, addend);
585280461Sdim  case ARM_RELOC_BR24 | rScattered | rPcRel | rLength4:
586280461Sdim    // ex: bl _foo+4 (and _foo is defined)
587280461Sdim    if (((instruction & 0x0F000000) == 0x0A000000)
588280461Sdim        && ((instruction & 0xF0000000) != 0xF0000000))
589280461Sdim      *kind = arm_b24;
590280461Sdim    else
591280461Sdim      *kind = arm_bl24;
592280461Sdim    displacement = getDisplacementFromArmBranch(instruction);
593280461Sdim    targetAddress = fixupAddress + 8 + displacement;
594280461Sdim    if (E ec = atomFromAddress(0, reloc.value, target, addend))
595280461Sdim      return ec;
596280461Sdim    // reloc.value is target atom's address.  Instruction contains branch
597280461Sdim    // to atom+addend.
598280461Sdim    *addend += (targetAddress - reloc.value);
599280461Sdim    return std::error_code();
600280461Sdim  case ARM_RELOC_VANILLA | rExtern | rLength4:
601280461Sdim    // ex: .long _foo (and _foo is undefined)
602280461Sdim    *kind = pointer32;
603280461Sdim    if (E ec = atomFromSymbolIndex(reloc.symbol, target))
604280461Sdim      return ec;
605280461Sdim    *addend = instruction;
606280461Sdim    return std::error_code();
607280461Sdim  case ARM_RELOC_VANILLA | rLength4:
608280461Sdim    // ex: .long _foo (and _foo is defined)
609280461Sdim    *kind = pointer32;
610280461Sdim    if (E ec = atomFromAddress(reloc.symbol, instruction, target, addend))
611280461Sdim      return ec;
612280461Sdim    *addend = clearThumbBit((uint32_t) * addend, *target);
613280461Sdim    return std::error_code();
614280461Sdim  case ARM_RELOC_VANILLA | rScattered | rLength4:
615280461Sdim    // ex: .long _foo+a (and _foo is defined)
616280461Sdim    *kind = pointer32;
617280461Sdim    if (E ec = atomFromAddress(0, reloc.value, target, addend))
618280461Sdim      return ec;
619280461Sdim    *addend += (clearThumbBit(instruction, *target) - reloc.value);
620280461Sdim    return std::error_code();
621280461Sdim  default:
622280461Sdim    return make_dynamic_error_code(Twine("unsupported arm relocation type"));
623280461Sdim  }
624280461Sdim  return std::error_code();
625280461Sdim}
626280461Sdim
627280461Sdimstd::error_code
628280461SdimArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1,
629280461Sdim                                     const normalized::Relocation &reloc2,
630280461Sdim                                     const DefinedAtom *inAtom,
631280461Sdim                                     uint32_t offsetInAtom,
632280461Sdim                                     uint64_t fixupAddress, bool isBig,
633280461Sdim                                     bool scatterable,
634280461Sdim                                     FindAtomBySectionAndAddress atomFromAddr,
635280461Sdim                                     FindAtomBySymbolIndex atomFromSymbolIndex,
636280461Sdim                                     Reference::KindValue *kind,
637280461Sdim                                     const lld::Atom **target,
638280461Sdim                                     Reference::Addend *addend) {
639280461Sdim  bool pointerDiff = false;
640280461Sdim  bool funcRel;
641280461Sdim  bool top;
642280461Sdim  bool thumbReloc;
643280461Sdim  switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
644280461Sdim  case ((ARM_RELOC_HALF_SECTDIFF  | rScattered | rLenThmbLo) << 16 |
645280461Sdim         ARM_RELOC_PAIR           | rScattered | rLenThmbLo):
646280461Sdim    // ex: movw	r1, :lower16:(_x-L1) [thumb mode]
647280461Sdim    *kind = thumb_movw_funcRel;
648280461Sdim    funcRel = true;
649280461Sdim    top = false;
650280461Sdim    thumbReloc = true;
651280461Sdim    break;
652280461Sdim  case ((ARM_RELOC_HALF_SECTDIFF  | rScattered | rLenThmbHi) << 16 |
653280461Sdim         ARM_RELOC_PAIR           | rScattered | rLenThmbHi):
654280461Sdim    // ex: movt	r1, :upper16:(_x-L1) [thumb mode]
655280461Sdim    *kind = thumb_movt_funcRel;
656280461Sdim    funcRel = true;
657280461Sdim    top = true;
658280461Sdim    thumbReloc = true;
659280461Sdim    break;
660280461Sdim  case ((ARM_RELOC_HALF_SECTDIFF  | rScattered | rLenArmLo) << 16 |
661280461Sdim         ARM_RELOC_PAIR           | rScattered | rLenArmLo):
662280461Sdim    // ex: movw	r1, :lower16:(_x-L1) [arm mode]
663280461Sdim    *kind = arm_movw_funcRel;
664280461Sdim    funcRel = true;
665280461Sdim    top = false;
666280461Sdim    thumbReloc = false;
667280461Sdim    break;
668280461Sdim  case ((ARM_RELOC_HALF_SECTDIFF  | rScattered | rLenArmHi) << 16 |
669280461Sdim         ARM_RELOC_PAIR           | rScattered | rLenArmHi):
670280461Sdim    // ex: movt	r1, :upper16:(_x-L1) [arm mode]
671280461Sdim    *kind = arm_movt_funcRel;
672280461Sdim    funcRel = true;
673280461Sdim    top = true;
674280461Sdim    thumbReloc = false;
675280461Sdim    break;
676280461Sdim  case ((ARM_RELOC_HALF     | rLenThmbLo) << 16 |
677280461Sdim         ARM_RELOC_PAIR     | rLenThmbLo):
678280461Sdim    // ex: movw	r1, :lower16:_x [thumb mode]
679280461Sdim    *kind = thumb_movw;
680280461Sdim    funcRel = false;
681280461Sdim    top = false;
682280461Sdim    thumbReloc = true;
683280461Sdim    break;
684280461Sdim  case ((ARM_RELOC_HALF     | rLenThmbHi) << 16 |
685280461Sdim         ARM_RELOC_PAIR     | rLenThmbHi):
686280461Sdim    // ex: movt	r1, :upper16:_x [thumb mode]
687280461Sdim    *kind = thumb_movt;
688280461Sdim    funcRel = false;
689280461Sdim    top = true;
690280461Sdim    thumbReloc = true;
691280461Sdim    break;
692280461Sdim  case ((ARM_RELOC_HALF     | rLenArmLo) << 16 |
693280461Sdim         ARM_RELOC_PAIR     | rLenArmLo):
694280461Sdim    // ex: movw	r1, :lower16:_x [arm mode]
695280461Sdim    *kind = arm_movw;
696280461Sdim    funcRel = false;
697280461Sdim    top = false;
698280461Sdim    thumbReloc = false;
699280461Sdim    break;
700280461Sdim  case ((ARM_RELOC_HALF     | rLenArmHi) << 16 |
701280461Sdim         ARM_RELOC_PAIR     | rLenArmHi):
702280461Sdim    // ex: movt	r1, :upper16:_x [arm mode]
703280461Sdim    *kind = arm_movt;
704280461Sdim    funcRel = false;
705280461Sdim    top = true;
706280461Sdim    thumbReloc = false;
707280461Sdim    break;
708280461Sdim  case ((ARM_RELOC_HALF | rScattered  | rLenThmbLo) << 16 |
709280461Sdim         ARM_RELOC_PAIR               | rLenThmbLo):
710280461Sdim    // ex: movw	r1, :lower16:_x+a [thumb mode]
711280461Sdim    *kind = thumb_movw;
712280461Sdim    funcRel = false;
713280461Sdim    top = false;
714280461Sdim    thumbReloc = true;
715280461Sdim    break;
716280461Sdim  case ((ARM_RELOC_HALF | rScattered  | rLenThmbHi) << 16 |
717280461Sdim         ARM_RELOC_PAIR               | rLenThmbHi):
718280461Sdim    // ex: movt	r1, :upper16:_x+a [thumb mode]
719280461Sdim    *kind = thumb_movt;
720280461Sdim    funcRel = false;
721280461Sdim    top = true;
722280461Sdim    thumbReloc = true;
723280461Sdim    break;
724280461Sdim  case ((ARM_RELOC_HALF | rScattered  | rLenArmLo) << 16 |
725280461Sdim         ARM_RELOC_PAIR               | rLenArmLo):
726280461Sdim    // ex: movw	r1, :lower16:_x+a [arm mode]
727280461Sdim    *kind = arm_movw;
728280461Sdim    funcRel = false;
729280461Sdim    top = false;
730280461Sdim    thumbReloc = false;
731280461Sdim    break;
732280461Sdim  case ((ARM_RELOC_HALF | rScattered  | rLenArmHi) << 16 |
733280461Sdim         ARM_RELOC_PAIR               | rLenArmHi):
734280461Sdim    // ex: movt	r1, :upper16:_x+a [arm mode]
735280461Sdim    *kind = arm_movt;
736280461Sdim    funcRel = false;
737280461Sdim    top = true;
738280461Sdim    thumbReloc = false;
739280461Sdim    break;
740280461Sdim  case ((ARM_RELOC_HALF | rExtern   | rLenThmbLo) << 16 |
741280461Sdim         ARM_RELOC_PAIR             | rLenThmbLo):
742280461Sdim    // ex: movw	r1, :lower16:_undef [thumb mode]
743280461Sdim    *kind = thumb_movw;
744280461Sdim    funcRel = false;
745280461Sdim    top = false;
746280461Sdim    thumbReloc = true;
747280461Sdim    break;
748280461Sdim  case ((ARM_RELOC_HALF | rExtern   | rLenThmbHi) << 16 |
749280461Sdim         ARM_RELOC_PAIR             | rLenThmbHi):
750280461Sdim    // ex: movt	r1, :upper16:_undef [thumb mode]
751280461Sdim    *kind = thumb_movt;
752280461Sdim    funcRel = false;
753280461Sdim    top = true;
754280461Sdim    thumbReloc = true;
755280461Sdim    break;
756280461Sdim  case ((ARM_RELOC_HALF | rExtern   | rLenArmLo) << 16 |
757280461Sdim         ARM_RELOC_PAIR             | rLenArmLo):
758280461Sdim    // ex: movw	r1, :lower16:_undef [arm mode]
759280461Sdim    *kind = arm_movw;
760280461Sdim    funcRel = false;
761280461Sdim    top = false;
762280461Sdim    thumbReloc = false;
763280461Sdim    break;
764280461Sdim  case ((ARM_RELOC_HALF | rExtern   | rLenArmHi) << 16 |
765280461Sdim         ARM_RELOC_PAIR             | rLenArmHi):
766280461Sdim    // ex: movt	r1, :upper16:_undef [arm mode]
767280461Sdim    *kind = arm_movt;
768280461Sdim    funcRel = false;
769280461Sdim    top = true;
770280461Sdim    thumbReloc = false;
771280461Sdim    break;
772280461Sdim  case ((ARM_RELOC_SECTDIFF       | rScattered | rLength4) << 16 |
773280461Sdim         ARM_RELOC_PAIR           | rScattered | rLength4):
774280461Sdim  case ((ARM_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 |
775280461Sdim         ARM_RELOC_PAIR           | rScattered | rLength4):
776280461Sdim    // ex: .long _foo - .
777280461Sdim    pointerDiff = true;
778280461Sdim    break;
779280461Sdim  default:
780280461Sdim    return make_dynamic_error_code(Twine("unsupported arm relocation pair"));
781280461Sdim  }
782280461Sdim  const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
783280461Sdim  std::error_code ec;
784280461Sdim  uint32_t instruction = *(const ulittle32_t *)fixupContent;
785280461Sdim  uint32_t value;
786280461Sdim  uint32_t fromAddress;
787280461Sdim  uint32_t toAddress;
788280461Sdim  uint16_t instruction16;
789280461Sdim  uint16_t other16;
790280461Sdim  const lld::Atom *fromTarget;
791280461Sdim  Reference::Addend offsetInTo;
792280461Sdim  Reference::Addend offsetInFrom;
793280461Sdim  if (pointerDiff) {
794280461Sdim    toAddress = reloc1.value;
795280461Sdim    fromAddress = reloc2.value;
796280461Sdim    ec = atomFromAddr(0, toAddress, target, &offsetInTo);
797280461Sdim    if (ec)
798280461Sdim      return ec;
799280461Sdim    ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom);
800280461Sdim    if (ec)
801280461Sdim      return ec;
802280461Sdim    if (scatterable && (fromTarget != inAtom))
803280461Sdim      return make_dynamic_error_code(Twine("SECTDIFF relocation where "
804280461Sdim                                           "subtrahend label is not in atom"));
805280461Sdim    *kind = delta32;
806280461Sdim    value = clearThumbBit(instruction, *target);
807280461Sdim    *addend = (int32_t)(value - (toAddress - fixupAddress));
808280461Sdim  } else if (funcRel) {
809280461Sdim    toAddress = reloc1.value;
810280461Sdim    fromAddress = reloc2.value;
811280461Sdim    ec = atomFromAddr(0, toAddress, target, &offsetInTo);
812280461Sdim    if (ec)
813280461Sdim      return ec;
814280461Sdim    ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom);
815280461Sdim    if (ec)
816280461Sdim      return ec;
817280461Sdim    if (fromTarget != inAtom)
818280461Sdim      return make_dynamic_error_code(
819280461Sdim          Twine("ARM_RELOC_HALF_SECTDIFF relocation "
820280461Sdim                "where subtrahend label is not in atom"));
821280461Sdim    other16 = (reloc2.offset & 0xFFFF);
822280461Sdim    if (thumbReloc) {
823280461Sdim      if (top) {
824280461Sdim        if (!isThumbMovt(instruction))
825280461Sdim          return make_dynamic_error_code(Twine("expected movt instruction"));
826280461Sdim      }
827280461Sdim      else {
828280461Sdim        if (!isThumbMovw(instruction))
829280461Sdim          return make_dynamic_error_code(Twine("expected movw instruction"));
830280461Sdim      }
831280461Sdim      instruction16 = getWordFromThumbMov(instruction);
832280461Sdim    }
833280461Sdim    else {
834280461Sdim      if (top) {
835280461Sdim        if (!isArmMovt(instruction))
836280461Sdim          return make_dynamic_error_code(Twine("expected movt instruction"));
837280461Sdim      }
838280461Sdim      else {
839280461Sdim        if (!isArmMovw(instruction))
840280461Sdim          return make_dynamic_error_code(Twine("expected movw instruction"));
841280461Sdim      }
842280461Sdim      instruction16 = getWordFromArmMov(instruction);
843280461Sdim    }
844280461Sdim    if (top)
845280461Sdim      value = (instruction16 << 16) | other16;
846280461Sdim    else
847280461Sdim      value = (other16 << 16) | instruction16;
848280461Sdim    value = clearThumbBit(value, *target);
849280461Sdim    int64_t ta = (int64_t) value - (toAddress - fromAddress);
850280461Sdim    *addend = ta - offsetInFrom;
851280461Sdim    return std::error_code();
852280461Sdim  } else {
853280461Sdim    uint32_t sectIndex;
854280461Sdim    if (thumbReloc) {
855280461Sdim      if (top) {
856280461Sdim        if (!isThumbMovt(instruction))
857280461Sdim          return make_dynamic_error_code(Twine("expected movt instruction"));
858280461Sdim      }
859280461Sdim      else {
860280461Sdim        if (!isThumbMovw(instruction))
861280461Sdim          return make_dynamic_error_code(Twine("expected movw instruction"));
862280461Sdim      }
863280461Sdim      instruction16 = getWordFromThumbMov(instruction);
864280461Sdim    }
865280461Sdim    else {
866280461Sdim      if (top) {
867280461Sdim        if (!isArmMovt(instruction))
868280461Sdim          return make_dynamic_error_code(Twine("expected movt instruction"));
869280461Sdim      }
870280461Sdim      else {
871280461Sdim        if (!isArmMovw(instruction))
872280461Sdim          return make_dynamic_error_code(Twine("expected movw instruction"));
873280461Sdim      }
874280461Sdim      instruction16 = getWordFromArmMov(instruction);
875280461Sdim    }
876280461Sdim    other16 = (reloc2.offset & 0xFFFF);
877280461Sdim    if (top)
878280461Sdim      value = (instruction16 << 16) | other16;
879280461Sdim    else
880280461Sdim      value = (other16 << 16) | instruction16;
881280461Sdim    if (reloc1.isExtern) {
882280461Sdim      ec = atomFromSymbolIndex(reloc1.symbol, target);
883280461Sdim      if (ec)
884280461Sdim        return ec;
885280461Sdim      *addend = value;
886280461Sdim    } else {
887280461Sdim      if (reloc1.scattered) {
888280461Sdim        toAddress = reloc1.value;
889280461Sdim        sectIndex = 0;
890280461Sdim      } else {
891280461Sdim        toAddress = value;
892280461Sdim        sectIndex = reloc1.symbol;
893280461Sdim      }
894280461Sdim      ec = atomFromAddr(sectIndex, toAddress, target, &offsetInTo);
895280461Sdim      if (ec)
896280461Sdim        return ec;
897280461Sdim      *addend = value - toAddress;
898280461Sdim    }
899280461Sdim  }
900280461Sdim
901280461Sdim  return std::error_code();
902280461Sdim}
903280461Sdim
904280461Sdimvoid ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *loc,
905280461Sdim                                      uint64_t fixupAddress,
906280461Sdim                                      uint64_t targetAddress,
907280461Sdim                                      uint64_t inAtomAddress,
908280461Sdim                                      bool &thumbMode, bool targetIsThumb) {
909280461Sdim  if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
910280461Sdim    return;
911280461Sdim  assert(ref.kindArch() == Reference::KindArch::ARM);
912280461Sdim  ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
913280461Sdim  int32_t displacement;
914280461Sdim  uint16_t value16;
915280461Sdim  uint32_t value32;
916280461Sdim  switch (static_cast<ArmKind>(ref.kindValue())) {
917280461Sdim  case modeThumbCode:
918280461Sdim    thumbMode = true;
919280461Sdim    break;
920280461Sdim  case modeArmCode:
921280461Sdim    thumbMode = false;
922280461Sdim    break;
923280461Sdim  case modeData:
924280461Sdim    break;
925280461Sdim  case thumb_b22:
926280461Sdim  case thumb_bl22:
927280461Sdim    assert(thumbMode);
928280461Sdim    displacement = (targetAddress - (fixupAddress + 4)) + ref.addend();
929280461Sdim    value32 = setDisplacementInThumbBranch(*loc32, fixupAddress,
930280461Sdim                                           displacement, targetIsThumb);
931280461Sdim    *loc32 = value32;
932280461Sdim    break;
933280461Sdim  case thumb_movw:
934280461Sdim    assert(thumbMode);
935280461Sdim    value16 = (targetAddress + ref.addend()) & 0xFFFF;
936280461Sdim    if (targetIsThumb)
937280461Sdim      value16 |= 1;
938280461Sdim    *loc32 = setWordFromThumbMov(*loc32, value16);
939280461Sdim    break;
940280461Sdim  case thumb_movt:
941280461Sdim    assert(thumbMode);
942280461Sdim    value16 = (targetAddress + ref.addend()) >> 16;
943280461Sdim    *loc32 = setWordFromThumbMov(*loc32, value16);
944280461Sdim    break;
945280461Sdim  case thumb_movw_funcRel:
946280461Sdim    assert(thumbMode);
947280461Sdim    value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF;
948280461Sdim    if (targetIsThumb)
949280461Sdim      value16 |= 1;
950280461Sdim    *loc32 = setWordFromThumbMov(*loc32, value16);
951280461Sdim    break;
952280461Sdim  case thumb_movt_funcRel:
953280461Sdim    assert(thumbMode);
954280461Sdim    value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16;
955280461Sdim    *loc32 = setWordFromThumbMov(*loc32, value16);
956280461Sdim    break;
957280461Sdim  case arm_b24:
958280461Sdim  case arm_bl24:
959280461Sdim   assert(!thumbMode);
960280461Sdim    displacement = (targetAddress - (fixupAddress + 8)) + ref.addend();
961280461Sdim    value32 = setDisplacementInArmBranch(*loc32, displacement, targetIsThumb);
962280461Sdim    *loc32 = value32;
963280461Sdim    break;
964280461Sdim  case arm_movw:
965280461Sdim    assert(!thumbMode);
966280461Sdim    value16 = (targetAddress + ref.addend()) & 0xFFFF;
967280461Sdim    if (targetIsThumb)
968280461Sdim      value16 |= 1;
969280461Sdim    *loc32 = setWordFromArmMov(*loc32, value16);
970280461Sdim    break;
971280461Sdim  case arm_movt:
972280461Sdim    assert(!thumbMode);
973280461Sdim    value16 = (targetAddress + ref.addend()) >> 16;
974280461Sdim    *loc32 = setWordFromArmMov(*loc32, value16);
975280461Sdim    break;
976280461Sdim  case arm_movw_funcRel:
977280461Sdim    assert(!thumbMode);
978280461Sdim    value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF;
979280461Sdim    if (targetIsThumb)
980280461Sdim      value16 |= 1;
981280461Sdim    *loc32 = setWordFromArmMov(*loc32, value16);
982280461Sdim    break;
983280461Sdim  case arm_movt_funcRel:
984280461Sdim    assert(!thumbMode);
985280461Sdim    value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16;
986280461Sdim    *loc32 = setWordFromArmMov(*loc32, value16);
987280461Sdim    break;
988280461Sdim  case pointer32:
989280461Sdim    if (targetIsThumb)
990280461Sdim      *loc32 = targetAddress + ref.addend() + 1;
991280461Sdim    else
992280461Sdim      *loc32 = targetAddress + ref.addend();
993280461Sdim    break;
994280461Sdim  case delta32:
995280461Sdim    if (targetIsThumb)
996280461Sdim      *loc32 = targetAddress - fixupAddress + ref.addend() + 1;
997280461Sdim    else
998280461Sdim      *loc32 = targetAddress - fixupAddress + ref.addend();
999280461Sdim    break;
1000280461Sdim  case lazyPointer:
1001280461Sdim    // do nothing
1002280461Sdim    break;
1003280461Sdim  case lazyImmediateLocation:
1004280461Sdim    *loc32 = ref.addend();
1005280461Sdim    break;
1006280461Sdim  case invalid:
1007280461Sdim    llvm_unreachable("invalid ARM Reference Kind");
1008280461Sdim    break;
1009280461Sdim  }
1010280461Sdim}
1011280461Sdim
1012280461Sdimvoid ArchHandler_arm::generateAtomContent(const DefinedAtom &atom,
1013280461Sdim                                          bool relocatable,
1014280461Sdim                                          FindAddressForAtom findAddress,
1015280461Sdim                                          FindAddressForAtom findSectionAddress,
1016280461Sdim                                          uint64_t imageBaseAddress,
1017280461Sdim                                          uint8_t *atomContentBuffer) {
1018280461Sdim  // Copy raw bytes.
1019280461Sdim  memcpy(atomContentBuffer, atom.rawContent().data(), atom.size());
1020280461Sdim  // Apply fix-ups.
1021280461Sdim  bool thumbMode = false;
1022280461Sdim  for (const Reference *ref : atom) {
1023280461Sdim    uint32_t offset = ref->offsetInAtom();
1024280461Sdim    const Atom *target = ref->target();
1025280461Sdim    uint64_t targetAddress = 0;
1026280461Sdim    bool targetIsThumb = false;
1027280461Sdim    if (const DefinedAtom *defTarg = dyn_cast<DefinedAtom>(target)) {
1028280461Sdim      targetAddress = findAddress(*target);
1029280461Sdim      targetIsThumb = isThumbFunction(*defTarg);
1030280461Sdim    }
1031280461Sdim    uint64_t atomAddress = findAddress(atom);
1032280461Sdim    uint64_t fixupAddress = atomAddress + offset;
1033280461Sdim    if (relocatable) {
1034280461Sdim      applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress,
1035280461Sdim                            targetAddress, atomAddress, thumbMode,
1036280461Sdim                            targetIsThumb);
1037280461Sdim    } else {
1038280461Sdim      applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress,
1039280461Sdim                      targetAddress, atomAddress, thumbMode, targetIsThumb);
1040280461Sdim    }
1041280461Sdim  }
1042280461Sdim}
1043280461Sdim
1044280461Sdim
1045280461Sdimbool ArchHandler_arm::useExternalRelocationTo(const Atom &target) {
1046280461Sdim  // Undefined symbols are referenced via external relocations.
1047280461Sdim  if (isa<UndefinedAtom>(&target))
1048280461Sdim    return true;
1049280461Sdim  if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&target)) {
1050280461Sdim     switch (defAtom->merge()) {
1051280461Sdim     case DefinedAtom::mergeAsTentative:
1052280461Sdim       // Tentative definitions are referenced via external relocations.
1053280461Sdim       return true;
1054280461Sdim     case DefinedAtom::mergeAsWeak:
1055280461Sdim     case DefinedAtom::mergeAsWeakAndAddressUsed:
1056280461Sdim       // Global weak-defs are referenced via external relocations.
1057280461Sdim       return (defAtom->scope() == DefinedAtom::scopeGlobal);
1058280461Sdim     default:
1059280461Sdim       break;
1060280461Sdim    }
1061280461Sdim  }
1062280461Sdim  // Everything else is reference via an internal relocation.
1063280461Sdim  return false;
1064280461Sdim}
1065280461Sdim
1066280461Sdimvoid ArchHandler_arm::applyFixupRelocatable(const Reference &ref, uint8_t *loc,
1067280461Sdim                                            uint64_t fixupAddress,
1068280461Sdim                                            uint64_t targetAddress,
1069280461Sdim                                            uint64_t inAtomAddress,
1070280461Sdim                                            bool &thumbMode,
1071280461Sdim                                            bool targetIsThumb) {
1072280461Sdim  if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
1073280461Sdim    return;
1074280461Sdim  assert(ref.kindArch() == Reference::KindArch::ARM);
1075280461Sdim  bool useExternalReloc = useExternalRelocationTo(*ref.target());
1076280461Sdim  ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
1077280461Sdim  int32_t displacement;
1078280461Sdim  uint16_t value16;
1079280461Sdim  uint32_t value32;
1080280461Sdim  bool targetIsUndef = isa<UndefinedAtom>(ref.target());
1081280461Sdim  switch (static_cast<ArmKind>(ref.kindValue())) {
1082280461Sdim  case modeThumbCode:
1083280461Sdim    thumbMode = true;
1084280461Sdim    break;
1085280461Sdim  case modeArmCode:
1086280461Sdim    thumbMode = false;
1087280461Sdim    break;
1088280461Sdim  case modeData:
1089280461Sdim    break;
1090280461Sdim  case thumb_b22:
1091280461Sdim  case thumb_bl22:
1092280461Sdim    assert(thumbMode);
1093280461Sdim    if (useExternalReloc)
1094280461Sdim      displacement = (ref.addend() - (fixupAddress + 4));
1095280461Sdim    else
1096280461Sdim      displacement = (targetAddress - (fixupAddress + 4)) + ref.addend();
1097280461Sdim    value32 = setDisplacementInThumbBranch(*loc32, fixupAddress,
1098280461Sdim                                           displacement,
1099280461Sdim                                           targetIsUndef || targetIsThumb);
1100280461Sdim    *loc32 = value32;
1101280461Sdim    break;
1102280461Sdim  case thumb_movw:
1103280461Sdim    assert(thumbMode);
1104280461Sdim    if (useExternalReloc)
1105280461Sdim      value16 = ref.addend() & 0xFFFF;
1106280461Sdim    else
1107280461Sdim      value16 = (targetAddress + ref.addend()) & 0xFFFF;
1108280461Sdim    *loc32 = setWordFromThumbMov(*loc32, value16);
1109280461Sdim    break;
1110280461Sdim  case thumb_movt:
1111280461Sdim    assert(thumbMode);
1112280461Sdim    if (useExternalReloc)
1113280461Sdim      value16 = ref.addend() >> 16;
1114280461Sdim    else
1115280461Sdim      value16 = (targetAddress + ref.addend()) >> 16;
1116280461Sdim    *loc32 = setWordFromThumbMov(*loc32, value16);
1117280461Sdim    break;
1118280461Sdim  case thumb_movw_funcRel:
1119280461Sdim    assert(thumbMode);
1120280461Sdim    value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF;
1121280461Sdim    *loc32 = setWordFromThumbMov(*loc32, value16);
1122280461Sdim    break;
1123280461Sdim  case thumb_movt_funcRel:
1124280461Sdim    assert(thumbMode);
1125280461Sdim    value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16;
1126280461Sdim    *loc32 = setWordFromThumbMov(*loc32, value16);
1127280461Sdim    break;
1128280461Sdim  case arm_b24:
1129280461Sdim  case arm_bl24:
1130280461Sdim    assert(!thumbMode);
1131280461Sdim    if (useExternalReloc)
1132280461Sdim      displacement = (ref.addend() - (fixupAddress + 8));
1133280461Sdim    else
1134280461Sdim      displacement = (targetAddress - (fixupAddress + 8)) + ref.addend();
1135280461Sdim    value32 = setDisplacementInArmBranch(*loc32, displacement,
1136280461Sdim                                         targetIsThumb);
1137280461Sdim    *loc32 = value32;
1138280461Sdim    break;
1139280461Sdim  case arm_movw:
1140280461Sdim    assert(!thumbMode);
1141280461Sdim    if (useExternalReloc)
1142280461Sdim      value16 = ref.addend() & 0xFFFF;
1143280461Sdim    else
1144280461Sdim      value16 = (targetAddress + ref.addend()) & 0xFFFF;
1145280461Sdim    *loc32 = setWordFromArmMov(*loc32, value16);
1146280461Sdim    break;
1147280461Sdim  case arm_movt:
1148280461Sdim    assert(!thumbMode);
1149280461Sdim    if (useExternalReloc)
1150280461Sdim      value16 = ref.addend() >> 16;
1151280461Sdim    else
1152280461Sdim      value16 = (targetAddress + ref.addend()) >> 16;
1153280461Sdim    *loc32 = setWordFromArmMov(*loc32, value16);
1154280461Sdim    break;
1155280461Sdim  case arm_movw_funcRel:
1156280461Sdim    assert(!thumbMode);
1157280461Sdim    value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF;
1158280461Sdim    *loc32 = setWordFromArmMov(*loc32, value16);
1159280461Sdim    break;
1160280461Sdim  case arm_movt_funcRel:
1161280461Sdim    assert(!thumbMode);
1162280461Sdim    value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16;
1163280461Sdim    *loc32 = setWordFromArmMov(*loc32, value16);
1164280461Sdim    break;
1165280461Sdim  case pointer32:
1166280461Sdim    *loc32 = targetAddress + ref.addend();
1167280461Sdim    break;
1168280461Sdim  case delta32:
1169280461Sdim    *loc32 = targetAddress - fixupAddress + ref.addend();
1170280461Sdim    break;
1171280461Sdim  case lazyPointer:
1172280461Sdim  case lazyImmediateLocation:
1173280461Sdim    // do nothing
1174280461Sdim    break;
1175280461Sdim  case invalid:
1176280461Sdim    llvm_unreachable("invalid ARM Reference Kind");
1177280461Sdim    break;
1178280461Sdim  }
1179280461Sdim}
1180280461Sdim
1181280461Sdimvoid ArchHandler_arm::appendSectionRelocations(
1182280461Sdim                                   const DefinedAtom &atom,
1183280461Sdim                                   uint64_t atomSectionOffset,
1184280461Sdim                                   const Reference &ref,
1185280461Sdim                                   FindSymbolIndexForAtom symbolIndexForAtom,
1186280461Sdim                                   FindSectionIndexForAtom sectionIndexForAtom,
1187280461Sdim                                   FindAddressForAtom addressForAtom,
1188280461Sdim                                   normalized::Relocations &relocs) {
1189280461Sdim  if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
1190280461Sdim    return;
1191280461Sdim  assert(ref.kindArch() == Reference::KindArch::ARM);
1192280461Sdim  uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
1193280461Sdim  bool useExternalReloc = useExternalRelocationTo(*ref.target());
1194280461Sdim  uint32_t targetAtomAddress;
1195280461Sdim  uint32_t fromAtomAddress;
1196280461Sdim  uint16_t other16;
1197280461Sdim  switch (static_cast<ArmKind>(ref.kindValue())) {
1198280461Sdim  case modeThumbCode:
1199280461Sdim  case modeArmCode:
1200280461Sdim  case modeData:
1201280461Sdim    // Do nothing.
1202280461Sdim    break;
1203280461Sdim  case thumb_b22:
1204280461Sdim  case thumb_bl22:
1205280461Sdim    if (useExternalReloc) {
1206280461Sdim      appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
1207280461Sdim                  ARM_THUMB_RELOC_BR22 | rExtern    | rPcRel | rLength4);
1208280461Sdim    } else {
1209280461Sdim      if (ref.addend() != 0)
1210280461Sdim        appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
1211280461Sdim                  ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4);
1212280461Sdim      else
1213280461Sdim        appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
1214280461Sdim                  ARM_THUMB_RELOC_BR22 |              rPcRel | rLength4);
1215280461Sdim    }
1216280461Sdim    break;
1217280461Sdim  case thumb_movw:
1218280461Sdim    if (useExternalReloc) {
1219280461Sdim      other16 = ref.addend() >> 16;
1220280461Sdim      appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
1221280461Sdim                  ARM_RELOC_HALF | rExtern    | rLenThmbLo);
1222280461Sdim      appendReloc(relocs, other16, 0, 0,
1223280461Sdim                  ARM_RELOC_PAIR              | rLenThmbLo);
1224280461Sdim    } else {
1225280461Sdim      targetAtomAddress = addressForAtom(*ref.target());
1226280461Sdim      if (ref.addend() != 0) {
1227280461Sdim        other16 = (targetAtomAddress + ref.addend()) >> 16;
1228280461Sdim        appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
1229280461Sdim                  ARM_RELOC_HALF | rScattered | rLenThmbLo);
1230280461Sdim        appendReloc(relocs, other16, 0, 0,
1231280461Sdim                  ARM_RELOC_PAIR              | rLenThmbLo);
1232280461Sdim      } else {
1233280461Sdim        other16 = (targetAtomAddress + ref.addend()) >> 16;
1234280461Sdim        appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
1235280461Sdim                  ARM_RELOC_HALF              | rLenThmbLo);
1236280461Sdim        appendReloc(relocs, other16, 0, 0,
1237280461Sdim                  ARM_RELOC_PAIR              | rLenThmbLo);
1238280461Sdim      }
1239280461Sdim    }
1240280461Sdim    break;
1241280461Sdim  case thumb_movt:
1242280461Sdim    if (useExternalReloc) {
1243280461Sdim      other16 = ref.addend() & 0xFFFF;
1244280461Sdim      appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
1245280461Sdim                  ARM_RELOC_HALF | rExtern    | rLenThmbHi);
1246280461Sdim      appendReloc(relocs, other16, 0, 0,
1247280461Sdim                  ARM_RELOC_PAIR              | rLenThmbHi);
1248280461Sdim    } else {
1249280461Sdim      targetAtomAddress = addressForAtom(*ref.target());
1250280461Sdim      if (ref.addend() != 0) {
1251280461Sdim        other16 = (targetAtomAddress + ref.addend()) & 0xFFFF;
1252280461Sdim        appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
1253280461Sdim                    ARM_RELOC_HALF | rScattered | rLenThmbHi);
1254280461Sdim        appendReloc(relocs, other16, 0, 0,
1255280461Sdim                    ARM_RELOC_PAIR              | rLenThmbHi);
1256280461Sdim      } else {
1257280461Sdim        other16 = (targetAtomAddress + ref.addend()) & 0xFFFF;
1258280461Sdim        appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
1259280461Sdim                    ARM_RELOC_HALF              | rLenThmbHi);
1260280461Sdim        appendReloc(relocs, other16, 0, 0,
1261280461Sdim                    ARM_RELOC_PAIR              | rLenThmbHi);
1262280461Sdim      }
1263280461Sdim    }
1264280461Sdim    break;
1265280461Sdim  case thumb_movw_funcRel:
1266280461Sdim    fromAtomAddress = addressForAtom(atom);
1267280461Sdim    targetAtomAddress = addressForAtom(*ref.target());
1268280461Sdim    other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16;
1269280461Sdim    appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
1270280461Sdim                ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo);
1271280461Sdim    appendReloc(relocs, other16, 0, fromAtomAddress,
1272280461Sdim                ARM_RELOC_PAIR          | rScattered | rLenThmbLo);
1273280461Sdim    break;
1274280461Sdim  case thumb_movt_funcRel:
1275280461Sdim    fromAtomAddress = addressForAtom(atom);
1276280461Sdim    targetAtomAddress = addressForAtom(*ref.target());
1277280461Sdim    other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF;
1278280461Sdim    appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
1279280461Sdim                ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi);
1280280461Sdim    appendReloc(relocs, other16, 0, fromAtomAddress,
1281280461Sdim                ARM_RELOC_PAIR          | rScattered | rLenThmbHi);
1282280461Sdim    break;
1283280461Sdim  case arm_b24:
1284280461Sdim  case arm_bl24:
1285280461Sdim    if (useExternalReloc) {
1286280461Sdim      appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
1287280461Sdim                  ARM_RELOC_BR24 | rExtern    | rPcRel | rLength4);
1288280461Sdim    } else {
1289280461Sdim      if (ref.addend() != 0)
1290280461Sdim        appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
1291280461Sdim                  ARM_RELOC_BR24 | rScattered | rPcRel | rLength4);
1292280461Sdim      else
1293280461Sdim        appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
1294280461Sdim                  ARM_RELOC_BR24 |              rPcRel | rLength4);
1295280461Sdim    }
1296280461Sdim    break;
1297280461Sdim  case arm_movw:
1298280461Sdim    if (useExternalReloc) {
1299280461Sdim      other16 = ref.addend() >> 16;
1300280461Sdim      appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
1301280461Sdim                  ARM_RELOC_HALF | rExtern    | rLenArmLo);
1302280461Sdim      appendReloc(relocs, other16, 0, 0,
1303280461Sdim                  ARM_RELOC_PAIR              | rLenArmLo);
1304280461Sdim    } else {
1305280461Sdim      targetAtomAddress = addressForAtom(*ref.target());
1306280461Sdim      if (ref.addend() != 0) {
1307280461Sdim        other16 = (targetAtomAddress + ref.addend()) >> 16;
1308280461Sdim        appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
1309280461Sdim                  ARM_RELOC_HALF | rScattered | rLenArmLo);
1310280461Sdim        appendReloc(relocs, other16, 0, 0,
1311280461Sdim                  ARM_RELOC_PAIR              | rLenArmLo);
1312280461Sdim      } else {
1313280461Sdim        other16 = (targetAtomAddress + ref.addend()) >> 16;
1314280461Sdim        appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
1315280461Sdim                  ARM_RELOC_HALF              | rLenArmLo);
1316280461Sdim        appendReloc(relocs, other16, 0, 0,
1317280461Sdim                  ARM_RELOC_PAIR              | rLenArmLo);
1318280461Sdim      }
1319280461Sdim    }
1320280461Sdim    break;
1321280461Sdim  case arm_movt:
1322280461Sdim    if (useExternalReloc) {
1323280461Sdim      other16 = ref.addend() & 0xFFFF;
1324280461Sdim      appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
1325280461Sdim                  ARM_RELOC_HALF | rExtern    | rLenArmHi);
1326280461Sdim      appendReloc(relocs, other16, 0, 0,
1327280461Sdim                  ARM_RELOC_PAIR              | rLenArmHi);
1328280461Sdim    } else {
1329280461Sdim      targetAtomAddress = addressForAtom(*ref.target());
1330280461Sdim      if (ref.addend() != 0) {
1331280461Sdim        other16 = (targetAtomAddress + ref.addend()) & 0xFFFF;
1332280461Sdim        appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
1333280461Sdim                  ARM_RELOC_HALF | rScattered | rLenArmHi);
1334280461Sdim        appendReloc(relocs, other16, 0, 0,
1335280461Sdim                  ARM_RELOC_PAIR              | rLenArmHi);
1336280461Sdim      } else {
1337280461Sdim        other16 = (targetAtomAddress + ref.addend()) & 0xFFFF;
1338280461Sdim        appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
1339280461Sdim                  ARM_RELOC_HALF              | rLenArmHi);
1340280461Sdim        appendReloc(relocs, other16, 0, 0,
1341280461Sdim                  ARM_RELOC_PAIR              | rLenArmHi);
1342280461Sdim      }
1343280461Sdim    }
1344280461Sdim    break;
1345280461Sdim  case arm_movw_funcRel:
1346280461Sdim    fromAtomAddress = addressForAtom(atom);
1347280461Sdim    targetAtomAddress = addressForAtom(*ref.target());
1348280461Sdim    other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16;
1349280461Sdim    appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
1350280461Sdim                ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo);
1351280461Sdim    appendReloc(relocs, other16, 0, fromAtomAddress,
1352280461Sdim                ARM_RELOC_PAIR          | rScattered | rLenArmLo);
1353280461Sdim    break;
1354280461Sdim  case arm_movt_funcRel:
1355280461Sdim    fromAtomAddress = addressForAtom(atom);
1356280461Sdim    targetAtomAddress = addressForAtom(*ref.target());
1357280461Sdim    other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF;
1358280461Sdim    appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
1359280461Sdim                ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi);
1360280461Sdim    appendReloc(relocs, other16, 0, fromAtomAddress,
1361280461Sdim                ARM_RELOC_PAIR          | rScattered | rLenArmHi);
1362280461Sdim    break;
1363280461Sdim  case pointer32:
1364280461Sdim    if (useExternalReloc) {
1365280461Sdim      appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()),  0,
1366280461Sdim                ARM_RELOC_VANILLA |    rExtern     |  rLength4);
1367280461Sdim    }
1368280461Sdim    else {
1369280461Sdim      if (ref.addend() != 0)
1370280461Sdim        appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
1371280461Sdim                ARM_RELOC_VANILLA |    rScattered  |  rLength4);
1372280461Sdim      else
1373280461Sdim        appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
1374280461Sdim                ARM_RELOC_VANILLA |                   rLength4);
1375280461Sdim    }
1376280461Sdim    break;
1377280461Sdim  case delta32:
1378280461Sdim    appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
1379280461Sdim              ARM_RELOC_SECTDIFF  |  rScattered    | rLength4);
1380280461Sdim    appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) +
1381280461Sdim                                                           ref.offsetInAtom(),
1382280461Sdim              ARM_RELOC_PAIR      |  rScattered    | rLength4);
1383280461Sdim    break;
1384280461Sdim  case lazyPointer:
1385280461Sdim  case lazyImmediateLocation:
1386280461Sdim    // do nothing
1387280461Sdim    break;
1388280461Sdim  case invalid:
1389280461Sdim    llvm_unreachable("invalid ARM Reference Kind");
1390280461Sdim    break;
1391280461Sdim  }
1392280461Sdim}
1393280461Sdim
1394280461Sdimvoid ArchHandler_arm::addAdditionalReferences(MachODefinedAtom &atom) {
1395280461Sdim  if (atom.isThumb()) {
1396280461Sdim    atom.addReference(0, modeThumbCode, &atom, 0, Reference::KindArch::ARM);
1397280461Sdim  }
1398280461Sdim}
1399280461Sdim
1400280461Sdimbool ArchHandler_arm::isThumbFunction(const DefinedAtom &atom) {
1401280461Sdim  for (const Reference *ref : atom) {
1402280461Sdim    if (ref->offsetInAtom() != 0)
1403280461Sdim      return false;
1404280461Sdim    if (ref->kindNamespace() != Reference::KindNamespace::mach_o)
1405280461Sdim      continue;
1406280461Sdim    assert(ref->kindArch() == Reference::KindArch::ARM);
1407280461Sdim    if (ref->kindValue() == modeThumbCode)
1408280461Sdim      return true;
1409280461Sdim  }
1410280461Sdim  return false;
1411280461Sdim}
1412280461Sdim
1413280461Sdim
1414280461Sdimclass Thumb2ToArmShimAtom : public SimpleDefinedAtom {
1415280461Sdimpublic:
1416280461Sdim  Thumb2ToArmShimAtom(MachOFile &file, StringRef targetName,
1417280461Sdim                      const DefinedAtom &target)
1418280461Sdim      : SimpleDefinedAtom(file) {
1419280461Sdim    addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM,
1420280461Sdim                 ArchHandler_arm::modeThumbCode, 0, this, 0);
1421280461Sdim    addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM,
1422280461Sdim                 ArchHandler_arm::delta32, 8, &target, 0);
1423280461Sdim    std::string name = std::string(targetName) + "$shim";
1424280461Sdim    StringRef tmp(name);
1425280461Sdim    _name = tmp.copy(file.allocator());
1426280461Sdim  }
1427280461Sdim
1428280461Sdim  StringRef name() const override {
1429280461Sdim    return _name;
1430280461Sdim  }
1431280461Sdim
1432280461Sdim  ContentType contentType() const override {
1433280461Sdim    return DefinedAtom::typeCode;
1434280461Sdim  }
1435280461Sdim
1436280461Sdim  Alignment alignment() const override {
1437280461Sdim    return Alignment(2);
1438280461Sdim  }
1439280461Sdim
1440280461Sdim  uint64_t size() const override {
1441280461Sdim    return 12;
1442280461Sdim  }
1443280461Sdim
1444280461Sdim  ContentPermissions permissions() const override {
1445280461Sdim    return DefinedAtom::permR_X;
1446280461Sdim  }
1447280461Sdim
1448280461Sdim  ArrayRef<uint8_t> rawContent() const override {
1449280461Sdim    static const uint8_t bytes[] =
1450280461Sdim    { 0xDF, 0xF8, 0x04, 0xC0,       //  ldr ip, pc + 4
1451280461Sdim      0xFF, 0x44,                   //  add ip, pc, ip
1452280461Sdim      0x60, 0x47,                   //  ldr pc, [ip]
1453280461Sdim      0x00, 0x00, 0x00, 0x00 };     //  .long target - this
1454280461Sdim    assert(sizeof(bytes) == size());
1455280461Sdim    return llvm::makeArrayRef(bytes, sizeof(bytes));
1456280461Sdim  }
1457280461Sdimprivate:
1458280461Sdim  StringRef _name;
1459280461Sdim};
1460280461Sdim
1461280461Sdim
1462280461Sdimclass ArmToThumbShimAtom : public SimpleDefinedAtom {
1463280461Sdimpublic:
1464280461Sdim  ArmToThumbShimAtom(MachOFile &file, StringRef targetName,
1465280461Sdim                     const DefinedAtom &target)
1466280461Sdim      : SimpleDefinedAtom(file) {
1467280461Sdim    addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM,
1468280461Sdim                 ArchHandler_arm::delta32, 12, &target, 0);
1469280461Sdim    std::string name = std::string(targetName) + "$shim";
1470280461Sdim    StringRef tmp(name);
1471280461Sdim    _name = tmp.copy(file.allocator());
1472280461Sdim  }
1473280461Sdim
1474280461Sdim  StringRef name() const override {
1475280461Sdim    return _name;
1476280461Sdim  }
1477280461Sdim
1478280461Sdim  ContentType contentType() const override {
1479280461Sdim    return DefinedAtom::typeCode;
1480280461Sdim  }
1481280461Sdim
1482280461Sdim  Alignment alignment() const override {
1483280461Sdim    return Alignment(2);
1484280461Sdim  }
1485280461Sdim
1486280461Sdim  uint64_t size() const override {
1487280461Sdim    return 16;
1488280461Sdim  }
1489280461Sdim
1490280461Sdim  ContentPermissions permissions() const override {
1491280461Sdim    return DefinedAtom::permR_X;
1492280461Sdim  }
1493280461Sdim
1494280461Sdim  ArrayRef<uint8_t> rawContent() const override {
1495280461Sdim    static const uint8_t bytes[] =
1496280461Sdim    { 0x04, 0xC0, 0x9F, 0xE5,       //  ldr ip, pc + 4
1497280461Sdim      0x0C, 0xC0, 0x8F, 0xE0,       //  add ip, pc, ip
1498280461Sdim      0x1C, 0xFF, 0x2F, 0xE1,       //  ldr pc, [ip]
1499280461Sdim      0x00, 0x00, 0x00, 0x00 };     //  .long target - this
1500280461Sdim    assert(sizeof(bytes) == size());
1501280461Sdim    return llvm::makeArrayRef(bytes, sizeof(bytes));
1502280461Sdim  }
1503280461Sdimprivate:
1504280461Sdim  StringRef _name;
1505280461Sdim};
1506280461Sdim
1507280461Sdimconst DefinedAtom *ArchHandler_arm::createShim(MachOFile &file,
1508280461Sdim                                               bool thumbToArm,
1509280461Sdim                                               const DefinedAtom &target) {
1510280461Sdim  bool isStub = (target.contentType() == DefinedAtom::typeStub);
1511280461Sdim  StringRef targetName = isStub ? stubName(target) : target.name();
1512280461Sdim  if (thumbToArm)
1513280461Sdim    return new (file.allocator()) Thumb2ToArmShimAtom(file, targetName, target);
1514280461Sdim  else
1515280461Sdim    return new (file.allocator()) ArmToThumbShimAtom(file, targetName, target);
1516280461Sdim}
1517280461Sdim
1518280461Sdim
1519280461Sdimstd::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm() {
1520280461Sdim  return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm());
1521280461Sdim}
1522280461Sdim
1523280461Sdim} // namespace mach_o
1524280461Sdim} // namespace lld
1525