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 bool RequireExtraAddend = false;
190 uint64_t ExtraAddendValue = 0;
191
192 FixupOffset += Fixup.getOffset();
193
194 // RISC-V pcrel relocation addends do not include the section offset.
195 if (IsPCRel)
196 FixedValue += FixupOffset;
197
198 // AUIPC fixups use relocations for the whole symbol value and only
199 // put the addend in the instruction itself. Clear out any value the
200 // generic code figured out from the symbol definition.
202 FixedValue = 0;
203
204 // %pcrel_lo relocations directly target the same symbol as the
205 // corresponding AUIPC, and encode (inline) an offset to that AUIPC
206 // in the immediate field of the instruction itself.
209 const MCFragment *AUIPCDF;
210 const MCFixup *AUIPCFixup =
211 getPCRelHiFixup(cast<MCSpecifierExpr>(*Fixup.getValue()), &AUIPCDF);
212 assert(AUIPCFixup);
213
214 // Calculate the offset from this fixup to the AUIPC it references, this
215 // will be put into the instruction itself.
216 FixedValue =
217 Asm.getFragmentOffset(*AUIPCDF) + AUIPCFixup->getOffset() - FixupOffset;
218 if (!isInt<12>(FixedValue)) {
219 RequireExtraAddend = true;
220 ExtraAddendValue = FixedValue;
221 if (!isValidInt<24>(
222 ExtraAddendValue,
223 "AUIPC out of range of corresponding %pcrel_lo instruction", Asm,
224 Fixup.getLoc()))
225 return;
226 }
227
228 // Retarget the rest of this function to reference the AUIPC's symbol.
229 MCValue RealTarget;
230 if (!AUIPCFixup->getValue()->evaluateAsValue(RealTarget, Asm)) {
231 Asm.getContext().reportError(AUIPCFixup->getLoc(),
232 "cannot understand AUIPC target");
233 return;
234 }
235 if (RealTarget.getSubSym()) {
236 Asm.getContext().reportError(AUIPCFixup->getLoc(),
237 "AUIPC target with symbol difference");
238 return;
239 }
240
241 Target = MCValue::get(RealTarget.getAddSym(), /*SymB*/ nullptr,
242 RealTarget.getConstant(), RISCV::S_PCREL_LO);
243
244 Log2Size = Log2_32(4);
245 const auto Spec = RealTarget.getSpecifier();
248 } else if (!getRISCVFixupKindMachOInfo(Fixup, Type, Target, Log2Size, Asm)) {
249 Asm.getContext().reportError(Fixup.getLoc(), "unknown RISC-V fixup kind");
250 return;
251 }
252
253 // imm19 relocations are for conditional branches, which require
254 // assembler local symbols. If we got here, that's not what we have,
255 // so report an error.
256 if (Kind == RISCV::fixup_riscv_branch ||
259 Asm.getContext().reportError(
260 Fixup.getLoc(), "conditional branch requires assembler-local"
261 " label. '" +
262 Target.getAddSym()->getName() + "' is external.");
263 return;
264 }
265
266 Value = Target.getConstant();
267
268 // Only .word and %pcrel_lo instructions inline the pc-relative
269 // offset in the instruction, but only if such offset fits the
270 // 12-bit immediate of an addi or an lw instrucion.
273 RequireExtraAddend)
274 FixedValue = 0;
275
276 // Emit relocations.
277
278 // Constants.
279 if (Target.isAbsolute()) {
280 // FIXME: Should this always be extern?
281 // SymbolNum of 0 indicates the absolute section.
282 if (IsPCRel) {
283 Asm.getContext().reportError(Fixup.getLoc(),
284 "PC relative absolute relocation!");
285 return;
286 }
287 emitRelocation(Writer, Fragment, FixupOffset, RelSymbol, Index,
288 /*IsPCRel*/ false, /*Log2Size*/ ~0U,
290 return;
291 }
292
293 // A - B + constant
294 if (const MCSymbol *B = Target.getSubSym()) {
295 const MCSymbol *A = Target.getAddSym();
296 const MCSymbol *A_Base = Writer->getAtom(*A);
297 const MCSymbol *B_Base = Writer->getAtom(*B);
298
299 // We don't support PCrel relocations of differences.
300 if (IsPCRel) {
301 Asm.getContext().reportError(Fixup.getLoc(),
302 "unsupported pc-relative relocation of "
303 "difference");
304 return;
305 }
306
307 // Ensure both symbols have base atoms for external relocations.
308 if (!checkSymbolBase(A_Base, A, Fixup, Asm) ||
309 !checkSymbolBase(B_Base, B, Fixup, Asm))
310 return;
311
312 if (A_Base && A_Base == B_Base) {
313 Asm.getContext().reportError(
314 Fixup.getLoc(), "unsupported relocation with identical base");
315 return;
316 }
317
318 Value +=
319 (!A->getFragment() ? 0 : Writer->getSymbolAddress(*A)) -
320 (!A_Base || !A_Base->getFragment() ? 0
321 : Writer->getSymbolAddress(*A_Base));
322 Value -=
323 (!B->getFragment() ? 0 : Writer->getSymbolAddress(*B)) -
324 (!B_Base || !B_Base->getFragment() ? 0
325 : Writer->getSymbolAddress(*B_Base));
326
327 // If there's any addend left to handle, inline it in the instruction's
328 // immediate.
329 FixedValue = Value;
330 if (!isValidInt<12>(
331 FixedValue,
332 "AUIPC out of range of corresponding %pcrel_lo instruction", Asm,
333 Fixup.getLoc()))
334 return;
335
336 emitRelocation(Writer, Fragment, FixupOffset, /*RelSymbol*/ A_Base, Index,
337 IsPCRel, Log2Size, /*Type*/ MachO::RISCV_RELOC_UNSIGNED);
338 // struct relocation_info (8 bytes)
339 emitRelocation(Writer, Fragment, FixupOffset, /*RelSymbol*/ B_Base, Index,
340 IsPCRel, Log2Size, /*Type*/ MachO::RISCV_RELOC_SUBTRACTOR);
341 return;
342 }
343
344 // A + constant
345 if (const MCSymbol *Symbol = Target.getAddSym()) {
346 assert(!Target.getSubSym() && "invalid expression");
347 const MCSectionMachO &Section =
348 static_cast<const MCSectionMachO &>(*Fragment->getParent());
349
350 const bool CanUseLocalRelocation =
351 canUseLocalRelocation(Section, *Symbol, Log2Size);
352 if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) {
353 if (!Symbol->isInSection()) {
354 checkSymbolBase(nullptr, Symbol, Fixup, Asm);
355 return;
356 }
357 const MCSection &Sec = Symbol->getSection();
359 Symbol->setUsedInReloc();
360 }
361
362 const MCSymbol *Base = Writer->getAtom(*Symbol);
363 // If the symbol is a variable it can either be in a section and
364 // we have a base or it is absolute and should have been expanded.
365 assert(!Symbol->isVariable() || Base);
366
367 // Relocations inside debug sections always use local relocations when
368 // possible. This seems to be done because the debugger doesn't fully
369 // understand relocation entries and expects to find values that
370 // have already been fixed up.
371 if (Symbol->isInSection()) {
372 if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
373 Base = nullptr;
374 }
375
376 // RISC-V uses external relocations as much as possible. For debug
377 // sections, and for pointer-sized relocations (.quad), we allow section
378 // relocations. It's code sections that run into trouble.
379 if (Base) {
380 RelSymbol = Base;
381
382 // Add the local offset, if needed.
383 if (Base != Symbol)
384 Value += Asm.getSymbolOffset(*Symbol) - Asm.getSymbolOffset(*Base);
385 } else if (Symbol->isInSection()) {
386 if (!CanUseLocalRelocation) {
387 checkSymbolBase(nullptr, Symbol, Fixup, Asm);
388 return;
389 }
390 // Adjust the relocation to be section-relative.
391 // The index is the section ordinal (1-based).
392 const MCSection &Sec = Symbol->getSection();
393 Index = Sec.getOrdinal() + 1;
394 Value += Writer->getSymbolAddress(*Symbol);
395
396 if (IsPCRel)
397 Value -= Writer->getFragmentAddress(Asm, Fragment) + Fixup.getOffset();
398 } else {
400 "This constant variable should have been expanded during evaluation");
401 }
403 // If there's any addend left to handle, encode it in the instruction.
404 FixedValue = Value;
405 // struct relocation_info (8 bytes)
406 emitRelocation(Writer, Fragment, FixupOffset, RelSymbol, Index,
407 /*IsPCRel*/ false, Log2Size,
409 return;
410 }
411 // We have an addend offset that is encoded in the relocation
412 // record, not inlined in the instruction.
413 if (Value) {
414 if (!isValidInt<24>(Value, "addend too big for relocation", Asm,
415 Fixup.getLoc()))
416 return;
417
418 emitRelocation(Writer, Fragment, FixupOffset, RelSymbol, Index, IsPCRel,
419 Log2Size, Type);
420 // Now set up the Addend relocation.
421 emitRelocation(Writer, Fragment, FixupOffset, /*RelSymbol*/ nullptr,
422 Value & 0xffffff, /*IsPCRel*/ false, /*Log2Size*/ 2,
424 return;
425 }
426
427 emitRelocation(Writer, Fragment, FixupOffset, RelSymbol, Index, IsPCRel,
428 Log2Size, Type);
429 }
430
431 if (RequireExtraAddend) {
432 emitRelocation(Writer, Fragment, FixupOffset, /*RelSymbol*/ nullptr,
433 ExtraAddendValue & 0xffffff, /*IsPCRel*/ false,
434 /*Log2Size*/ 2, /*Type*/ MachO::RISCV_RELOC_ADDEND);
435 }
436}
437
438std::unique_ptr<MCObjectTargetWriter>
440 return std::make_unique<RISCVMachObjectWriter>(CPUType, CPUSubtype);
441}
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: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.
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