1 //===-- ARMMachObjectWriter.cpp - ARM Mach Object 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/ARMBaseInfo.h" 11 #include "MCTargetDesc/ARMFixupKinds.h" 12 #include "llvm/ADT/Twine.h" 13 #include "llvm/MC/MCAssembler.h" 14 #include "llvm/MC/MCAsmLayout.h" 15 #include "llvm/MC/MCMachObjectWriter.h" 16 #include "llvm/MC/MCContext.h" 17 #include "llvm/MC/MCExpr.h" 18 #include "llvm/MC/MCFixup.h" 19 #include "llvm/MC/MCFixupKindInfo.h" 20 #include "llvm/MC/MCMachOSymbolFlags.h" 21 #include "llvm/MC/MCValue.h" 22 #include "llvm/Object/MachOFormat.h" 23 #include "llvm/Support/ErrorHandling.h" 24 using namespace llvm; 25 using namespace llvm::object; 26 27 namespace { 28 class ARMMachObjectWriter : public MCMachObjectTargetWriter { 29 void RecordARMScatteredRelocation(MachObjectWriter *Writer, 30 const MCAssembler &Asm, 31 const MCAsmLayout &Layout, 32 const MCFragment *Fragment, 33 const MCFixup &Fixup, 34 MCValue Target, 35 unsigned Log2Size, 36 uint64_t &FixedValue); 37 void RecordARMMovwMovtRelocation(MachObjectWriter *Writer, 38 const MCAssembler &Asm, 39 const MCAsmLayout &Layout, 40 const MCFragment *Fragment, 41 const MCFixup &Fixup, MCValue Target, 42 uint64_t &FixedValue); 43 44 public: 45 ARMMachObjectWriter(bool Is64Bit, uint32_t CPUType, 46 uint32_t CPUSubtype) 47 : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype, 48 /*UseAggressiveSymbolFolding=*/true) {} 49 50 void RecordRelocation(MachObjectWriter *Writer, 51 const MCAssembler &Asm, const MCAsmLayout &Layout, 52 const MCFragment *Fragment, const MCFixup &Fixup, 53 MCValue Target, uint64_t &FixedValue); 54 }; 55 } 56 57 static bool getARMFixupKindMachOInfo(unsigned Kind, unsigned &RelocType, 58 unsigned &Log2Size) { 59 RelocType = unsigned(macho::RIT_Vanilla); 60 Log2Size = ~0U; 61 62 switch (Kind) { 63 default: 64 return false; 65 66 case FK_Data_1: 67 Log2Size = llvm::Log2_32(1); 68 return true; 69 case FK_Data_2: 70 Log2Size = llvm::Log2_32(2); 71 return true; 72 case FK_Data_4: 73 Log2Size = llvm::Log2_32(4); 74 return true; 75 case FK_Data_8: 76 Log2Size = llvm::Log2_32(8); 77 return true; 78 79 // Handle 24-bit branch kinds. 80 case ARM::fixup_arm_ldst_pcrel_12: 81 case ARM::fixup_arm_pcrel_10: 82 case ARM::fixup_arm_adr_pcrel_12: 83 case ARM::fixup_arm_condbranch: 84 case ARM::fixup_arm_uncondbranch: 85 RelocType = unsigned(macho::RIT_ARM_Branch24Bit); 86 // Report as 'long', even though that is not quite accurate. 87 Log2Size = llvm::Log2_32(4); 88 return true; 89 90 // Handle Thumb branches. 91 case ARM::fixup_arm_thumb_br: 92 RelocType = unsigned(macho::RIT_ARM_ThumbBranch22Bit); 93 Log2Size = llvm::Log2_32(2); 94 return true; 95 96 case ARM::fixup_t2_uncondbranch: 97 case ARM::fixup_arm_thumb_bl: 98 case ARM::fixup_arm_thumb_blx: 99 RelocType = unsigned(macho::RIT_ARM_ThumbBranch22Bit); 100 Log2Size = llvm::Log2_32(4); 101 return true; 102 103 case ARM::fixup_arm_movt_hi16: 104 case ARM::fixup_arm_movt_hi16_pcrel: 105 case ARM::fixup_t2_movt_hi16: 106 case ARM::fixup_t2_movt_hi16_pcrel: 107 RelocType = unsigned(macho::RIT_ARM_HalfDifference); 108 // Report as 'long', even though that is not quite accurate. 109 Log2Size = llvm::Log2_32(4); 110 return true; 111 112 case ARM::fixup_arm_movw_lo16: 113 case ARM::fixup_arm_movw_lo16_pcrel: 114 case ARM::fixup_t2_movw_lo16: 115 case ARM::fixup_t2_movw_lo16_pcrel: 116 RelocType = unsigned(macho::RIT_ARM_Half); 117 // Report as 'long', even though that is not quite accurate. 118 Log2Size = llvm::Log2_32(4); 119 return true; 120 } 121 } 122 123 void ARMMachObjectWriter:: 124 RecordARMMovwMovtRelocation(MachObjectWriter *Writer, 125 const MCAssembler &Asm, 126 const MCAsmLayout &Layout, 127 const MCFragment *Fragment, 128 const MCFixup &Fixup, 129 MCValue Target, 130 uint64_t &FixedValue) { 131 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset(); 132 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind()); 133 unsigned Type = macho::RIT_ARM_Half; 134 135 // See <reloc.h>. 136 const MCSymbol *A = &Target.getSymA()->getSymbol(); 137 MCSymbolData *A_SD = &Asm.getSymbolData(*A); 138 139 if (!A_SD->getFragment()) 140 Asm.getContext().FatalError(Fixup.getLoc(), 141 "symbol '" + A->getName() + 142 "' can not be undefined in a subtraction expression"); 143 144 uint32_t Value = Writer->getSymbolAddress(A_SD, Layout); 145 uint32_t Value2 = 0; 146 uint64_t SecAddr = 147 Writer->getSectionAddress(A_SD->getFragment()->getParent()); 148 FixedValue += SecAddr; 149 150 if (const MCSymbolRefExpr *B = Target.getSymB()) { 151 MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol()); 152 153 if (!B_SD->getFragment()) 154 Asm.getContext().FatalError(Fixup.getLoc(), 155 "symbol '" + B->getSymbol().getName() + 156 "' can not be undefined in a subtraction expression"); 157 158 // Select the appropriate difference relocation type. 159 Type = macho::RIT_ARM_HalfDifference; 160 Value2 = Writer->getSymbolAddress(B_SD, Layout); 161 FixedValue -= Writer->getSectionAddress(B_SD->getFragment()->getParent()); 162 } 163 164 // Relocations are written out in reverse order, so the PAIR comes first. 165 // ARM_RELOC_HALF and ARM_RELOC_HALF_SECTDIFF abuse the r_length field: 166 // 167 // For these two r_type relocations they always have a pair following them and 168 // the r_length bits are used differently. The encoding of the r_length is as 169 // follows: 170 // low bit of r_length: 171 // 0 - :lower16: for movw instructions 172 // 1 - :upper16: for movt instructions 173 // high bit of r_length: 174 // 0 - arm instructions 175 // 1 - thumb instructions 176 // the other half of the relocated expression is in the following pair 177 // relocation entry in the the low 16 bits of r_address field. 178 unsigned ThumbBit = 0; 179 unsigned MovtBit = 0; 180 switch ((unsigned)Fixup.getKind()) { 181 default: break; 182 case ARM::fixup_arm_movt_hi16: 183 case ARM::fixup_arm_movt_hi16_pcrel: 184 MovtBit = 1; 185 // The thumb bit shouldn't be set in the 'other-half' bit of the 186 // relocation, but it will be set in FixedValue if the base symbol 187 // is a thumb function. Clear it out here. 188 if (A_SD->getFlags() & SF_ThumbFunc) 189 FixedValue &= 0xfffffffe; 190 break; 191 case ARM::fixup_t2_movt_hi16: 192 case ARM::fixup_t2_movt_hi16_pcrel: 193 if (A_SD->getFlags() & SF_ThumbFunc) 194 FixedValue &= 0xfffffffe; 195 MovtBit = 1; 196 // Fallthrough 197 case ARM::fixup_t2_movw_lo16: 198 case ARM::fixup_t2_movw_lo16_pcrel: 199 ThumbBit = 1; 200 break; 201 } 202 203 if (Type == macho::RIT_ARM_HalfDifference) { 204 uint32_t OtherHalf = MovtBit 205 ? (FixedValue & 0xffff) : ((FixedValue & 0xffff0000) >> 16); 206 207 macho::RelocationEntry MRE; 208 MRE.Word0 = ((OtherHalf << 0) | 209 (macho::RIT_Pair << 24) | 210 (MovtBit << 28) | 211 (ThumbBit << 29) | 212 (IsPCRel << 30) | 213 macho::RF_Scattered); 214 MRE.Word1 = Value2; 215 Writer->addRelocation(Fragment->getParent(), MRE); 216 } 217 218 macho::RelocationEntry MRE; 219 MRE.Word0 = ((FixupOffset << 0) | 220 (Type << 24) | 221 (MovtBit << 28) | 222 (ThumbBit << 29) | 223 (IsPCRel << 30) | 224 macho::RF_Scattered); 225 MRE.Word1 = Value; 226 Writer->addRelocation(Fragment->getParent(), MRE); 227 } 228 229 void ARMMachObjectWriter::RecordARMScatteredRelocation(MachObjectWriter *Writer, 230 const MCAssembler &Asm, 231 const MCAsmLayout &Layout, 232 const MCFragment *Fragment, 233 const MCFixup &Fixup, 234 MCValue Target, 235 unsigned Log2Size, 236 uint64_t &FixedValue) { 237 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset(); 238 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind()); 239 unsigned Type = macho::RIT_Vanilla; 240 241 // See <reloc.h>. 242 const MCSymbol *A = &Target.getSymA()->getSymbol(); 243 MCSymbolData *A_SD = &Asm.getSymbolData(*A); 244 245 if (!A_SD->getFragment()) 246 Asm.getContext().FatalError(Fixup.getLoc(), 247 "symbol '" + A->getName() + 248 "' can not be undefined in a subtraction expression"); 249 250 uint32_t Value = Writer->getSymbolAddress(A_SD, Layout); 251 uint64_t SecAddr = Writer->getSectionAddress(A_SD->getFragment()->getParent()); 252 FixedValue += SecAddr; 253 uint32_t Value2 = 0; 254 255 if (const MCSymbolRefExpr *B = Target.getSymB()) { 256 MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol()); 257 258 if (!B_SD->getFragment()) 259 Asm.getContext().FatalError(Fixup.getLoc(), 260 "symbol '" + B->getSymbol().getName() + 261 "' can not be undefined in a subtraction expression"); 262 263 // Select the appropriate difference relocation type. 264 Type = macho::RIT_Difference; 265 Value2 = Writer->getSymbolAddress(B_SD, Layout); 266 FixedValue -= Writer->getSectionAddress(B_SD->getFragment()->getParent()); 267 } 268 269 // Relocations are written out in reverse order, so the PAIR comes first. 270 if (Type == macho::RIT_Difference || 271 Type == macho::RIT_Generic_LocalDifference) { 272 macho::RelocationEntry MRE; 273 MRE.Word0 = ((0 << 0) | 274 (macho::RIT_Pair << 24) | 275 (Log2Size << 28) | 276 (IsPCRel << 30) | 277 macho::RF_Scattered); 278 MRE.Word1 = Value2; 279 Writer->addRelocation(Fragment->getParent(), MRE); 280 } 281 282 macho::RelocationEntry MRE; 283 MRE.Word0 = ((FixupOffset << 0) | 284 (Type << 24) | 285 (Log2Size << 28) | 286 (IsPCRel << 30) | 287 macho::RF_Scattered); 288 MRE.Word1 = Value; 289 Writer->addRelocation(Fragment->getParent(), MRE); 290 } 291 292 void ARMMachObjectWriter::RecordRelocation(MachObjectWriter *Writer, 293 const MCAssembler &Asm, 294 const MCAsmLayout &Layout, 295 const MCFragment *Fragment, 296 const MCFixup &Fixup, 297 MCValue Target, 298 uint64_t &FixedValue) { 299 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind()); 300 unsigned Log2Size; 301 unsigned RelocType = macho::RIT_Vanilla; 302 if (!getARMFixupKindMachOInfo(Fixup.getKind(), RelocType, Log2Size)) 303 // If we failed to get fixup kind info, it's because there's no legal 304 // relocation type for the fixup kind. This happens when it's a fixup that's 305 // expected to always be resolvable at assembly time and not have any 306 // relocations needed. 307 Asm.getContext().FatalError(Fixup.getLoc(), 308 "unsupported relocation on symbol"); 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 if (RelocType == macho::RIT_ARM_Half || 315 RelocType == macho::RIT_ARM_HalfDifference) 316 return RecordARMMovwMovtRelocation(Writer, Asm, Layout, Fragment, Fixup, 317 Target, FixedValue); 318 return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup, 319 Target, Log2Size, FixedValue); 320 } 321 322 // Get the symbol data, if any. 323 MCSymbolData *SD = 0; 324 if (Target.getSymA()) 325 SD = &Asm.getSymbolData(Target.getSymA()->getSymbol()); 326 327 // FIXME: For other platforms, we need to use scattered relocations for 328 // internal relocations with offsets. If this is an internal relocation with 329 // an offset, it also needs a scattered relocation entry. 330 // 331 // Is this right for ARM? 332 uint32_t Offset = Target.getConstant(); 333 if (IsPCRel && RelocType == macho::RIT_Vanilla) 334 Offset += 1 << Log2Size; 335 if (Offset && SD && !Writer->doesSymbolRequireExternRelocation(SD)) 336 return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup, 337 Target, Log2Size, FixedValue); 338 339 // See <reloc.h>. 340 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset(); 341 unsigned Index = 0; 342 unsigned IsExtern = 0; 343 unsigned Type = 0; 344 345 if (Target.isAbsolute()) { // constant 346 // FIXME! 347 report_fatal_error("FIXME: relocations to absolute targets " 348 "not yet implemented"); 349 } else { 350 // Resolve constant variables. 351 if (SD->getSymbol().isVariable()) { 352 int64_t Res; 353 if (SD->getSymbol().getVariableValue()->EvaluateAsAbsolute( 354 Res, Layout, Writer->getSectionAddressMap())) { 355 FixedValue = Res; 356 return; 357 } 358 } 359 360 // Check whether we need an external or internal relocation. 361 if (Writer->doesSymbolRequireExternRelocation(SD)) { 362 IsExtern = 1; 363 Index = SD->getIndex(); 364 365 // For external relocations, make sure to offset the fixup value to 366 // compensate for the addend of the symbol address, if it was 367 // undefined. This occurs with weak definitions, for example. 368 if (!SD->Symbol->isUndefined()) 369 FixedValue -= Layout.getSymbolOffset(SD); 370 } else { 371 // The index is the section ordinal (1-based). 372 const MCSectionData &SymSD = Asm.getSectionData( 373 SD->getSymbol().getSection()); 374 Index = SymSD.getOrdinal() + 1; 375 FixedValue += Writer->getSectionAddress(&SymSD); 376 } 377 if (IsPCRel) 378 FixedValue -= Writer->getSectionAddress(Fragment->getParent()); 379 380 // The type is determined by the fixup kind. 381 Type = RelocType; 382 } 383 384 // struct relocation_info (8 bytes) 385 macho::RelocationEntry MRE; 386 MRE.Word0 = FixupOffset; 387 MRE.Word1 = ((Index << 0) | 388 (IsPCRel << 24) | 389 (Log2Size << 25) | 390 (IsExtern << 27) | 391 (Type << 28)); 392 Writer->addRelocation(Fragment->getParent(), MRE); 393 } 394 395 MCObjectWriter *llvm::createARMMachObjectWriter(raw_ostream &OS, 396 bool Is64Bit, 397 uint32_t CPUType, 398 uint32_t CPUSubtype) { 399 return createMachObjectWriter(new ARMMachObjectWriter(Is64Bit, 400 CPUType, 401 CPUSubtype), 402 OS, /*IsLittleEndian=*/true); 403 } 404