1 //===-- PPCMachObjectWriter.cpp - PPC Mach-O Writer -----------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "MCTargetDesc/PPCMCTargetDesc.h" 11 #include "MCTargetDesc/PPCFixupKinds.h" 12 #include "llvm/ADT/Twine.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" 21 #include "llvm/Support/MachO.h" 22 23 using namespace llvm; 24 25 namespace { 26 class PPCMachObjectWriter : public MCMachObjectTargetWriter { 27 bool recordScatteredRelocation(MachObjectWriter *Writer, 28 const MCAssembler &Asm, 29 const MCAsmLayout &Layout, 30 const MCFragment *Fragment, 31 const MCFixup &Fixup, MCValue Target, 32 unsigned Log2Size, uint64_t &FixedValue); 33 34 void RecordPPCRelocation(MachObjectWriter *Writer, const MCAssembler &Asm, 35 const MCAsmLayout &Layout, 36 const MCFragment *Fragment, const MCFixup &Fixup, 37 MCValue Target, uint64_t &FixedValue); 38 39 public: 40 PPCMachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype) 41 : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype) {} 42 43 void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm, 44 const MCAsmLayout &Layout, const MCFragment *Fragment, 45 const MCFixup &Fixup, MCValue Target, 46 uint64_t &FixedValue) override { 47 if (Writer->is64Bit()) { 48 report_fatal_error("Relocation emission for MachO/PPC64 unimplemented."); 49 } else 50 RecordPPCRelocation(Writer, Asm, Layout, Fragment, Fixup, Target, 51 FixedValue); 52 } 53 }; 54 } 55 56 /// computes the log2 of the size of the relocation, 57 /// used for relocation_info::r_length. 58 static unsigned getFixupKindLog2Size(unsigned Kind) { 59 switch (Kind) { 60 default: 61 report_fatal_error("log2size(FixupKind): Unhandled fixup kind!"); 62 case FK_PCRel_1: 63 case FK_Data_1: 64 return 0; 65 case FK_PCRel_2: 66 case FK_Data_2: 67 return 1; 68 case FK_PCRel_4: 69 case PPC::fixup_ppc_brcond14: 70 case PPC::fixup_ppc_half16: 71 case PPC::fixup_ppc_br24: 72 case FK_Data_4: 73 return 2; 74 case FK_PCRel_8: 75 case FK_Data_8: 76 return 3; 77 } 78 return 0; 79 } 80 81 /// Translates generic PPC fixup kind to Mach-O/PPC relocation type enum. 82 /// Outline based on PPCELFObjectWriter::getRelocType(). 83 static unsigned getRelocType(const MCValue &Target, 84 const MCFixupKind FixupKind, // from 85 // Fixup.getKind() 86 const bool IsPCRel) { 87 const MCSymbolRefExpr::VariantKind Modifier = 88 Target.isAbsolute() ? MCSymbolRefExpr::VK_None 89 : Target.getSymA()->getKind(); 90 // determine the type of the relocation 91 unsigned Type = MachO::GENERIC_RELOC_VANILLA; 92 if (IsPCRel) { // relative to PC 93 switch ((unsigned)FixupKind) { 94 default: 95 report_fatal_error("Unimplemented fixup kind (relative)"); 96 case PPC::fixup_ppc_br24: 97 Type = MachO::PPC_RELOC_BR24; // R_PPC_REL24 98 break; 99 case PPC::fixup_ppc_brcond14: 100 Type = MachO::PPC_RELOC_BR14; 101 break; 102 case PPC::fixup_ppc_half16: 103 switch (Modifier) { 104 default: 105 llvm_unreachable("Unsupported modifier for half16 fixup"); 106 case MCSymbolRefExpr::VK_PPC_HA: 107 Type = MachO::PPC_RELOC_HA16; 108 break; 109 case MCSymbolRefExpr::VK_PPC_LO: 110 Type = MachO::PPC_RELOC_LO16; 111 break; 112 case MCSymbolRefExpr::VK_PPC_HI: 113 Type = MachO::PPC_RELOC_HI16; 114 break; 115 } 116 break; 117 } 118 } else { 119 switch ((unsigned)FixupKind) { 120 default: 121 report_fatal_error("Unimplemented fixup kind (absolute)!"); 122 case PPC::fixup_ppc_half16: 123 switch (Modifier) { 124 default: 125 llvm_unreachable("Unsupported modifier for half16 fixup"); 126 case MCSymbolRefExpr::VK_PPC_HA: 127 Type = MachO::PPC_RELOC_HA16_SECTDIFF; 128 break; 129 case MCSymbolRefExpr::VK_PPC_LO: 130 Type = MachO::PPC_RELOC_LO16_SECTDIFF; 131 break; 132 case MCSymbolRefExpr::VK_PPC_HI: 133 Type = MachO::PPC_RELOC_HI16_SECTDIFF; 134 break; 135 } 136 break; 137 case FK_Data_4: 138 break; 139 case FK_Data_2: 140 break; 141 } 142 } 143 return Type; 144 } 145 146 static void makeRelocationInfo(MachO::any_relocation_info &MRE, 147 const uint32_t FixupOffset, const uint32_t Index, 148 const unsigned IsPCRel, const unsigned Log2Size, 149 const unsigned IsExtern, const unsigned Type) { 150 MRE.r_word0 = FixupOffset; 151 // The bitfield offsets that work (as determined by trial-and-error) 152 // are different than what is documented in the mach-o manuals. 153 // This appears to be an endianness issue; reversing the order of the 154 // documented bitfields in <llvm/Support/MachO.h> fixes this (but 155 // breaks x86/ARM assembly). 156 MRE.r_word1 = ((Index << 8) | // was << 0 157 (IsPCRel << 7) | // was << 24 158 (Log2Size << 5) | // was << 25 159 (IsExtern << 4) | // was << 27 160 (Type << 0)); // was << 28 161 } 162 163 static void 164 makeScatteredRelocationInfo(MachO::any_relocation_info &MRE, 165 const uint32_t Addr, const unsigned Type, 166 const unsigned Log2Size, const unsigned IsPCRel, 167 const uint32_t Value2) { 168 // For notes on bitfield positions and endianness, see: 169 // https://developer.apple.com/library/mac/documentation/developertools/conceptual/MachORuntime/Reference/reference.html#//apple_ref/doc/uid/20001298-scattered_relocation_entry 170 MRE.r_word0 = ((Addr << 0) | (Type << 24) | (Log2Size << 28) | 171 (IsPCRel << 30) | MachO::R_SCATTERED); 172 MRE.r_word1 = Value2; 173 } 174 175 /// Compute fixup offset (address). 176 static uint32_t getFixupOffset(const MCAsmLayout &Layout, 177 const MCFragment *Fragment, 178 const MCFixup &Fixup) { 179 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset(); 180 // On Mach-O, ppc_fixup_half16 relocations must refer to the 181 // start of the instruction, not the second halfword, as ELF does 182 if (unsigned(Fixup.getKind()) == PPC::fixup_ppc_half16) 183 FixupOffset &= ~uint32_t(3); 184 return FixupOffset; 185 } 186 187 /// \return false if falling back to using non-scattered relocation, 188 /// otherwise true for normal scattered relocation. 189 /// based on X86MachObjectWriter::recordScatteredRelocation 190 /// and ARMMachObjectWriter::recordScatteredRelocation 191 bool PPCMachObjectWriter::recordScatteredRelocation( 192 MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout, 193 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, 194 unsigned Log2Size, uint64_t &FixedValue) { 195 // caller already computes these, can we just pass and reuse? 196 const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup); 197 const MCFixupKind FK = Fixup.getKind(); 198 const unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, FK); 199 const unsigned Type = getRelocType(Target, FK, IsPCRel); 200 201 // Is this a local or SECTDIFF relocation entry? 202 // SECTDIFF relocation entries have symbol subtractions, 203 // and require two entries, the first for the add-symbol value, 204 // the second for the subtract-symbol value. 205 206 // See <reloc.h>. 207 const MCSymbol *A = &Target.getSymA()->getSymbol(); 208 209 if (!A->getFragment()) 210 report_fatal_error("symbol '" + A->getName() + 211 "' can not be undefined in a subtraction expression"); 212 213 uint32_t Value = Writer->getSymbolAddress(*A, Layout); 214 uint64_t SecAddr = Writer->getSectionAddress(A->getFragment()->getParent()); 215 FixedValue += SecAddr; 216 uint32_t Value2 = 0; 217 218 if (const MCSymbolRefExpr *B = Target.getSymB()) { 219 const MCSymbol *SB = &B->getSymbol(); 220 221 if (!SB->getFragment()) 222 report_fatal_error("symbol '" + B->getSymbol().getName() + 223 "' can not be undefined in a subtraction expression"); 224 225 // FIXME: is Type correct? see include/llvm/Support/MachO.h 226 Value2 = Writer->getSymbolAddress(B->getSymbol(), Layout); 227 FixedValue -= Writer->getSectionAddress(SB->getFragment()->getParent()); 228 } 229 // FIXME: does FixedValue get used?? 230 231 // Relocations are written out in reverse order, so the PAIR comes first. 232 if (Type == MachO::PPC_RELOC_SECTDIFF || 233 Type == MachO::PPC_RELOC_HI16_SECTDIFF || 234 Type == MachO::PPC_RELOC_LO16_SECTDIFF || 235 Type == MachO::PPC_RELOC_HA16_SECTDIFF || 236 Type == MachO::PPC_RELOC_LO14_SECTDIFF || 237 Type == MachO::PPC_RELOC_LOCAL_SECTDIFF) { 238 // X86 had this piece, but ARM does not 239 // If the offset is too large to fit in a scattered relocation, 240 // we're hosed. It's an unfortunate limitation of the MachO format. 241 if (FixupOffset > 0xffffff) { 242 char Buffer[32]; 243 format("0x%x", FixupOffset).print(Buffer, sizeof(Buffer)); 244 Asm.getContext().reportError(Fixup.getLoc(), 245 Twine("Section too large, can't encode " 246 "r_address (") + 247 Buffer + ") into 24 bits of scattered " 248 "relocation entry."); 249 return false; 250 } 251 252 // Is this supposed to follow MCTarget/PPCAsmBackend.cpp:adjustFixupValue()? 253 // see PPCMCExpr::evaluateAsRelocatableImpl() 254 uint32_t other_half = 0; 255 switch (Type) { 256 case MachO::PPC_RELOC_LO16_SECTDIFF: 257 other_half = (FixedValue >> 16) & 0xffff; 258 // applyFixupOffset longer extracts the high part because it now assumes 259 // this was already done. 260 // It looks like this is not true for the FixedValue needed with Mach-O 261 // relocs. 262 // So we need to adjust FixedValue again here. 263 FixedValue &= 0xffff; 264 break; 265 case MachO::PPC_RELOC_HA16_SECTDIFF: 266 other_half = FixedValue & 0xffff; 267 FixedValue = 268 ((FixedValue >> 16) + ((FixedValue & 0x8000) ? 1 : 0)) & 0xffff; 269 break; 270 case MachO::PPC_RELOC_HI16_SECTDIFF: 271 other_half = FixedValue & 0xffff; 272 FixedValue = (FixedValue >> 16) & 0xffff; 273 break; 274 default: 275 llvm_unreachable("Invalid PPC scattered relocation type."); 276 break; 277 } 278 279 MachO::any_relocation_info MRE; 280 makeScatteredRelocationInfo(MRE, other_half, MachO::GENERIC_RELOC_PAIR, 281 Log2Size, IsPCRel, Value2); 282 Writer->addRelocation(nullptr, Fragment->getParent(), MRE); 283 } else { 284 // If the offset is more than 24-bits, it won't fit in a scattered 285 // relocation offset field, so we fall back to using a non-scattered 286 // relocation. This is a bit risky, as if the offset reaches out of 287 // the block and the linker is doing scattered loading on this 288 // symbol, things can go badly. 289 // 290 // Required for 'as' compatibility. 291 if (FixupOffset > 0xffffff) 292 return false; 293 } 294 MachO::any_relocation_info MRE; 295 makeScatteredRelocationInfo(MRE, FixupOffset, Type, Log2Size, IsPCRel, Value); 296 Writer->addRelocation(nullptr, Fragment->getParent(), MRE); 297 return true; 298 } 299 300 // see PPCELFObjectWriter for a general outline of cases 301 void PPCMachObjectWriter::RecordPPCRelocation( 302 MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout, 303 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, 304 uint64_t &FixedValue) { 305 const MCFixupKind FK = Fixup.getKind(); // unsigned 306 const unsigned Log2Size = getFixupKindLog2Size(FK); 307 const bool IsPCRel = Writer->isFixupKindPCRel(Asm, FK); 308 const unsigned RelocType = getRelocType(Target, FK, IsPCRel); 309 310 // If this is a difference or a defined symbol plus an offset, then we need a 311 // scattered relocation entry. Differences always require scattered 312 // relocations. 313 if (Target.getSymB() && 314 // Q: are branch targets ever scattered? 315 RelocType != MachO::PPC_RELOC_BR24 && 316 RelocType != MachO::PPC_RELOC_BR14) { 317 recordScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup, Target, 318 Log2Size, FixedValue); 319 return; 320 } 321 322 // this doesn't seem right for RIT_PPC_BR24 323 // Get the symbol data, if any. 324 const MCSymbol *A = nullptr; 325 if (Target.getSymA()) 326 A = &Target.getSymA()->getSymbol(); 327 328 // See <reloc.h>. 329 const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup); 330 unsigned Index = 0; 331 unsigned Type = RelocType; 332 333 const MCSymbol *RelSymbol = nullptr; 334 if (Target.isAbsolute()) { // constant 335 // SymbolNum of 0 indicates the absolute section. 336 // 337 // FIXME: Currently, these are never generated (see code below). I cannot 338 // find a case where they are actually emitted. 339 report_fatal_error("FIXME: relocations to absolute targets " 340 "not yet implemented"); 341 // the above line stolen from ARM, not sure 342 } else { 343 // Resolve constant variables. 344 if (A->isVariable()) { 345 int64_t Res; 346 if (A->getVariableValue()->evaluateAsAbsolute( 347 Res, Layout, Writer->getSectionAddressMap())) { 348 FixedValue = Res; 349 return; 350 } 351 } 352 353 // Check whether we need an external or internal relocation. 354 if (Writer->doesSymbolRequireExternRelocation(*A)) { 355 RelSymbol = A; 356 // For external relocations, make sure to offset the fixup value to 357 // compensate for the addend of the symbol address, if it was 358 // undefined. This occurs with weak definitions, for example. 359 if (!A->isUndefined()) 360 FixedValue -= Layout.getSymbolOffset(*A); 361 } else { 362 // The index is the section ordinal (1-based). 363 const MCSection &Sec = A->getSection(); 364 Index = Sec.getOrdinal() + 1; 365 FixedValue += Writer->getSectionAddress(&Sec); 366 } 367 if (IsPCRel) 368 FixedValue -= Writer->getSectionAddress(Fragment->getParent()); 369 } 370 371 // struct relocation_info (8 bytes) 372 MachO::any_relocation_info MRE; 373 makeRelocationInfo(MRE, FixupOffset, Index, IsPCRel, Log2Size, false, Type); 374 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE); 375 } 376 377 MCObjectWriter *llvm::createPPCMachObjectWriter(raw_pwrite_stream &OS, 378 bool Is64Bit, uint32_t CPUType, 379 uint32_t CPUSubtype) { 380 return createMachObjectWriter( 381 new PPCMachObjectWriter(Is64Bit, CPUType, CPUSubtype), OS, 382 /*IsLittleEndian=*/false); 383 } 384