1 //===-- PPCMachObjectWriter.cpp - PPC Mach-O Writer -----------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "MCTargetDesc/PPCFixupKinds.h"
10 #include "MCTargetDesc/PPCMCTargetDesc.h"
11 #include "llvm/ADT/Twine.h"
12 #include "llvm/BinaryFormat/MachO.h"
13 #include "llvm/MC/MCAsmLayout.h"
14 #include "llvm/MC/MCAssembler.h"
15 #include "llvm/MC/MCContext.h"
16 #include "llvm/MC/MCMachObjectWriter.h"
17 #include "llvm/MC/MCSectionMachO.h"
18 #include "llvm/MC/MCValue.h"
19 #include "llvm/Support/ErrorHandling.h"
20 #include "llvm/Support/Format.h"
25 class PPCMachObjectWriter
: public MCMachObjectTargetWriter
{
26 bool recordScatteredRelocation(MachObjectWriter
*Writer
,
27 const MCAssembler
&Asm
,
28 const MCAsmLayout
&Layout
,
29 const MCFragment
*Fragment
,
30 const MCFixup
&Fixup
, MCValue Target
,
31 unsigned Log2Size
, uint64_t &FixedValue
);
33 void RecordPPCRelocation(MachObjectWriter
*Writer
, const MCAssembler
&Asm
,
34 const MCAsmLayout
&Layout
,
35 const MCFragment
*Fragment
, const MCFixup
&Fixup
,
36 MCValue Target
, uint64_t &FixedValue
);
39 PPCMachObjectWriter(bool Is64Bit
, uint32_t CPUType
, uint32_t CPUSubtype
)
40 : MCMachObjectTargetWriter(Is64Bit
, CPUType
, CPUSubtype
) {}
42 void recordRelocation(MachObjectWriter
*Writer
, MCAssembler
&Asm
,
43 const MCAsmLayout
&Layout
, const MCFragment
*Fragment
,
44 const MCFixup
&Fixup
, MCValue Target
,
45 uint64_t &FixedValue
) override
{
46 if (Writer
->is64Bit()) {
47 report_fatal_error("Relocation emission for MachO/PPC64 unimplemented.");
49 RecordPPCRelocation(Writer
, Asm
, Layout
, Fragment
, Fixup
, Target
,
55 /// computes the log2 of the size of the relocation,
56 /// used for relocation_info::r_length.
57 static unsigned getFixupKindLog2Size(unsigned Kind
) {
60 report_fatal_error("log2size(FixupKind): Unhandled fixup kind!");
68 case PPC::fixup_ppc_brcond14
:
69 case PPC::fixup_ppc_half16
:
70 case PPC::fixup_ppc_br24
:
80 /// Translates generic PPC fixup kind to Mach-O/PPC relocation type enum.
81 /// Outline based on PPCELFObjectWriter::getRelocType().
82 static unsigned getRelocType(const MCValue
&Target
,
83 const MCFixupKind FixupKind
, // from
86 const MCSymbolRefExpr::VariantKind Modifier
=
87 Target
.isAbsolute() ? MCSymbolRefExpr::VK_None
88 : Target
.getSymA()->getKind();
89 // determine the type of the relocation
90 unsigned Type
= MachO::GENERIC_RELOC_VANILLA
;
91 if (IsPCRel
) { // relative to PC
92 switch ((unsigned)FixupKind
) {
94 report_fatal_error("Unimplemented fixup kind (relative)");
95 case PPC::fixup_ppc_br24
:
96 Type
= MachO::PPC_RELOC_BR24
; // R_PPC_REL24
98 case PPC::fixup_ppc_brcond14
:
99 Type
= MachO::PPC_RELOC_BR14
;
101 case PPC::fixup_ppc_half16
:
104 llvm_unreachable("Unsupported modifier for half16 fixup");
105 case MCSymbolRefExpr::VK_PPC_HA
:
106 Type
= MachO::PPC_RELOC_HA16
;
108 case MCSymbolRefExpr::VK_PPC_LO
:
109 Type
= MachO::PPC_RELOC_LO16
;
111 case MCSymbolRefExpr::VK_PPC_HI
:
112 Type
= MachO::PPC_RELOC_HI16
;
118 switch ((unsigned)FixupKind
) {
120 report_fatal_error("Unimplemented fixup kind (absolute)!");
121 case PPC::fixup_ppc_half16
:
124 llvm_unreachable("Unsupported modifier for half16 fixup");
125 case MCSymbolRefExpr::VK_PPC_HA
:
126 Type
= MachO::PPC_RELOC_HA16_SECTDIFF
;
128 case MCSymbolRefExpr::VK_PPC_LO
:
129 Type
= MachO::PPC_RELOC_LO16_SECTDIFF
;
131 case MCSymbolRefExpr::VK_PPC_HI
:
132 Type
= MachO::PPC_RELOC_HI16_SECTDIFF
;
145 static void makeRelocationInfo(MachO::any_relocation_info
&MRE
,
146 const uint32_t FixupOffset
, const uint32_t Index
,
147 const unsigned IsPCRel
, const unsigned Log2Size
,
148 const unsigned IsExtern
, const unsigned Type
) {
149 MRE
.r_word0
= FixupOffset
;
150 // The bitfield offsets that work (as determined by trial-and-error)
151 // are different than what is documented in the mach-o manuals.
152 // This appears to be an endianness issue; reversing the order of the
153 // documented bitfields in <llvm/BinaryFormat/MachO.h> fixes this (but
154 // breaks x86/ARM assembly).
155 MRE
.r_word1
= ((Index
<< 8) | // was << 0
156 (IsPCRel
<< 7) | // was << 24
157 (Log2Size
<< 5) | // was << 25
158 (IsExtern
<< 4) | // was << 27
159 (Type
<< 0)); // was << 28
163 makeScatteredRelocationInfo(MachO::any_relocation_info
&MRE
,
164 const uint32_t Addr
, const unsigned Type
,
165 const unsigned Log2Size
, const unsigned IsPCRel
,
166 const uint32_t Value2
) {
167 // For notes on bitfield positions and endianness, see:
168 // https://developer.apple.com/library/mac/documentation/developertools/conceptual/MachORuntime/Reference/reference.html#//apple_ref/doc/uid/20001298-scattered_relocation_entry
169 MRE
.r_word0
= ((Addr
<< 0) | (Type
<< 24) | (Log2Size
<< 28) |
170 (IsPCRel
<< 30) | MachO::R_SCATTERED
);
171 MRE
.r_word1
= Value2
;
174 /// Compute fixup offset (address).
175 static uint32_t getFixupOffset(const MCAsmLayout
&Layout
,
176 const MCFragment
*Fragment
,
177 const MCFixup
&Fixup
) {
178 uint32_t FixupOffset
= Layout
.getFragmentOffset(Fragment
) + Fixup
.getOffset();
179 // On Mach-O, ppc_fixup_half16 relocations must refer to the
180 // start of the instruction, not the second halfword, as ELF does
181 if (Fixup
.getTargetKind() == PPC::fixup_ppc_half16
)
182 FixupOffset
&= ~uint32_t(3);
186 /// \return false if falling back to using non-scattered relocation,
187 /// otherwise true for normal scattered relocation.
188 /// based on X86MachObjectWriter::recordScatteredRelocation
189 /// and ARMMachObjectWriter::recordScatteredRelocation
190 bool PPCMachObjectWriter::recordScatteredRelocation(
191 MachObjectWriter
*Writer
, const MCAssembler
&Asm
, const MCAsmLayout
&Layout
,
192 const MCFragment
*Fragment
, const MCFixup
&Fixup
, MCValue Target
,
193 unsigned Log2Size
, uint64_t &FixedValue
) {
194 // caller already computes these, can we just pass and reuse?
195 const uint32_t FixupOffset
= getFixupOffset(Layout
, Fragment
, Fixup
);
196 const MCFixupKind FK
= Fixup
.getKind();
197 const unsigned IsPCRel
= Writer
->isFixupKindPCRel(Asm
, FK
);
198 const unsigned Type
= getRelocType(Target
, FK
, IsPCRel
);
200 // Is this a local or SECTDIFF relocation entry?
201 // SECTDIFF relocation entries have symbol subtractions,
202 // and require two entries, the first for the add-symbol value,
203 // the second for the subtract-symbol value.
206 const MCSymbol
*A
= &Target
.getSymA()->getSymbol();
208 if (!A
->getFragment())
209 report_fatal_error("symbol '" + A
->getName() +
210 "' can not be undefined in a subtraction expression");
212 uint32_t Value
= Writer
->getSymbolAddress(*A
, Layout
);
213 uint64_t SecAddr
= Writer
->getSectionAddress(A
->getFragment()->getParent());
214 FixedValue
+= SecAddr
;
217 if (const MCSymbolRefExpr
*B
= Target
.getSymB()) {
218 const MCSymbol
*SB
= &B
->getSymbol();
220 if (!SB
->getFragment())
221 report_fatal_error("symbol '" + SB
->getName() +
222 "' can not be undefined in a subtraction expression");
224 // FIXME: is Type correct? see include/llvm/BinaryFormat/MachO.h
225 Value2
= Writer
->getSymbolAddress(*SB
, Layout
);
226 FixedValue
-= Writer
->getSectionAddress(SB
->getFragment()->getParent());
228 // FIXME: does FixedValue get used??
230 // Relocations are written out in reverse order, so the PAIR comes first.
231 if (Type
== MachO::PPC_RELOC_SECTDIFF
||
232 Type
== MachO::PPC_RELOC_HI16_SECTDIFF
||
233 Type
== MachO::PPC_RELOC_LO16_SECTDIFF
||
234 Type
== MachO::PPC_RELOC_HA16_SECTDIFF
||
235 Type
== MachO::PPC_RELOC_LO14_SECTDIFF
||
236 Type
== MachO::PPC_RELOC_LOCAL_SECTDIFF
) {
237 // X86 had this piece, but ARM does not
238 // If the offset is too large to fit in a scattered relocation,
239 // we're hosed. It's an unfortunate limitation of the MachO format.
240 if (FixupOffset
> 0xffffff) {
242 format("0x%x", FixupOffset
).print(Buffer
, sizeof(Buffer
));
243 Asm
.getContext().reportError(Fixup
.getLoc(),
244 Twine("Section too large, can't encode "
246 Buffer
+ ") into 24 bits of scattered "
247 "relocation entry.");
251 // Is this supposed to follow MCTarget/PPCAsmBackend.cpp:adjustFixupValue()?
252 // see PPCMCExpr::evaluateAsRelocatableImpl()
253 uint32_t other_half
= 0;
255 case MachO::PPC_RELOC_LO16_SECTDIFF
:
256 other_half
= (FixedValue
>> 16) & 0xffff;
257 // applyFixupOffset longer extracts the high part because it now assumes
258 // this was already done.
259 // It looks like this is not true for the FixedValue needed with Mach-O
261 // So we need to adjust FixedValue again here.
262 FixedValue
&= 0xffff;
264 case MachO::PPC_RELOC_HA16_SECTDIFF
:
265 other_half
= FixedValue
& 0xffff;
267 ((FixedValue
>> 16) + ((FixedValue
& 0x8000) ? 1 : 0)) & 0xffff;
269 case MachO::PPC_RELOC_HI16_SECTDIFF
:
270 other_half
= FixedValue
& 0xffff;
271 FixedValue
= (FixedValue
>> 16) & 0xffff;
274 llvm_unreachable("Invalid PPC scattered relocation type.");
278 MachO::any_relocation_info MRE
;
279 makeScatteredRelocationInfo(MRE
, other_half
, MachO::GENERIC_RELOC_PAIR
,
280 Log2Size
, IsPCRel
, Value2
);
281 Writer
->addRelocation(nullptr, Fragment
->getParent(), MRE
);
283 // If the offset is more than 24-bits, it won't fit in a scattered
284 // relocation offset field, so we fall back to using a non-scattered
285 // relocation. This is a bit risky, as if the offset reaches out of
286 // the block and the linker is doing scattered loading on this
287 // symbol, things can go badly.
289 // Required for 'as' compatibility.
290 if (FixupOffset
> 0xffffff)
293 MachO::any_relocation_info MRE
;
294 makeScatteredRelocationInfo(MRE
, FixupOffset
, Type
, Log2Size
, IsPCRel
, Value
);
295 Writer
->addRelocation(nullptr, Fragment
->getParent(), MRE
);
299 // see PPCELFObjectWriter for a general outline of cases
300 void PPCMachObjectWriter::RecordPPCRelocation(
301 MachObjectWriter
*Writer
, const MCAssembler
&Asm
, const MCAsmLayout
&Layout
,
302 const MCFragment
*Fragment
, const MCFixup
&Fixup
, MCValue Target
,
303 uint64_t &FixedValue
) {
304 const MCFixupKind FK
= Fixup
.getKind(); // unsigned
305 const unsigned Log2Size
= getFixupKindLog2Size(FK
);
306 const bool IsPCRel
= Writer
->isFixupKindPCRel(Asm
, FK
);
307 const unsigned RelocType
= getRelocType(Target
, FK
, IsPCRel
);
309 // If this is a difference or a defined symbol plus an offset, then we need a
310 // scattered relocation entry. Differences always require scattered
312 if (Target
.getSymB() &&
313 // Q: are branch targets ever scattered?
314 RelocType
!= MachO::PPC_RELOC_BR24
&&
315 RelocType
!= MachO::PPC_RELOC_BR14
) {
316 recordScatteredRelocation(Writer
, Asm
, Layout
, Fragment
, Fixup
, Target
,
317 Log2Size
, FixedValue
);
321 // this doesn't seem right for RIT_PPC_BR24
322 // Get the symbol data, if any.
323 const MCSymbol
*A
= nullptr;
324 if (Target
.getSymA())
325 A
= &Target
.getSymA()->getSymbol();
328 const uint32_t FixupOffset
= getFixupOffset(Layout
, Fragment
, Fixup
);
330 unsigned Type
= RelocType
;
332 const MCSymbol
*RelSymbol
= nullptr;
333 if (Target
.isAbsolute()) { // constant
334 // SymbolNum of 0 indicates the absolute section.
336 // FIXME: Currently, these are never generated (see code below). I cannot
337 // find a case where they are actually emitted.
338 report_fatal_error("FIXME: relocations to absolute targets "
339 "not yet implemented");
340 // the above line stolen from ARM, not sure
342 // Resolve constant variables.
343 if (A
->isVariable()) {
345 if (A
->getVariableValue()->evaluateAsAbsolute(
346 Res
, Layout
, Writer
->getSectionAddressMap())) {
352 // Check whether we need an external or internal relocation.
353 if (Writer
->doesSymbolRequireExternRelocation(*A
)) {
355 // For external relocations, make sure to offset the fixup value to
356 // compensate for the addend of the symbol address, if it was
357 // undefined. This occurs with weak definitions, for example.
358 if (!A
->isUndefined())
359 FixedValue
-= Layout
.getSymbolOffset(*A
);
361 // The index is the section ordinal (1-based).
362 const MCSection
&Sec
= A
->getSection();
363 Index
= Sec
.getOrdinal() + 1;
364 FixedValue
+= Writer
->getSectionAddress(&Sec
);
367 FixedValue
-= Writer
->getSectionAddress(Fragment
->getParent());
370 // struct relocation_info (8 bytes)
371 MachO::any_relocation_info MRE
;
372 makeRelocationInfo(MRE
, FixupOffset
, Index
, IsPCRel
, Log2Size
, false, Type
);
373 Writer
->addRelocation(RelSymbol
, Fragment
->getParent(), MRE
);
376 std::unique_ptr
<MCObjectTargetWriter
>
377 llvm::createPPCMachObjectWriter(bool Is64Bit
, uint32_t CPUType
,
378 uint32_t CPUSubtype
) {
379 return std::make_unique
<PPCMachObjectWriter
>(Is64Bit
, CPUType
, CPUSubtype
);