1//===-- PPCAsmBackend.cpp - PPC Assembler Backend -------------------------===//
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
9#include "MCTargetDesc/PPCFixupKinds.h"
10#include "MCTargetDesc/PPCMCAsmInfo.h"
11#include "MCTargetDesc/PPCMCTargetDesc.h"
12#include "llvm/BinaryFormat/ELF.h"
13#include "llvm/BinaryFormat/MachO.h"
14#include "llvm/MC/MCAsmBackend.h"
15#include "llvm/MC/MCAssembler.h"
16#include "llvm/MC/MCContext.h"
17#include "llvm/MC/MCELFObjectWriter.h"
18#include "llvm/MC/MCMachObjectWriter.h"
19#include "llvm/MC/MCObjectWriter.h"
20#include "llvm/MC/MCSubtargetInfo.h"
21#include "llvm/MC/MCSymbolELF.h"
22#include "llvm/MC/MCSymbolXCOFF.h"
23#include "llvm/MC/MCValue.h"
24#include "llvm/MC/TargetRegistry.h"
25#include "llvm/Support/ErrorHandling.h"
26using namespace llvm;
27
28static uint64_t adjustFixupValue(MCContext &Ctx, const MCFixup &Fixup,
29 unsigned Kind, uint64_t Value) {
30 auto checkBrFixup = [&](unsigned Bits) {
31 int64_t SVal = int64_t(Value);
32 if ((Value & 3) != 0) {
33 Ctx.reportError(L: Fixup.getLoc(), Msg: "branch target not a multiple of four (" +
34 Twine(SVal) + ")");
35 return;
36 }
37
38 // Low two bits are not encoded.
39 if (!isIntN(N: Bits + 2, x: Value)) {
40 Ctx.reportError(L: Fixup.getLoc(), Msg: "branch target out of range (" +
41 Twine(SVal) + " not between " +
42 Twine(minIntN(N: Bits) * 4) + " and " +
43 Twine(maxIntN(N: Bits) * 4) + ")");
44 }
45 };
46
47 switch (Kind) {
48 default:
49 llvm_unreachable("Unknown fixup kind!");
50 case FK_Data_1:
51 case FK_Data_2:
52 case FK_Data_4:
53 case FK_Data_8:
54 case PPC::fixup_ppc_nofixup:
55 return Value;
56 case PPC::fixup_ppc_brcond14:
57 case PPC::fixup_ppc_brcond14abs:
58 checkBrFixup(14);
59 return Value & 0xfffc;
60 case PPC::fixup_ppc_br24:
61 case PPC::fixup_ppc_br24abs:
62 case PPC::fixup_ppc_br24_notoc:
63 checkBrFixup(24);
64 return Value & 0x3fffffc;
65 case PPC::fixup_ppc_half16:
66 return Value & 0xffff;
67 case PPC::fixup_ppc_half16ds:
68 case PPC::fixup_ppc_half16dq:
69 return Value & 0xfffc;
70 case PPC::fixup_ppc_pcrel32:
71 case PPC::fixup_ppc_imm32:
72 return Value & 0xffffffff;
73 case PPC::fixup_ppc_pcrel34:
74 case PPC::fixup_ppc_imm34:
75 return Value & 0x3ffffffff;
76 }
77}
78
79static unsigned getFixupKindNumBytes(unsigned Kind) {
80 switch (Kind) {
81 default:
82 llvm_unreachable("Unknown fixup kind!");
83 case FK_Data_1:
84 return 1;
85 case FK_Data_2:
86 case PPC::fixup_ppc_half16:
87 case PPC::fixup_ppc_half16ds:
88 case PPC::fixup_ppc_half16dq:
89 return 2;
90 case FK_Data_4:
91 case PPC::fixup_ppc_brcond14:
92 case PPC::fixup_ppc_brcond14abs:
93 case PPC::fixup_ppc_br24:
94 case PPC::fixup_ppc_br24abs:
95 case PPC::fixup_ppc_br24_notoc:
96 return 4;
97 case PPC::fixup_ppc_pcrel32:
98 case PPC::fixup_ppc_imm32:
99 case PPC::fixup_ppc_pcrel34:
100 case PPC::fixup_ppc_imm34:
101 case FK_Data_8:
102 return 8;
103 case PPC::fixup_ppc_nofixup:
104 return 0;
105 }
106}
107
108namespace {
109
110class PPCAsmBackend : public MCAsmBackend {
111protected:
112 Triple TT;
113public:
114 PPCAsmBackend(const Target &T, const Triple &TT)
115 : MCAsmBackend(TT.isLittleEndian() ? llvm::endianness::little
116 : llvm::endianness::big),
117 TT(TT) {}
118
119 MCFixupKindInfo getFixupKindInfo(MCFixupKind Kind) const override;
120
121 void applyFixup(const MCFragment &, const MCFixup &Fixup,
122 const MCValue &Target, uint8_t *Data, uint64_t Value,
123 bool IsResolved) override;
124
125 bool shouldForceRelocation(const MCFixup &Fixup, const MCValue &Target) {
126 // If there is a @ specifier, unless it is optimized out (e.g. constant @l),
127 // force a relocation.
128 if (Target.getSpecifier())
129 return true;
130 MCFixupKind Kind = Fixup.getKind();
131 switch ((unsigned)Kind) {
132 default:
133 return false;
134 case PPC::fixup_ppc_br24:
135 case PPC::fixup_ppc_br24abs:
136 case PPC::fixup_ppc_br24_notoc:
137 // If the target symbol has a local entry point we must not attempt
138 // to resolve the fixup directly. Emit a relocation and leave
139 // resolution of the final target address to the linker.
140 if (const auto *A = Target.getAddSym()) {
141 if (getContext().isELF()) {
142 // The "other" values are stored in the last 6 bits of the second
143 // byte. The traditional defines for STO values assume the full byte
144 // and thus the shift to pack it.
145 unsigned Other = static_cast<const MCSymbolELF *>(A)->getOther() << 2;
146 if ((Other & ELF::STO_PPC64_LOCAL_MASK) != 0)
147 return true;
148 } else if (getContext().isXCOFF()) {
149 auto *S = static_cast<const MCSymbolXCOFF *>(A);
150 return !Target.isAbsolute() && S->isExternal() &&
151 S->getStorageClass() == XCOFF::C_WEAKEXT;
152 }
153 }
154 return false;
155 }
156 }
157
158 bool writeNopData(raw_ostream &OS, uint64_t Count,
159 const MCSubtargetInfo *STI) const override {
160 uint64_t NumNops = Count / 4;
161 for (uint64_t i = 0; i != NumNops; ++i)
162 support::endian::write<uint32_t>(os&: OS, value: 0x60000000, endian: Endian);
163
164 OS.write_zeros(NumZeros: Count % 4);
165
166 return true;
167 }
168};
169} // end anonymous namespace
170
171MCFixupKindInfo PPCAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
172 // clang-format off
173 const static MCFixupKindInfo InfosBE[PPC::NumTargetFixupKinds] = {
174 // name offset bits flags
175 {.Name: "fixup_ppc_br24", .TargetOffset: 6, .TargetSize: 24, .Flags: 0},
176 {.Name: "fixup_ppc_br24_notoc", .TargetOffset: 6, .TargetSize: 24, .Flags: 0},
177 {.Name: "fixup_ppc_brcond14", .TargetOffset: 16, .TargetSize: 14, .Flags: 0},
178 {.Name: "fixup_ppc_br24abs", .TargetOffset: 6, .TargetSize: 24, .Flags: 0},
179 {.Name: "fixup_ppc_brcond14abs", .TargetOffset: 16, .TargetSize: 14, .Flags: 0},
180 {.Name: "fixup_ppc_half16", .TargetOffset: 0, .TargetSize: 16, .Flags: 0},
181 {.Name: "fixup_ppc_half16ds", .TargetOffset: 0, .TargetSize: 14, .Flags: 0},
182 {.Name: "fixup_ppc_pcrel32", .TargetOffset: 0, .TargetSize: 32, .Flags: 0},
183 {.Name: "fixup_ppc_imm32", .TargetOffset: 0, .TargetSize: 32, .Flags: 0},
184 {.Name: "fixup_ppc_pcrel34", .TargetOffset: 0, .TargetSize: 34, .Flags: 0},
185 {.Name: "fixup_ppc_imm34", .TargetOffset: 0, .TargetSize: 34, .Flags: 0},
186 {.Name: "fixup_ppc_nofixup", .TargetOffset: 0, .TargetSize: 0, .Flags: 0}};
187 const static MCFixupKindInfo InfosLE[PPC::NumTargetFixupKinds] = {
188 // name offset bits flags
189 {.Name: "fixup_ppc_br24", .TargetOffset: 2, .TargetSize: 24, .Flags: 0},
190 {.Name: "fixup_ppc_br24_notoc", .TargetOffset: 2, .TargetSize: 24, .Flags: 0},
191 {.Name: "fixup_ppc_brcond14", .TargetOffset: 2, .TargetSize: 14, .Flags: 0},
192 {.Name: "fixup_ppc_br24abs", .TargetOffset: 2, .TargetSize: 24, .Flags: 0},
193 {.Name: "fixup_ppc_brcond14abs", .TargetOffset: 2, .TargetSize: 14, .Flags: 0},
194 {.Name: "fixup_ppc_half16", .TargetOffset: 0, .TargetSize: 16, .Flags: 0},
195 {.Name: "fixup_ppc_half16ds", .TargetOffset: 2, .TargetSize: 14, .Flags: 0},
196 {.Name: "fixup_ppc_pcrel32", .TargetOffset: 0, .TargetSize: 32, .Flags: 0},
197 {.Name: "fixup_ppc_imm32", .TargetOffset: 0, .TargetSize: 32, .Flags: 0},
198 {.Name: "fixup_ppc_pcrel34", .TargetOffset: 0, .TargetSize: 34, .Flags: 0},
199 {.Name: "fixup_ppc_imm34", .TargetOffset: 0, .TargetSize: 34, .Flags: 0},
200 {.Name: "fixup_ppc_nofixup", .TargetOffset: 0, .TargetSize: 0, .Flags: 0}};
201 // clang-format on
202
203 // Fixup kinds from .reloc directive are like R_PPC_NONE/R_PPC64_NONE. They
204 // do not require any extra processing.
205 if (mc::isRelocation(FixupKind: Kind))
206 return {};
207
208 if (Kind < FirstTargetFixupKind)
209 return MCAsmBackend::getFixupKindInfo(Kind);
210
211 assert(Kind - FirstTargetFixupKind < PPC::NumTargetFixupKinds &&
212 "Invalid kind!");
213 return (Endian == llvm::endianness::little
214 ? InfosLE
215 : InfosBE)[Kind - FirstTargetFixupKind];
216}
217
218void PPCAsmBackend::applyFixup(const MCFragment &F, const MCFixup &Fixup,
219 const MCValue &TargetVal, uint8_t *Data,
220 uint64_t Value, bool IsResolved) {
221 // In PPC64 ELFv1, .quad .TOC.@tocbase in the .opd section is expected to
222 // reference the null symbol.
223 auto Target = TargetVal;
224 if (Target.getSpecifier() == PPC::S_TOCBASE)
225 Target.setAddSym(nullptr);
226 if (IsResolved && shouldForceRelocation(Fixup, Target))
227 IsResolved = false;
228 if (!IsResolved)
229 Asm->getWriter().recordRelocation(F, Fixup, Target, FixedValue&: Value);
230
231 MCFixupKind Kind = Fixup.getKind();
232 if (mc::isRelocation(FixupKind: Kind))
233 return;
234 Value = adjustFixupValue(Ctx&: getContext(), Fixup, Kind, Value);
235 if (!Value)
236 return; // Doesn't change encoding.
237
238 unsigned NumBytes = getFixupKindNumBytes(Kind);
239
240 // For each byte of the fragment that the fixup touches, mask in the bits
241 // from the fixup value. The Value has been "split up" into the appropriate
242 // bitfields above.
243 for (unsigned i = 0; i != NumBytes; ++i) {
244 unsigned Idx = Endian == llvm::endianness::little ? i : (NumBytes - 1 - i);
245 Data[i] |= uint8_t((Value >> (Idx * 8)) & 0xff);
246 }
247}
248
249// FIXME: This should be in a separate file.
250namespace {
251
252class ELFPPCAsmBackend : public PPCAsmBackend {
253public:
254 ELFPPCAsmBackend(const Target &T, const Triple &TT) : PPCAsmBackend(T, TT) {}
255
256 std::unique_ptr<MCObjectTargetWriter>
257 createObjectTargetWriter() const override {
258 uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(OSType: TT.getOS());
259 bool Is64 = TT.isPPC64();
260 return createPPCELFObjectWriter(Is64Bit: Is64, OSABI);
261 }
262
263 std::optional<MCFixupKind> getFixupKind(StringRef Name) const override;
264};
265
266class XCOFFPPCAsmBackend : public PPCAsmBackend {
267public:
268 XCOFFPPCAsmBackend(const Target &T, const Triple &TT)
269 : PPCAsmBackend(T, TT) {}
270
271 std::unique_ptr<MCObjectTargetWriter>
272 createObjectTargetWriter() const override {
273 return createPPCXCOFFObjectWriter(Is64Bit: TT.isArch64Bit());
274 }
275};
276
277} // end anonymous namespace
278
279std::optional<MCFixupKind>
280ELFPPCAsmBackend::getFixupKind(StringRef Name) const {
281 if (TT.isOSBinFormatELF()) {
282 unsigned Type;
283 if (TT.isPPC64()) {
284 Type = llvm::StringSwitch<unsigned>(Name)
285#define ELF_RELOC(X, Y) .Case(#X, Y)
286#include "llvm/BinaryFormat/ELFRelocs/PowerPC64.def"
287#undef ELF_RELOC
288 .Case(S: "BFD_RELOC_NONE", Value: ELF::R_PPC64_NONE)
289 .Case(S: "BFD_RELOC_16", Value: ELF::R_PPC64_ADDR16)
290 .Case(S: "BFD_RELOC_32", Value: ELF::R_PPC64_ADDR32)
291 .Case(S: "BFD_RELOC_64", Value: ELF::R_PPC64_ADDR64)
292 .Default(Value: -1u);
293 } else {
294 Type = llvm::StringSwitch<unsigned>(Name)
295#define ELF_RELOC(X, Y) .Case(#X, Y)
296#include "llvm/BinaryFormat/ELFRelocs/PowerPC.def"
297#undef ELF_RELOC
298 .Case(S: "BFD_RELOC_NONE", Value: ELF::R_PPC_NONE)
299 .Case(S: "BFD_RELOC_16", Value: ELF::R_PPC_ADDR16)
300 .Case(S: "BFD_RELOC_32", Value: ELF::R_PPC_ADDR32)
301 .Default(Value: -1u);
302 }
303 if (Type != -1u)
304 return static_cast<MCFixupKind>(FirstLiteralRelocationKind + Type);
305 }
306 return std::nullopt;
307}
308
309MCAsmBackend *llvm::createPPCAsmBackend(const Target &T,
310 const MCSubtargetInfo &STI,
311 const MCRegisterInfo &MRI,
312 const MCTargetOptions &Options) {
313 const Triple &TT = STI.getTargetTriple();
314 if (TT.isOSBinFormatXCOFF())
315 return new XCOFFPPCAsmBackend(T, TT);
316
317 return new ELFPPCAsmBackend(T, TT);
318}
319