File: ARM64Common.cpp

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (152 lines) | stat: -rw-r--r-- 5,279 bytes parent folder | download | duplicates (12)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//===- ARM64Common.cpp ----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "Arch/ARM64Common.h"

#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/Endian.h"

using namespace llvm::MachO;
using namespace llvm::support::endian;
using namespace lld;
using namespace lld::macho;

int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
                                       const relocation_info rel) const {
  if (rel.r_type != ARM64_RELOC_UNSIGNED &&
      rel.r_type != ARM64_RELOC_SUBTRACTOR) {
    // All other reloc types should use the ADDEND relocation to store their
    // addends.
    // TODO(gkm): extract embedded addend just so we can assert that it is 0
    return 0;
  }

  const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
  const uint8_t *loc = buf + offset + rel.r_address;
  switch (rel.r_length) {
  case 2:
    return static_cast<int32_t>(read32le(loc));
  case 3:
    return read64le(loc);
  default:
    llvm_unreachable("invalid r_length");
  }
}

static void writeValue(uint8_t *loc, const Reloc &r, uint64_t value) {
  switch (r.length) {
  case 2:
    checkInt(loc, r, value, 32);
    write32le(loc, value);
    break;
  case 3:
    write64le(loc, value);
    break;
  default:
    llvm_unreachable("invalid r_length");
  }
}

// For instruction relocations (load, store, add), the base
// instruction is pre-populated in the text section. A pre-populated
// instruction has opcode & register-operand bits set, with immediate
// operands zeroed. We read it from text, OR-in the immediate
// operands, then write-back the completed instruction.
void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
                              uint64_t pc) const {
  auto loc32 = reinterpret_cast<uint32_t *>(loc);
  uint32_t base = ((r.length == 2) ? read32le(loc) : 0);
  switch (r.type) {
  case ARM64_RELOC_BRANCH26:
    encodeBranch26(loc32, r, base, value - pc);
    break;
  case ARM64_RELOC_SUBTRACTOR:
  case ARM64_RELOC_UNSIGNED:
    writeValue(loc, r, value);
    break;
  case ARM64_RELOC_POINTER_TO_GOT:
    if (r.pcrel)
      value -= pc;
    writeValue(loc, r, value);
    break;
  case ARM64_RELOC_PAGE21:
  case ARM64_RELOC_GOT_LOAD_PAGE21:
  case ARM64_RELOC_TLVP_LOAD_PAGE21:
    assert(r.pcrel);
    encodePage21(loc32, r, base, pageBits(value) - pageBits(pc));
    break;
  case ARM64_RELOC_PAGEOFF12:
  case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
  case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
    assert(!r.pcrel);
    encodePageOff12(loc32, r, base, value);
    break;
  default:
    llvm_unreachable("unexpected relocation type");
  }
}

void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const {
  // The instruction format comments below are quoted from
  // ArmĀ® Architecture Reference Manual
  // Armv8, for Armv8-A architecture profile
  // ARM DDI 0487G.a (ID011921)
  uint32_t instruction = read32le(loc);
  // C6.2.132 LDR (immediate)
  // This matches both the 64- and 32-bit variants:
  // LDR <(X|W)t>, [<Xn|SP>{, #<pimm>}]
  if ((instruction & 0xbfc00000) != 0xb9400000)
    error(getRelocAttrs(type).name + " reloc requires LDR instruction");
  assert(((instruction >> 10) & 0xfff) == 0 &&
         "non-zero embedded LDR immediate");
  // C6.2.4 ADD (immediate)
  // ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}
  instruction = ((instruction & 0x001fffff) | 0x91000000);
  write32le(loc, instruction);
}

void ARM64Common::handleDtraceReloc(const Symbol *sym, const Reloc &r,
                                    uint8_t *loc) const {
  assert(r.type == ARM64_RELOC_BRANCH26);

  if (config->outputType == MH_OBJECT)
    return;

  if (sym->getName().starts_with("___dtrace_probe")) {
    // change call site to a NOP
    write32le(loc, 0xD503201F);
  } else if (sym->getName().starts_with("___dtrace_isenabled")) {
    // change call site to 'MOVZ X0,0'
    write32le(loc, 0xD2800000);
  } else {
    error("Unrecognized dtrace symbol prefix: " + toString(*sym));
  }
}

static void reportUnalignedLdrStr(Twine loc, uint64_t va, int align,
                                  const Symbol *sym) {
  std::string symbolHint;
  if (sym)
    symbolHint = " (" + toString(*sym) + ")";
  error(loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" +
        llvm::utohexstr(va) + symbolHint + " is not " + Twine(align) +
        "-byte aligned");
}

void macho::reportUnalignedLdrStr(void *loc, const lld::macho::Reloc &r,
                                  uint64_t va, int align) {
  uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;
  const InputSection *isec = offsetToInputSection(&off);
  std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";
  ::reportUnalignedLdrStr(locStr, va, align, r.referent.dyn_cast<Symbol *>());
}

void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d,
                                  uint64_t va, int align) {
  ::reportUnalignedLdrStr(d.reason, va, align, d.symbol);
}