1259698Sdim//===-- PPCMachObjectWriter.cpp - PPC Mach-O Writer -----------------------===// 2259698Sdim// 3259698Sdim// The LLVM Compiler Infrastructure 4259698Sdim// 5259698Sdim// This file is distributed under the University of Illinois Open Source 6259698Sdim// License. See LICENSE.TXT for details. 7259698Sdim// 8259698Sdim//===----------------------------------------------------------------------===// 9259698Sdim 10259698Sdim#include "MCTargetDesc/PPCMCTargetDesc.h" 11259698Sdim#include "MCTargetDesc/PPCFixupKinds.h" 12259698Sdim#include "llvm/ADT/Twine.h" 13259698Sdim#include "llvm/MC/MCAsmLayout.h" 14259698Sdim#include "llvm/MC/MCAssembler.h" 15259698Sdim#include "llvm/MC/MCContext.h" 16259698Sdim#include "llvm/MC/MCMachObjectWriter.h" 17259698Sdim#include "llvm/MC/MCSectionMachO.h" 18259698Sdim#include "llvm/MC/MCValue.h" 19259698Sdim#include "llvm/Support/ErrorHandling.h" 20259698Sdim#include "llvm/Support/Format.h" 21259698Sdim#include "llvm/Support/MachO.h" 22259698Sdim 23259698Sdimusing namespace llvm; 24259698Sdim 25259698Sdimnamespace { 26259698Sdimclass PPCMachObjectWriter : public MCMachObjectTargetWriter { 27288943Sdim bool recordScatteredRelocation(MachObjectWriter *Writer, 28259698Sdim const MCAssembler &Asm, 29259698Sdim const MCAsmLayout &Layout, 30259698Sdim const MCFragment *Fragment, 31259698Sdim const MCFixup &Fixup, MCValue Target, 32259698Sdim unsigned Log2Size, uint64_t &FixedValue); 33259698Sdim 34259698Sdim void RecordPPCRelocation(MachObjectWriter *Writer, const MCAssembler &Asm, 35259698Sdim const MCAsmLayout &Layout, 36259698Sdim const MCFragment *Fragment, const MCFixup &Fixup, 37259698Sdim MCValue Target, uint64_t &FixedValue); 38259698Sdim 39259698Sdimpublic: 40259698Sdim PPCMachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype) 41288943Sdim : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype) {} 42259698Sdim 43288943Sdim void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm, 44259698Sdim const MCAsmLayout &Layout, const MCFragment *Fragment, 45259698Sdim const MCFixup &Fixup, MCValue Target, 46276479Sdim uint64_t &FixedValue) override { 47259698Sdim if (Writer->is64Bit()) { 48259698Sdim report_fatal_error("Relocation emission for MachO/PPC64 unimplemented."); 49259698Sdim } else 50259698Sdim RecordPPCRelocation(Writer, Asm, Layout, Fragment, Fixup, Target, 51259698Sdim FixedValue); 52259698Sdim } 53259698Sdim}; 54259698Sdim} 55259698Sdim 56259698Sdim/// computes the log2 of the size of the relocation, 57259698Sdim/// used for relocation_info::r_length. 58259698Sdimstatic unsigned getFixupKindLog2Size(unsigned Kind) { 59259698Sdim switch (Kind) { 60259698Sdim default: 61259698Sdim report_fatal_error("log2size(FixupKind): Unhandled fixup kind!"); 62259698Sdim case FK_PCRel_1: 63259698Sdim case FK_Data_1: 64259698Sdim return 0; 65259698Sdim case FK_PCRel_2: 66259698Sdim case FK_Data_2: 67259698Sdim return 1; 68259698Sdim case FK_PCRel_4: 69259698Sdim case PPC::fixup_ppc_brcond14: 70259698Sdim case PPC::fixup_ppc_half16: 71259698Sdim case PPC::fixup_ppc_br24: 72259698Sdim case FK_Data_4: 73259698Sdim return 2; 74259698Sdim case FK_PCRel_8: 75259698Sdim case FK_Data_8: 76259698Sdim return 3; 77259698Sdim } 78259698Sdim return 0; 79259698Sdim} 80259698Sdim 81259698Sdim/// Translates generic PPC fixup kind to Mach-O/PPC relocation type enum. 82280031Sdim/// Outline based on PPCELFObjectWriter::GetRelocType(). 83259698Sdimstatic unsigned getRelocType(const MCValue &Target, 84259698Sdim const MCFixupKind FixupKind, // from 85259698Sdim // Fixup.getKind() 86259698Sdim const bool IsPCRel) { 87259698Sdim const MCSymbolRefExpr::VariantKind Modifier = 88259698Sdim Target.isAbsolute() ? MCSymbolRefExpr::VK_None 89259698Sdim : Target.getSymA()->getKind(); 90259698Sdim // determine the type of the relocation 91259698Sdim unsigned Type = MachO::GENERIC_RELOC_VANILLA; 92259698Sdim if (IsPCRel) { // relative to PC 93259698Sdim switch ((unsigned)FixupKind) { 94259698Sdim default: 95259698Sdim report_fatal_error("Unimplemented fixup kind (relative)"); 96259698Sdim case PPC::fixup_ppc_br24: 97259698Sdim Type = MachO::PPC_RELOC_BR24; // R_PPC_REL24 98259698Sdim break; 99259698Sdim case PPC::fixup_ppc_brcond14: 100259698Sdim Type = MachO::PPC_RELOC_BR14; 101259698Sdim break; 102259698Sdim case PPC::fixup_ppc_half16: 103259698Sdim switch (Modifier) { 104259698Sdim default: 105259698Sdim llvm_unreachable("Unsupported modifier for half16 fixup"); 106259698Sdim case MCSymbolRefExpr::VK_PPC_HA: 107259698Sdim Type = MachO::PPC_RELOC_HA16; 108259698Sdim break; 109259698Sdim case MCSymbolRefExpr::VK_PPC_LO: 110259698Sdim Type = MachO::PPC_RELOC_LO16; 111259698Sdim break; 112259698Sdim case MCSymbolRefExpr::VK_PPC_HI: 113259698Sdim Type = MachO::PPC_RELOC_HI16; 114259698Sdim break; 115259698Sdim } 116259698Sdim break; 117259698Sdim } 118259698Sdim } else { 119259698Sdim switch ((unsigned)FixupKind) { 120259698Sdim default: 121259698Sdim report_fatal_error("Unimplemented fixup kind (absolute)!"); 122259698Sdim case PPC::fixup_ppc_half16: 123259698Sdim switch (Modifier) { 124259698Sdim default: 125259698Sdim llvm_unreachable("Unsupported modifier for half16 fixup"); 126259698Sdim case MCSymbolRefExpr::VK_PPC_HA: 127259698Sdim Type = MachO::PPC_RELOC_HA16_SECTDIFF; 128259698Sdim break; 129259698Sdim case MCSymbolRefExpr::VK_PPC_LO: 130259698Sdim Type = MachO::PPC_RELOC_LO16_SECTDIFF; 131259698Sdim break; 132259698Sdim case MCSymbolRefExpr::VK_PPC_HI: 133259698Sdim Type = MachO::PPC_RELOC_HI16_SECTDIFF; 134259698Sdim break; 135259698Sdim } 136259698Sdim break; 137259698Sdim case FK_Data_4: 138259698Sdim break; 139259698Sdim case FK_Data_2: 140259698Sdim break; 141259698Sdim } 142259698Sdim } 143259698Sdim return Type; 144259698Sdim} 145259698Sdim 146259698Sdimstatic void makeRelocationInfo(MachO::any_relocation_info &MRE, 147259698Sdim const uint32_t FixupOffset, const uint32_t Index, 148259698Sdim const unsigned IsPCRel, const unsigned Log2Size, 149259698Sdim const unsigned IsExtern, const unsigned Type) { 150259698Sdim MRE.r_word0 = FixupOffset; 151259698Sdim // The bitfield offsets that work (as determined by trial-and-error) 152259698Sdim // are different than what is documented in the mach-o manuals. 153259698Sdim // This appears to be an endianness issue; reversing the order of the 154259698Sdim // documented bitfields in <llvm/Support/MachO.h> fixes this (but 155259698Sdim // breaks x86/ARM assembly). 156259698Sdim MRE.r_word1 = ((Index << 8) | // was << 0 157259698Sdim (IsPCRel << 7) | // was << 24 158259698Sdim (Log2Size << 5) | // was << 25 159259698Sdim (IsExtern << 4) | // was << 27 160259698Sdim (Type << 0)); // was << 28 161259698Sdim} 162259698Sdim 163259698Sdimstatic void 164259698SdimmakeScatteredRelocationInfo(MachO::any_relocation_info &MRE, 165259698Sdim const uint32_t Addr, const unsigned Type, 166259698Sdim const unsigned Log2Size, const unsigned IsPCRel, 167259698Sdim const uint32_t Value2) { 168259698Sdim // For notes on bitfield positions and endianness, see: 169259698Sdim // https://developer.apple.com/library/mac/documentation/developertools/conceptual/MachORuntime/Reference/reference.html#//apple_ref/doc/uid/20001298-scattered_relocation_entry 170259698Sdim MRE.r_word0 = ((Addr << 0) | (Type << 24) | (Log2Size << 28) | 171259698Sdim (IsPCRel << 30) | MachO::R_SCATTERED); 172259698Sdim MRE.r_word1 = Value2; 173259698Sdim} 174259698Sdim 175259698Sdim/// Compute fixup offset (address). 176259698Sdimstatic uint32_t getFixupOffset(const MCAsmLayout &Layout, 177259698Sdim const MCFragment *Fragment, 178259698Sdim const MCFixup &Fixup) { 179259698Sdim uint32_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset(); 180259698Sdim // On Mach-O, ppc_fixup_half16 relocations must refer to the 181259698Sdim // start of the instruction, not the second halfword, as ELF does 182259698Sdim if (unsigned(Fixup.getKind()) == PPC::fixup_ppc_half16) 183259698Sdim FixupOffset &= ~uint32_t(3); 184259698Sdim return FixupOffset; 185259698Sdim} 186259698Sdim 187259698Sdim/// \return false if falling back to using non-scattered relocation, 188259698Sdim/// otherwise true for normal scattered relocation. 189288943Sdim/// based on X86MachObjectWriter::recordScatteredRelocation 190288943Sdim/// and ARMMachObjectWriter::recordScatteredRelocation 191288943Sdimbool PPCMachObjectWriter::recordScatteredRelocation( 192259698Sdim MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout, 193259698Sdim const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, 194259698Sdim unsigned Log2Size, uint64_t &FixedValue) { 195259698Sdim // caller already computes these, can we just pass and reuse? 196259698Sdim const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup); 197259698Sdim const MCFixupKind FK = Fixup.getKind(); 198259698Sdim const unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, FK); 199259698Sdim const unsigned Type = getRelocType(Target, FK, IsPCRel); 200259698Sdim 201259698Sdim // Is this a local or SECTDIFF relocation entry? 202259698Sdim // SECTDIFF relocation entries have symbol subtractions, 203259698Sdim // and require two entries, the first for the add-symbol value, 204259698Sdim // the second for the subtract-symbol value. 205259698Sdim 206259698Sdim // See <reloc.h>. 207259698Sdim const MCSymbol *A = &Target.getSymA()->getSymbol(); 208259698Sdim 209288943Sdim if (!A->getFragment()) 210259698Sdim report_fatal_error("symbol '" + A->getName() + 211259698Sdim "' can not be undefined in a subtraction expression"); 212259698Sdim 213288943Sdim uint32_t Value = Writer->getSymbolAddress(*A, Layout); 214288943Sdim uint64_t SecAddr = Writer->getSectionAddress(A->getFragment()->getParent()); 215259698Sdim FixedValue += SecAddr; 216259698Sdim uint32_t Value2 = 0; 217259698Sdim 218259698Sdim if (const MCSymbolRefExpr *B = Target.getSymB()) { 219288943Sdim const MCSymbol *SB = &B->getSymbol(); 220259698Sdim 221288943Sdim if (!SB->getFragment()) 222259698Sdim report_fatal_error("symbol '" + B->getSymbol().getName() + 223259698Sdim "' can not be undefined in a subtraction expression"); 224259698Sdim 225259698Sdim // FIXME: is Type correct? see include/llvm/Support/MachO.h 226288943Sdim Value2 = Writer->getSymbolAddress(B->getSymbol(), Layout); 227288943Sdim FixedValue -= Writer->getSectionAddress(SB->getFragment()->getParent()); 228259698Sdim } 229259698Sdim // FIXME: does FixedValue get used?? 230259698Sdim 231259698Sdim // Relocations are written out in reverse order, so the PAIR comes first. 232259698Sdim if (Type == MachO::PPC_RELOC_SECTDIFF || 233259698Sdim Type == MachO::PPC_RELOC_HI16_SECTDIFF || 234259698Sdim Type == MachO::PPC_RELOC_LO16_SECTDIFF || 235259698Sdim Type == MachO::PPC_RELOC_HA16_SECTDIFF || 236259698Sdim Type == MachO::PPC_RELOC_LO14_SECTDIFF || 237259698Sdim Type == MachO::PPC_RELOC_LOCAL_SECTDIFF) { 238259698Sdim // X86 had this piece, but ARM does not 239259698Sdim // If the offset is too large to fit in a scattered relocation, 240259698Sdim // we're hosed. It's an unfortunate limitation of the MachO format. 241259698Sdim if (FixupOffset > 0xffffff) { 242259698Sdim char Buffer[32]; 243259698Sdim format("0x%x", FixupOffset).print(Buffer, sizeof(Buffer)); 244296417Sdim Asm.getContext().reportError(Fixup.getLoc(), 245259698Sdim Twine("Section too large, can't encode " 246259698Sdim "r_address (") + 247259698Sdim Buffer + ") into 24 bits of scattered " 248259698Sdim "relocation entry."); 249296417Sdim return false; 250259698Sdim } 251259698Sdim 252259698Sdim // Is this supposed to follow MCTarget/PPCAsmBackend.cpp:adjustFixupValue()? 253288943Sdim // see PPCMCExpr::evaluateAsRelocatableImpl() 254259698Sdim uint32_t other_half = 0; 255259698Sdim switch (Type) { 256259698Sdim case MachO::PPC_RELOC_LO16_SECTDIFF: 257259698Sdim other_half = (FixedValue >> 16) & 0xffff; 258259698Sdim // applyFixupOffset longer extracts the high part because it now assumes 259259698Sdim // this was already done. 260259698Sdim // It looks like this is not true for the FixedValue needed with Mach-O 261259698Sdim // relocs. 262259698Sdim // So we need to adjust FixedValue again here. 263259698Sdim FixedValue &= 0xffff; 264259698Sdim break; 265259698Sdim case MachO::PPC_RELOC_HA16_SECTDIFF: 266259698Sdim other_half = FixedValue & 0xffff; 267259698Sdim FixedValue = 268259698Sdim ((FixedValue >> 16) + ((FixedValue & 0x8000) ? 1 : 0)) & 0xffff; 269259698Sdim break; 270259698Sdim case MachO::PPC_RELOC_HI16_SECTDIFF: 271259698Sdim other_half = FixedValue & 0xffff; 272259698Sdim FixedValue = (FixedValue >> 16) & 0xffff; 273259698Sdim break; 274259698Sdim default: 275259698Sdim llvm_unreachable("Invalid PPC scattered relocation type."); 276259698Sdim break; 277259698Sdim } 278259698Sdim 279259698Sdim MachO::any_relocation_info MRE; 280259698Sdim makeScatteredRelocationInfo(MRE, other_half, MachO::GENERIC_RELOC_PAIR, 281259698Sdim Log2Size, IsPCRel, Value2); 282288943Sdim Writer->addRelocation(nullptr, Fragment->getParent(), MRE); 283259698Sdim } else { 284259698Sdim // If the offset is more than 24-bits, it won't fit in a scattered 285259698Sdim // relocation offset field, so we fall back to using a non-scattered 286259698Sdim // relocation. This is a bit risky, as if the offset reaches out of 287259698Sdim // the block and the linker is doing scattered loading on this 288259698Sdim // symbol, things can go badly. 289259698Sdim // 290259698Sdim // Required for 'as' compatibility. 291259698Sdim if (FixupOffset > 0xffffff) 292259698Sdim return false; 293259698Sdim } 294259698Sdim MachO::any_relocation_info MRE; 295259698Sdim makeScatteredRelocationInfo(MRE, FixupOffset, Type, Log2Size, IsPCRel, Value); 296288943Sdim Writer->addRelocation(nullptr, Fragment->getParent(), MRE); 297259698Sdim return true; 298259698Sdim} 299259698Sdim 300259698Sdim// see PPCELFObjectWriter for a general outline of cases 301259698Sdimvoid PPCMachObjectWriter::RecordPPCRelocation( 302259698Sdim MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout, 303259698Sdim const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, 304259698Sdim uint64_t &FixedValue) { 305259698Sdim const MCFixupKind FK = Fixup.getKind(); // unsigned 306259698Sdim const unsigned Log2Size = getFixupKindLog2Size(FK); 307259698Sdim const bool IsPCRel = Writer->isFixupKindPCRel(Asm, FK); 308259698Sdim const unsigned RelocType = getRelocType(Target, FK, IsPCRel); 309259698Sdim 310259698Sdim // If this is a difference or a defined symbol plus an offset, then we need a 311259698Sdim // scattered relocation entry. Differences always require scattered 312259698Sdim // relocations. 313259698Sdim if (Target.getSymB() && 314259698Sdim // Q: are branch targets ever scattered? 315259698Sdim RelocType != MachO::PPC_RELOC_BR24 && 316259698Sdim RelocType != MachO::PPC_RELOC_BR14) { 317288943Sdim recordScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup, Target, 318259698Sdim Log2Size, FixedValue); 319259698Sdim return; 320259698Sdim } 321259698Sdim 322259698Sdim // this doesn't seem right for RIT_PPC_BR24 323259698Sdim // Get the symbol data, if any. 324288943Sdim const MCSymbol *A = nullptr; 325259698Sdim if (Target.getSymA()) 326288943Sdim A = &Target.getSymA()->getSymbol(); 327259698Sdim 328259698Sdim // See <reloc.h>. 329259698Sdim const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup); 330259698Sdim unsigned Index = 0; 331259698Sdim unsigned Type = RelocType; 332259698Sdim 333288943Sdim const MCSymbol *RelSymbol = nullptr; 334259698Sdim if (Target.isAbsolute()) { // constant 335259698Sdim // SymbolNum of 0 indicates the absolute section. 336259698Sdim // 337259698Sdim // FIXME: Currently, these are never generated (see code below). I cannot 338259698Sdim // find a case where they are actually emitted. 339259698Sdim report_fatal_error("FIXME: relocations to absolute targets " 340259698Sdim "not yet implemented"); 341259698Sdim // the above line stolen from ARM, not sure 342259698Sdim } else { 343259698Sdim // Resolve constant variables. 344288943Sdim if (A->isVariable()) { 345259698Sdim int64_t Res; 346288943Sdim if (A->getVariableValue()->evaluateAsAbsolute( 347259698Sdim Res, Layout, Writer->getSectionAddressMap())) { 348259698Sdim FixedValue = Res; 349259698Sdim return; 350259698Sdim } 351259698Sdim } 352259698Sdim 353259698Sdim // Check whether we need an external or internal relocation. 354288943Sdim if (Writer->doesSymbolRequireExternRelocation(*A)) { 355288943Sdim RelSymbol = A; 356259698Sdim // For external relocations, make sure to offset the fixup value to 357259698Sdim // compensate for the addend of the symbol address, if it was 358259698Sdim // undefined. This occurs with weak definitions, for example. 359288943Sdim if (!A->isUndefined()) 360288943Sdim FixedValue -= Layout.getSymbolOffset(*A); 361259698Sdim } else { 362259698Sdim // The index is the section ordinal (1-based). 363288943Sdim const MCSection &Sec = A->getSection(); 364288943Sdim Index = Sec.getOrdinal() + 1; 365288943Sdim FixedValue += Writer->getSectionAddress(&Sec); 366259698Sdim } 367259698Sdim if (IsPCRel) 368259698Sdim FixedValue -= Writer->getSectionAddress(Fragment->getParent()); 369259698Sdim } 370259698Sdim 371259698Sdim // struct relocation_info (8 bytes) 372259698Sdim MachO::any_relocation_info MRE; 373288943Sdim makeRelocationInfo(MRE, FixupOffset, Index, IsPCRel, Log2Size, false, Type); 374288943Sdim Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE); 375259698Sdim} 376259698Sdim 377288943SdimMCObjectWriter *llvm::createPPCMachObjectWriter(raw_pwrite_stream &OS, 378288943Sdim bool Is64Bit, uint32_t CPUType, 379259698Sdim uint32_t CPUSubtype) { 380259698Sdim return createMachObjectWriter( 381259698Sdim new PPCMachObjectWriter(Is64Bit, CPUType, CPUSubtype), OS, 382259698Sdim /*IsLittleEndian=*/false); 383259698Sdim} 384