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