1dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines//===-- AArch64MachObjectWriter.cpp - ARM Mach Object Writer --------------===// 236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines// 336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines// The LLVM Compiler Infrastructure 436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines// 536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines// This file is distributed under the University of Illinois Open Source 636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines// License. See LICENSE.TXT for details. 736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines// 836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines//===----------------------------------------------------------------------===// 936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 10dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines#include "MCTargetDesc/AArch64FixupKinds.h" 11dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines#include "MCTargetDesc/AArch64MCTargetDesc.h" 1236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines#include "llvm/MC/MCAssembler.h" 1336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines#include "llvm/MC/MCAsmLayout.h" 1436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines#include "llvm/MC/MCContext.h" 1536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines#include "llvm/MC/MCExpr.h" 1636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines#include "llvm/MC/MCFixup.h" 1736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines#include "llvm/MC/MCMachObjectWriter.h" 1836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines#include "llvm/MC/MCSectionMachO.h" 1936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines#include "llvm/MC/MCValue.h" 2036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines#include "llvm/ADT/Twine.h" 2136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines#include "llvm/Support/ErrorHandling.h" 2236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines#include "llvm/Support/MachO.h" 2336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hinesusing namespace llvm; 2436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 2536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hinesnamespace { 26dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hinesclass AArch64MachObjectWriter : public MCMachObjectTargetWriter { 27dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines bool getAArch64FixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType, 2836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines const MCSymbolRefExpr *Sym, 2936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines unsigned &Log2Size, const MCAssembler &Asm); 3036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 3136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hinespublic: 32dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines AArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype) 3336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines : MCMachObjectTargetWriter(true /* is64Bit */, CPUType, CPUSubtype, 3436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines /*UseAggressiveSymbolFolding=*/true) {} 3536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 3636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines void RecordRelocation(MachObjectWriter *Writer, const MCAssembler &Asm, 3736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines const MCAsmLayout &Layout, const MCFragment *Fragment, 3836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines const MCFixup &Fixup, MCValue Target, 39dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines uint64_t &FixedValue) override; 4036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines}; 4136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines} 4236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 43dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hinesbool AArch64MachObjectWriter::getAArch64FixupKindMachOInfo( 4436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines const MCFixup &Fixup, unsigned &RelocType, const MCSymbolRefExpr *Sym, 4536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines unsigned &Log2Size, const MCAssembler &Asm) { 4636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines RelocType = unsigned(MachO::ARM64_RELOC_UNSIGNED); 4736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Log2Size = ~0U; 4836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 4936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines switch ((unsigned)Fixup.getKind()) { 5036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines default: 5136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return false; 5236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 5336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines case FK_Data_1: 5436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Log2Size = llvm::Log2_32(1); 5536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return true; 5636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines case FK_Data_2: 5736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Log2Size = llvm::Log2_32(2); 5836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return true; 5936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines case FK_Data_4: 6036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Log2Size = llvm::Log2_32(4); 6136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (Sym->getKind() == MCSymbolRefExpr::VK_GOT) 6236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT); 6336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return true; 6436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines case FK_Data_8: 6536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Log2Size = llvm::Log2_32(8); 6636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (Sym->getKind() == MCSymbolRefExpr::VK_GOT) 6736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT); 6836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return true; 69dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines case AArch64::fixup_aarch64_add_imm12: 70dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines case AArch64::fixup_aarch64_ldst_imm12_scale1: 71dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines case AArch64::fixup_aarch64_ldst_imm12_scale2: 72dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines case AArch64::fixup_aarch64_ldst_imm12_scale4: 73dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines case AArch64::fixup_aarch64_ldst_imm12_scale8: 74dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines case AArch64::fixup_aarch64_ldst_imm12_scale16: 7536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Log2Size = llvm::Log2_32(4); 7636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines switch (Sym->getKind()) { 7736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines default: 78cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hines llvm_unreachable("Unexpected symbol reference variant kind!"); 7936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines case MCSymbolRefExpr::VK_PAGEOFF: 8036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines RelocType = unsigned(MachO::ARM64_RELOC_PAGEOFF12); 8136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return true; 8236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines case MCSymbolRefExpr::VK_GOTPAGEOFF: 8336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12); 8436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return true; 8536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines case MCSymbolRefExpr::VK_TLVPPAGEOFF: 8636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12); 8736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return true; 8836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 89dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines case AArch64::fixup_aarch64_pcrel_adrp_imm21: 9036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Log2Size = llvm::Log2_32(4); 9136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // This encompasses the relocation for the whole 21-bit value. 9236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines switch (Sym->getKind()) { 9336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines default: 9436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm.getContext().FatalError(Fixup.getLoc(), 9536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "ADR/ADRP relocations must be GOT relative"); 9636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines case MCSymbolRefExpr::VK_PAGE: 9736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines RelocType = unsigned(MachO::ARM64_RELOC_PAGE21); 9836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return true; 9936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines case MCSymbolRefExpr::VK_GOTPAGE: 10036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGE21); 10136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return true; 10236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines case MCSymbolRefExpr::VK_TLVPPAGE: 10336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGE21); 10436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return true; 10536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 10636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return true; 107dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines case AArch64::fixup_aarch64_pcrel_branch26: 108dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines case AArch64::fixup_aarch64_pcrel_call26: 10936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Log2Size = llvm::Log2_32(4); 11036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines RelocType = unsigned(MachO::ARM64_RELOC_BRANCH26); 11136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return true; 11236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 11336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines} 11436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 115dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hinesvoid AArch64MachObjectWriter::RecordRelocation( 11636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout, 11736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, 11836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines uint64_t &FixedValue) { 11936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind()); 12036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 12136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // See <reloc.h>. 12236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines uint32_t FixupOffset = Layout.getFragmentOffset(Fragment); 12336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines unsigned Log2Size = 0; 12436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines int64_t Value = 0; 12536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines unsigned Index = 0; 12636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines unsigned IsExtern = 0; 12736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines unsigned Type = 0; 12836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines unsigned Kind = Fixup.getKind(); 12936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 13036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines FixupOffset += Fixup.getOffset(); 13136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 132dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines // AArch64 pcrel relocation addends do not include the section offset. 13336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (IsPCRel) 13436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines FixedValue += FixupOffset; 13536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 13636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // ADRP fixups use relocations for the whole symbol value and only 13736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // put the addend in the instruction itself. Clear out any value the 13836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // generic code figured out from the sybmol definition. 139dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21) 14036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines FixedValue = 0; 14136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 14236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // imm19 relocations are for conditional branches, which require 14336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // assembler local symbols. If we got here, that's not what we have, 14436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // so complain loudly. 145dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines if (Kind == AArch64::fixup_aarch64_pcrel_branch19) { 14636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm.getContext().FatalError(Fixup.getLoc(), 14736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "conditional branch requires assembler-local" 14836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines " label. '" + 14936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Target.getSymA()->getSymbol().getName() + 15036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "' is external."); 15136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return; 15236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 15336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 15436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // 14-bit branch relocations should only target internal labels, and so 15536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // should never get here. 156dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines if (Kind == AArch64::fixup_aarch64_pcrel_branch14) { 15736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm.getContext().FatalError(Fixup.getLoc(), 15836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "Invalid relocation on conditional branch!"); 15936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return; 16036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 16136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 162dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size, 16336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm)) { 164dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines Asm.getContext().FatalError(Fixup.getLoc(), "unknown AArch64 fixup kind!"); 16536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return; 16636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 16736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 16836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Value = Target.getConstant(); 16936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 17036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (Target.isAbsolute()) { // constant 17136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // FIXME: Should this always be extern? 17236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // SymbolNum of 0 indicates the absolute section. 17336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Type = MachO::ARM64_RELOC_UNSIGNED; 17436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Index = 0; 17536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 17636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (IsPCRel) { 17736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines IsExtern = 1; 17836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm.getContext().FatalError(Fixup.getLoc(), 17936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "PC relative absolute relocation!"); 18036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 18136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // FIXME: x86_64 sets the type to a branch reloc here. Should we do 18236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // something similar? 18336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 18436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } else if (Target.getSymB()) { // A - B + constant 18536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines const MCSymbol *A = &Target.getSymA()->getSymbol(); 186dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines const MCSymbolData &A_SD = Asm.getSymbolData(*A); 18736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines const MCSymbolData *A_Base = Asm.getAtom(&A_SD); 18836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 18936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines const MCSymbol *B = &Target.getSymB()->getSymbol(); 190dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines const MCSymbolData &B_SD = Asm.getSymbolData(*B); 19136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines const MCSymbolData *B_Base = Asm.getAtom(&B_SD); 19236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 19336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // Check for "_foo@got - .", which comes through here as: 19436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // Ltmp0: 19536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // ... _foo@got - Ltmp0 19636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT && 19736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None && 19836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Layout.getSymbolOffset(&B_SD) == 19936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) { 20036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // SymB is the PC, so use a PC-rel pointer-to-GOT relocation. 20136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Index = A_Base->getIndex(); 20236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines IsExtern = 1; 20336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Type = MachO::ARM64_RELOC_POINTER_TO_GOT; 20436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines IsPCRel = 1; 20536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines MachO::any_relocation_info MRE; 20636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines MRE.r_word0 = FixupOffset; 20736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | 20836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines (IsExtern << 27) | (Type << 28)); 20936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Writer->addRelocation(Fragment->getParent(), MRE); 21036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return; 21136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None || 21236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None) 21336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // Otherwise, neither symbol can be modified. 21436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm.getContext().FatalError(Fixup.getLoc(), 21536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "unsupported relocation of modified symbol"); 21636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 21736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // We don't support PCrel relocations of differences. 21836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (IsPCRel) 21936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm.getContext().FatalError(Fixup.getLoc(), 22036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "unsupported pc-relative relocation of " 22136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "difference"); 22236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 223dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines // AArch64 always uses external relocations. If there is no symbol to use as 224dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines // a base address (a local symbol with no preceding non-local symbol), 22536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // error out. 22636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // 22736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // FIXME: We should probably just synthesize an external symbol and use 22836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // that. 22936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (!A_Base) 23036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm.getContext().FatalError( 23136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Fixup.getLoc(), 23236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "unsupported relocation of local symbol '" + A->getName() + 23336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "'. Must have non-local symbol earlier in section."); 23436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (!B_Base) 23536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm.getContext().FatalError( 23636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Fixup.getLoc(), 23736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "unsupported relocation of local symbol '" + B->getName() + 23836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "'. Must have non-local symbol earlier in section."); 23936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 24036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (A_Base == B_Base && A_Base) 24136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm.getContext().FatalError(Fixup.getLoc(), 24236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "unsupported relocation with identical base"); 24336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 244dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines Value += (!A_SD.getFragment() ? 0 245dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines : Writer->getSymbolAddress(&A_SD, Layout)) - 246dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines (!A_Base || !A_Base->getFragment() 24736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines ? 0 24836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines : Writer->getSymbolAddress(A_Base, Layout)); 249dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines Value -= (!B_SD.getFragment() ? 0 250dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines : Writer->getSymbolAddress(&B_SD, Layout)) - 251dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines (!B_Base || !B_Base->getFragment() 25236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines ? 0 25336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines : Writer->getSymbolAddress(B_Base, Layout)); 25436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 25536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Index = A_Base->getIndex(); 25636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines IsExtern = 1; 25736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Type = MachO::ARM64_RELOC_UNSIGNED; 25836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 25936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines MachO::any_relocation_info MRE; 26036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines MRE.r_word0 = FixupOffset; 26136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | 26236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines (IsExtern << 27) | (Type << 28)); 26336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Writer->addRelocation(Fragment->getParent(), MRE); 26436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 26536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Index = B_Base->getIndex(); 26636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines IsExtern = 1; 26736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Type = MachO::ARM64_RELOC_SUBTRACTOR; 26836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } else { // A + constant 26936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines const MCSymbol *Symbol = &Target.getSymA()->getSymbol(); 270dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines const MCSymbolData &SD = Asm.getSymbolData(*Symbol); 27136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines const MCSymbolData *Base = Asm.getAtom(&SD); 27236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines const MCSectionMachO &Section = static_cast<const MCSectionMachO &>( 27336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Fragment->getParent()->getSection()); 27436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 27536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // If the symbol is a variable and we weren't able to get a Base for it 27636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // (i.e., it's not in the symbol table associated with a section) resolve 27736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // the relocation based its expansion instead. 27836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (Symbol->isVariable() && !Base) { 27936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // If the evaluation is an absolute value, just use that directly 28036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // to keep things easy. 28136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines int64_t Res; 28236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute( 28336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Res, Layout, Writer->getSectionAddressMap())) { 28436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines FixedValue = Res; 28536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return; 28636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 28736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 28836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // FIXME: Will the Target we already have ever have any data in it 28936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // we need to preserve and merge with the new Target? How about 29036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // the FixedValue? 29136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (!Symbol->getVariableValue()->EvaluateAsRelocatable(Target, &Layout)) 29236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm.getContext().FatalError(Fixup.getLoc(), 29336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "unable to resolve variable '" + 29436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Symbol->getName() + "'"); 29536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return RecordRelocation(Writer, Asm, Layout, Fragment, Fixup, Target, 29636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines FixedValue); 29736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 29836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 29936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // Relocations inside debug sections always use local relocations when 30036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // possible. This seems to be done because the debugger doesn't fully 30136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // understand relocation entries and expects to find values that 30236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // have already been fixed up. 30336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (Symbol->isInSection()) { 30436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (Section.hasAttribute(MachO::S_ATTR_DEBUG)) 305dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines Base = nullptr; 30636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 30736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 308dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines // AArch64 uses external relocations as much as possible. For debug 309dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines // sections, and for pointer-sized relocations (.quad), we allow section 310dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines // relocations. It's code sections that run into trouble. 31136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (Base) { 31236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Index = Base->getIndex(); 31336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines IsExtern = 1; 31436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 31536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // Add the local offset, if needed. 31636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (Base != &SD) 31736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Value += Layout.getSymbolOffset(&SD) - Layout.getSymbolOffset(Base); 31836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } else if (Symbol->isInSection()) { 31936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // Pointer-sized relocations can use a local relocation. Otherwise, 32036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // we have to be in a debug info section. 32136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (!Section.hasAttribute(MachO::S_ATTR_DEBUG) && Log2Size != 3) 32236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm.getContext().FatalError( 32336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Fixup.getLoc(), 32436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "unsupported relocation of local symbol '" + Symbol->getName() + 32536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "'. Must have non-local symbol earlier in section."); 32636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // Adjust the relocation to be section-relative. 32736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // The index is the section ordinal (1-based). 32836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines const MCSectionData &SymSD = 32936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm.getSectionData(SD.getSymbol().getSection()); 33036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Index = SymSD.getOrdinal() + 1; 33136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines IsExtern = 0; 33236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Value += Writer->getSymbolAddress(&SD, Layout); 33336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 33436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (IsPCRel) 33536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Value -= Writer->getFragmentAddress(Fragment, Layout) + 33636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Fixup.getOffset() + (1ULL << Log2Size); 33736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } else { 33836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // Resolve constant variables. 33936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (SD.getSymbol().isVariable()) { 34036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines int64_t Res; 34136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute( 34236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Res, Layout, Writer->getSectionAddressMap())) { 34336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines FixedValue = Res; 34436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines return; 34536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 34636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 34736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Asm.getContext().FatalError(Fixup.getLoc(), 34836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines "unsupported relocation of variable '" + 34936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Symbol->getName() + "'"); 35036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 35136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 35236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 35336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // If the relocation kind is Branch26, Page21, or Pageoff12, any addend 35436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // is represented via an Addend relocation, not encoded directly into 35536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // the instruction. 35636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines if ((Type == MachO::ARM64_RELOC_BRANCH26 || 35736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Type == MachO::ARM64_RELOC_PAGE21 || 35836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Type == MachO::ARM64_RELOC_PAGEOFF12) && 35936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Value) { 36036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines assert((Value & 0xff000000) == 0 && "Added relocation out of range!"); 36136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 36236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines MachO::any_relocation_info MRE; 36336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines MRE.r_word0 = FixupOffset; 36436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | 36536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines (IsExtern << 27) | (Type << 28)); 36636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Writer->addRelocation(Fragment->getParent(), MRE); 36736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 36836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // Now set up the Addend relocation. 36936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Type = MachO::ARM64_RELOC_ADDEND; 37036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Index = Value; 37136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines IsPCRel = 0; 37236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Log2Size = 2; 37336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines IsExtern = 0; 37436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 37536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // Put zero into the instruction itself. The addend is in the relocation. 37636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Value = 0; 37736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines } 37836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 37936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // If there's any addend left to handle, encode it in the instruction. 38036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines FixedValue = Value; 38136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 38236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines // struct relocation_info (8 bytes) 38336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines MachO::any_relocation_info MRE; 38436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines MRE.r_word0 = FixupOffset; 38536b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | 38636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines (IsExtern << 27) | (Type << 28)); 38736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines Writer->addRelocation(Fragment->getParent(), MRE); 38836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines} 38936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 390dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen HinesMCObjectWriter *llvm::createAArch64MachObjectWriter(raw_ostream &OS, 39136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines uint32_t CPUType, 39236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines uint32_t CPUSubtype) { 393dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines return createMachObjectWriter( 394dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines new AArch64MachObjectWriter(CPUType, CPUSubtype), OS, 395dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines /*IsLittleEndian=*/true); 39636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines} 397