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