LLVM 24.0.0git
RISCVMachObjectWriter.cpp
Go to the documentation of this file.
1//===-- RISCVMachObjectWriter.cpp - RISC-V 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
11#include "RISCVMCAsmInfo.h"
12#include "llvm/ADT/Twine.h"
14#include "llvm/MC/MCAsmInfo.h"
16#include "llvm/MC/MCAssembler.h"
17#include "llvm/MC/MCContext.h"
18#include "llvm/MC/MCExpr.h"
19#include "llvm/MC/MCFixup.h"
21#include "llvm/MC/MCSection.h"
23#include "llvm/MC/MCSymbol.h"
24#include "llvm/MC/MCValue.h"
27#include <cassert>
28#include <cstdint>
29
30using namespace llvm;
31
32namespace {
33
34class RISCVMachObjectWriter : public MCMachObjectTargetWriter {
35 bool getRISCVFixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType,
36 const MCValue Sym, unsigned &Log2Size,
37 const MCAssembler &Asm);
38
39public:
40 RISCVMachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype)
41 : MCMachObjectTargetWriter(false, CPUType, CPUSubtype) {}
42
43 void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm,
44 const MCFragment *Fragment, const MCFixup &Fixup,
45 MCValue Target, uint64_t &FixedValue) override;
46};
47
48} // end anonymous namespace
49
50bool RISCVMachObjectWriter::getRISCVFixupKindMachOInfo(const MCFixup &Fixup,
51 unsigned &RelocType,
52 const MCValue Sym,
53 unsigned &Log2Size,
54 const MCAssembler &Asm) {
55 RelocType = unsigned(MachO::RISCV_RELOC_UNSIGNED);
56 Log2Size = ~0U;
57
58 if (Sym.getSpecifier() == RISCV::S_GOT_HI) {
59 Log2Size = Log2_32(4);
60 RelocType = unsigned(MachO::RISCV_RELOC_GOT_HI20);
61 return true;
62 }
63
64 switch (Fixup.getKind()) {
65 default:
66 return false;
67
68 case FK_Data_1:
69 Log2Size = Log2_32(1);
70 return true;
71 case FK_Data_2:
72 Log2Size = Log2_32(2);
73 return true;
74 case FK_Data_4:
75 Log2Size = Log2_32(4);
76 return true;
77 case FK_Data_8:
78 Log2Size = Log2_32(8);
79 return true;
82 llvm_unreachable("lo12 fixups should have been resolved elsewhere");
85 Log2Size = Log2_32(4);
86 RelocType = MachO::RISCV_RELOC_LO12;
87 return true;
89 Log2Size = Log2_32(4);
90 if (Sym.getSpecifier() != RISCV::S_PCREL_HI) {
91 Asm.getContext().reportError(Fixup.getLoc(),
92 "unknown AUIPC relocation kind");
93 return false;
94 }
95 RelocType = unsigned(MachO::RISCV_RELOC_HI20);
96 return true;
98 Log2Size = Log2_32(4);
99 RelocType = unsigned(MachO::RISCV_RELOC_HI20);
100 return true;
104 Log2Size = Log2_32(4);
105 RelocType = unsigned(MachO::RISCV_RELOC_BRANCH21);
106 return true;
107 }
108}
109
110static bool canUseLocalRelocation(const MCSectionMachO &Section,
111 const MCSymbol &Symbol, unsigned Log2Size) {
112 // Debug info sections can use local relocations.
113 if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
114 return true;
115
116 // Otherwise, only pointer sized relocations are supported.
117 if (Log2Size != 2)
118 return false;
119
120 // But only if they don't point to a few forbidden sections.
121 if (!Symbol.isInSection())
122 return true;
123 const MCSectionMachO &RefSec =
124 static_cast<const MCSectionMachO &>(Symbol.getSection());
125 if (RefSec.getType() == MachO::S_CSTRING_LITERALS)
126 return false;
127
128 if (RefSec.getSegmentName() == "__DATA" &&
129 RefSec.getName() == "__objc_classrefs")
130 return false;
131
132 return true;
133}
134
135static void emitRelocation(MachObjectWriter *Writer, const MCFragment *Fragment,
136 uint32_t FixupOffset, const MCSymbol *RelSymbol,
137 unsigned Index, bool IsPCRel, unsigned Log2Size,
138 unsigned Type) {
139
140 assert(isUInt<2>(Log2Size) && "Invalid Log2Size");
141 assert(isUInt<4>(Type) && "Invalid type");
142 assert(isUInt<24>(Index) && "Invalid Index");
144 MRE.r_word0 = FixupOffset;
145 MRE.r_word1 =
146 (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
147 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
148}
149
150static bool checkSymbolBase(const MCSymbol *Base, const MCSymbol *Symbol,
151 const MCFixup &Fixup, MCAssembler &Asm) {
152 if (!Base) {
153 Asm.getContext().reportError(
154 Fixup.getLoc(),
155 "unsupported relocation of local symbol '" + Symbol->getName() +
156 "'. Must have non-local symbol earlier in section.");
157 return false;
158 }
159 return true;
160}
161
162template <unsigned Bits>
163static bool isValidInt(const uint64_t &FixedValue, const char *Msg,
164 MCAssembler &Asm, const SMLoc Loc) {
165 const bool IsValid = isInt<Bits>(FixedValue);
166 if (!IsValid)
167 Asm.getContext().reportError(Loc, Msg);
168 return IsValid;
169}
170
171extern const MCFixup *getPCRelHiFixup(const MCSpecifierExpr &Expr,
172 const MCFragment **DFOut);
173
174void RISCVMachObjectWriter::recordRelocation(
175 MachObjectWriter *Writer, MCAssembler &Asm, const MCFragment *Fragment,
176 const MCFixup &Fixup, MCValue Target, uint64_t &FixedValue) {
177 const bool IsPCRel =
178 Fixup.isPCRel() || Target.getSpecifier() == RISCV::S_GOT_HI;
179
180 // See <reloc.h>.
181 uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment);
182 unsigned Log2Size = 0;
183 int64_t Value = 0;
184 unsigned Index = 0;
185 unsigned Type = 0;
186 const unsigned Kind = Fixup.getKind();
187 const MCSymbol *RelSymbol = nullptr;
188 bool RequireExtraAddend = false;
189 uint64_t ExtraAddendValue = 0;
190
191 FixupOffset += Fixup.getOffset();
192
193 // RISC-V pcrel relocation addends do not include the section offset.
194 if (IsPCRel)
195 FixedValue += FixupOffset;
196
197 // AUIPC fixups use relocations for the whole symbol value and only
198 // put the addend in the instruction itself. Clear out any value the
199 // generic code figured out from the symbol definition.
201 FixedValue = 0;
202
203 // %pcrel_lo relocations directly target the same symbol as the
204 // corresponding AUIPC, and encode (inline) an offset to that AUIPC
205 // in the immediate field of the instruction itself.
208 const MCFragment *AUIPCDF;
209 const MCFixup *AUIPCFixup =
210 getPCRelHiFixup(cast<MCSpecifierExpr>(*Fixup.getValue()), &AUIPCDF);
211 assert(AUIPCFixup);
212
213 // Calculate the offset from this fixup to the AUIPC it references, this
214 // will be put into the instruction itself.
215 FixedValue =
216 Asm.getFragmentOffset(*AUIPCDF) + AUIPCFixup->getOffset() - FixupOffset;
217 if (!isInt<12>(FixedValue)) {
218 RequireExtraAddend = true;
219 ExtraAddendValue = FixedValue;
220 if (!isValidInt<24>(
221 ExtraAddendValue,
222 "AUIPC out of range of corresponding %pcrel_lo instruction", Asm,
223 Fixup.getLoc()))
224 return;
225 }
226
227 // Retarget the rest of this function to reference the AUIPC's symbol.
228 MCValue RealTarget;
229 if (!AUIPCFixup->getValue()->evaluateAsValue(RealTarget, Asm)) {
230 Asm.getContext().reportError(AUIPCFixup->getLoc(),
231 "cannot understand AUIPC target");
232 return;
233 }
234 if (RealTarget.getSubSym()) {
235 Asm.getContext().reportError(AUIPCFixup->getLoc(),
236 "AUIPC target with symbol difference");
237 return;
238 }
239
240 Target = MCValue::get(RealTarget.getAddSym(), /*SymB*/ nullptr,
241 RealTarget.getConstant(), RISCV::S_PCREL_LO);
242
243 Log2Size = Log2_32(4);
244 const auto Spec = RealTarget.getSpecifier();
247 } else if (!getRISCVFixupKindMachOInfo(Fixup, Type, Target, Log2Size, Asm)) {
248 Asm.getContext().reportError(Fixup.getLoc(), "unknown RISC-V fixup kind");
249 return;
250 }
251
252 // imm19 relocations are for conditional branches, which require
253 // assembler local symbols. If we got here, that's not what we have,
254 // so report an error.
255 if (Kind == RISCV::fixup_riscv_branch ||
258 Asm.getContext().reportError(
259 Fixup.getLoc(), "conditional branch requires assembler-local"
260 " label. '" +
261 Target.getAddSym()->getName() + "' is external.");
262 return;
263 }
264
265 Value = Target.getConstant();
266
267 // Only .word and %pcrel_lo instructions inline the pc-relative
268 // offset in the instruction, but only if such offset fits the
269 // 12-bit immediate of an addi or an lw instrucion.
272 RequireExtraAddend)
273 FixedValue = 0;
274
275 // Emit relocations.
276
277 // Constants.
278 if (Target.isAbsolute()) {
279 // FIXME: Should this always be extern?
280 // SymbolNum of 0 indicates the absolute section.
281 if (IsPCRel) {
282 Asm.getContext().reportError(Fixup.getLoc(),
283 "PC relative absolute relocation!");
284 return;
285 }
286 emitRelocation(Writer, Fragment, FixupOffset, RelSymbol, Index,
287 /*IsPCRel*/ false, /*Log2Size*/ ~0U,
289 return;
290 }
291
292 // A - B + constant
293 if (const MCSymbol *B = Target.getSubSym()) {
294 const MCSymbol *A = Target.getAddSym();
295 const MCSymbol *A_Base = Writer->getAtom(*A);
296 const MCSymbol *B_Base = Writer->getAtom(*B);
297
298 // We don't support PCrel relocations of differences.
299 if (IsPCRel) {
300 Asm.getContext().reportError(Fixup.getLoc(),
301 "unsupported pc-relative relocation of "
302 "difference");
303 return;
304 }
305
306 // Ensure both symbols have base atoms for external relocations.
307 if (!checkSymbolBase(A_Base, A, Fixup, Asm) ||
308 !checkSymbolBase(B_Base, B, Fixup, Asm))
309 return;
310
311 if (A_Base && A_Base == B_Base) {
312 Asm.getContext().reportError(
313 Fixup.getLoc(), "unsupported relocation with identical base");
314 return;
315 }
316
317 Value +=
318 (!A->getFragment() ? 0 : Writer->getSymbolAddress(*A)) -
319 (!A_Base || !A_Base->getFragment() ? 0
320 : Writer->getSymbolAddress(*A_Base));
321 Value -=
322 (!B->getFragment() ? 0 : Writer->getSymbolAddress(*B)) -
323 (!B_Base || !B_Base->getFragment() ? 0
324 : Writer->getSymbolAddress(*B_Base));
325
326 // If there's any addend left to handle, inline it in the instruction's
327 // immediate.
328 FixedValue = Value;
329 if (!isValidInt<12>(
330 FixedValue,
331 "AUIPC out of range of corresponding %pcrel_lo instruction", Asm,
332 Fixup.getLoc()))
333 return;
334
335 emitRelocation(Writer, Fragment, FixupOffset, /*RelSymbol*/ A_Base, Index,
336 IsPCRel, Log2Size, /*Type*/ MachO::RISCV_RELOC_UNSIGNED);
337 // struct relocation_info (8 bytes)
338 emitRelocation(Writer, Fragment, FixupOffset, /*RelSymbol*/ B_Base, Index,
339 IsPCRel, Log2Size, /*Type*/ MachO::RISCV_RELOC_SUBTRACTOR);
340 return;
341 }
342
343 // A + constant
344 if (const MCSymbol *Symbol = Target.getAddSym()) {
345 assert(!Target.getSubSym() && "invalid expression");
346 const MCSectionMachO &Section =
347 static_cast<const MCSectionMachO &>(*Fragment->getParent());
348
349 const bool CanUseLocalRelocation =
350 canUseLocalRelocation(Section, *Symbol, Log2Size);
351 if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) {
352 if (!Symbol->isInSection()) {
353 checkSymbolBase(nullptr, Symbol, Fixup, Asm);
354 return;
355 }
356 const MCSection &Sec = Symbol->getSection();
358 Symbol->setUsedInReloc();
359 }
360
361 const MCSymbol *Base = Writer->getAtom(*Symbol);
362 // If the symbol is a variable it can either be in a section and
363 // we have a base or it is absolute and should have been expanded.
364 assert(!Symbol->isVariable() || Base);
365
366 // Relocations inside debug sections always use local relocations when
367 // possible. This seems to be done because the debugger doesn't fully
368 // understand relocation entries and expects to find values that
369 // have already been fixed up.
370 if (Symbol->isInSection()) {
371 if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
372 Base = nullptr;
373 }
374
375 // RISC-V uses external relocations as much as possible. For debug
376 // sections, and for pointer-sized relocations (.quad), we allow section
377 // relocations. It's code sections that run into trouble.
378 if (Base) {
379 RelSymbol = Base;
380
381 // Add the local offset, if needed.
382 if (Base != Symbol)
383 Value += Asm.getSymbolOffset(*Symbol) - Asm.getSymbolOffset(*Base);
384 } else if (Symbol->isInSection()) {
385 if (!CanUseLocalRelocation) {
386 checkSymbolBase(nullptr, Symbol, Fixup, Asm);
387 return;
388 }
389 // Adjust the relocation to be section-relative.
390 // The index is the section ordinal (1-based).
391 const MCSection &Sec = Symbol->getSection();
392 Index = Sec.getOrdinal() + 1;
393 Value += Writer->getSymbolAddress(*Symbol);
394
395 if (IsPCRel)
396 Value -= Writer->getFragmentAddress(Asm, Fragment) + Fixup.getOffset();
397 } else {
399 "This constant variable should have been expanded during evaluation");
400 }
402 // If there's any addend left to handle, encode it in the instruction.
403 FixedValue = Value;
404 // struct relocation_info (8 bytes)
405 emitRelocation(Writer, Fragment, FixupOffset, RelSymbol, Index,
406 /*IsPCRel*/ false, Log2Size,
408 return;
409 }
410 // We have an addend offset that is encoded in the relocation
411 // record, not inlined in the instruction.
412 if (Value) {
413 if (!isValidInt<24>(Value, "addend too big for relocation", Asm,
414 Fixup.getLoc()))
415 return;
416
417 emitRelocation(Writer, Fragment, FixupOffset, RelSymbol, Index, IsPCRel,
418 Log2Size, Type);
419 // Now set up the Addend relocation.
420 emitRelocation(Writer, Fragment, FixupOffset, /*RelSymbol*/ nullptr,
421 Value & 0xffffff, /*IsPCRel*/ false, /*Log2Size*/ 2,
423 return;
424 }
425
426 emitRelocation(Writer, Fragment, FixupOffset, RelSymbol, Index, IsPCRel,
427 Log2Size, Type);
428 }
429
430 if (RequireExtraAddend) {
431 emitRelocation(Writer, Fragment, FixupOffset, /*RelSymbol*/ nullptr,
432 ExtraAddendValue & 0xffffff, /*IsPCRel*/ false,
433 /*Log2Size*/ 2, /*Type*/ MachO::RISCV_RELOC_ADDEND);
434 }
435}
436
437std::unique_ptr<MCObjectTargetWriter>
439 return std::make_unique<RISCVMachObjectWriter>(CPUType, CPUSubtype);
440}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static bool canUseLocalRelocation(const MCSectionMachO &Section, const MCSymbol &Symbol, unsigned Log2Size)
unsigned uint64_t
Function Alias Analysis false
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
PowerPC TLS Dynamic Call Fixup
const MCFixup * getPCRelHiFixup(const MCSpecifierExpr &Expr, const MCFragment **DFOut)
static bool checkSymbolBase(const MCSymbol *Base, const MCSymbol *Symbol, const MCFixup &Fixup, MCAssembler &Asm)
static bool isValidInt(const uint64_t &FixedValue, const char *Msg, MCAssembler &Asm, const SMLoc Loc)
static void emitRelocation(MachObjectWriter *Writer, const MCFragment *Fragment, uint32_t FixupOffset, const MCSymbol *RelSymbol, unsigned Index, bool IsPCRel, unsigned Log2Size, unsigned Type)
const char * Msg
static bool isSectionAtomizableBySymbols(const MCSection &Section)
True if the section is atomized using the symbols in it.
LLVM_ABI bool evaluateAsValue(MCValue &Res, const MCAssembler &Asm) const
Try to evaluate the expression to the form (a - b + constant) where neither a nor b are variables.
Definition MCExpr.cpp:453
Encode information on a single operation to perform on a byte sequence (e.g., an encoded instruction)...
Definition MCFixup.h:61
const MCExpr * getValue() const
Definition MCFixup.h:101
LLVM_ABI SMLoc getLoc() const
uint32_t getOffset() const
Definition MCFixup.h:98
MCSection * getParent() const
Definition MCSection.h:181
This represents a section on a Mach-O system (used by Mac OS X).
MachO::SectionType getType() const
StringRef getSegmentName() const
unsigned getOrdinal() const
Definition MCSection.h:673
StringRef getName() const
Definition MCSection.h:650
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
Definition MCSymbol.h:42
MCFragment * getFragment() const
Definition MCSymbol.h:345
static MCValue get(const MCSymbol *SymA, const MCSymbol *SymB=nullptr, int64_t Val=0, uint32_t Specifier=0)
Definition MCValue.h:56
const MCSymbol * getAddSym() const
Definition MCValue.h:49
int64_t getConstant() const
Definition MCValue.h:44
uint32_t getSpecifier() const
Definition MCValue.h:46
const MCSymbol * getSubSym() const
Definition MCValue.h:51
uint64_t getFragmentAddress(const MCAssembler &Asm, const MCFragment *Fragment) const
void addRelocation(const MCSymbol *RelSymbol, const MCSection *Sec, MachO::any_relocation_info &MRE)
const MCSymbol * getAtom(const MCSymbol &S) const
uint64_t getSymbolAddress(const MCSymbol &S) const
Represents a location in source code.
Definition SMLoc.h:22
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ S_CSTRING_LITERALS
S_CSTRING_LITERALS - Section with literal C strings.
Definition MachO.h:131
@ RISCV_RELOC_GOT_HI20
Definition MachO.h:522
@ RISCV_RELOC_HI20
Definition MachO.h:505
@ RISCV_RELOC_GOT_LO12
Definition MachO.h:525
@ RISCV_RELOC_BRANCH21
Definition MachO.h:502
@ RISCV_RELOC_UNSIGNED
Definition MachO.h:483
@ RISCV_RELOC_SUBTRACTOR
Definition MachO.h:496
@ RISCV_RELOC_LO12
Definition MachO.h:516
@ RISCV_RELOC_ADDEND
Definition MachO.h:535
@ S_ATTR_DEBUG
S_ATTR_DEBUG - A debug section.
Definition MachO.h:207
This is an optimization pass for GlobalISel generic memory operations.
constexpr bool isInt(int64_t x)
Checks if an integer fits into the given bit width.
Definition MathExtras.h:166
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
std::unique_ptr< MCObjectTargetWriter > createRISCVMachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype)
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:326
constexpr bool isUInt(uint64_t x)
Checks if an unsigned integer fits into the given bit width.
Definition MathExtras.h:190
@ FK_Data_8
A eight-byte fixup.
Definition MCFixup.h:37
@ FK_Data_1
A one-byte fixup.
Definition MCFixup.h:34
@ FK_Data_4
A four-byte fixup.
Definition MCFixup.h:36
@ FK_Data_2
A two-byte fixup.
Definition MCFixup.h:35
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559