ARMMachObjectWriter.cpp revision 577b09155f9a6fa38e5a7918da9701e120b3642f
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/MCExpr.h" 17#include "llvm/MC/MCFixup.h" 18#include "llvm/MC/MCFixupKindInfo.h" 19#include "llvm/MC/MCMachOSymbolFlags.h" 20#include "llvm/MC/MCValue.h" 21#include "llvm/Object/MachOFormat.h" 22#include "llvm/Support/ErrorHandling.h" 23using namespace llvm; 24using namespace llvm::object; 25 26namespace { 27class ARMMachObjectWriter : public MCMachObjectTargetWriter { 28 void RecordARMScatteredRelocation(MachObjectWriter *Writer, 29 const MCAssembler &Asm, 30 const MCAsmLayout &Layout, 31 const MCFragment *Fragment, 32 const MCFixup &Fixup, 33 MCValue Target, 34 unsigned Log2Size, 35 uint64_t &FixedValue); 36 void RecordARMMovwMovtRelocation(MachObjectWriter *Writer, 37 const MCAssembler &Asm, 38 const MCAsmLayout &Layout, 39 const MCFragment *Fragment, 40 const MCFixup &Fixup, MCValue Target, 41 uint64_t &FixedValue); 42 43public: 44 ARMMachObjectWriter(bool Is64Bit, uint32_t CPUType, 45 uint32_t CPUSubtype) 46 : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype, 47 /*UseAggressiveSymbolFolding=*/true) {} 48 49 void RecordRelocation(MachObjectWriter *Writer, 50 const MCAssembler &Asm, const MCAsmLayout &Layout, 51 const MCFragment *Fragment, const MCFixup &Fixup, 52 MCValue Target, uint64_t &FixedValue); 53}; 54} 55 56static bool getARMFixupKindMachOInfo(unsigned Kind, unsigned &RelocType, 57 unsigned &Log2Size) { 58 RelocType = unsigned(macho::RIT_Vanilla); 59 Log2Size = ~0U; 60 61 switch (Kind) { 62 default: 63 return false; 64 65 case ARM::fixup_t2_condbranch: 66 assert(0); 67 return false; 68 case FK_Data_1: 69 Log2Size = llvm::Log2_32(1); 70 return true; 71 case FK_Data_2: 72 Log2Size = llvm::Log2_32(2); 73 return true; 74 case FK_Data_4: 75 Log2Size = llvm::Log2_32(4); 76 return true; 77 case FK_Data_8: 78 Log2Size = llvm::Log2_32(8); 79 return true; 80 81 // Handle 24-bit branch kinds. 82 case ARM::fixup_arm_ldst_pcrel_12: 83 case ARM::fixup_arm_pcrel_10: 84 case ARM::fixup_arm_adr_pcrel_12: 85 case ARM::fixup_arm_condbranch: 86 case ARM::fixup_arm_uncondbranch: 87 RelocType = unsigned(macho::RIT_ARM_Branch24Bit); 88 // Report as 'long', even though that is not quite accurate. 89 Log2Size = llvm::Log2_32(4); 90 return true; 91 92 // Handle Thumb branches. 93 case ARM::fixup_arm_thumb_br: 94 RelocType = unsigned(macho::RIT_ARM_ThumbBranch22Bit); 95 Log2Size = llvm::Log2_32(2); 96 return true; 97 98 case ARM::fixup_t2_uncondbranch: 99 case ARM::fixup_arm_thumb_bl: 100 case ARM::fixup_arm_thumb_blx: 101 RelocType = unsigned(macho::RIT_ARM_ThumbBranch22Bit); 102 Log2Size = llvm::Log2_32(4); 103 return true; 104 105 case ARM::fixup_arm_movt_hi16: 106 case ARM::fixup_arm_movt_hi16_pcrel: 107 case ARM::fixup_t2_movt_hi16: 108 case ARM::fixup_t2_movt_hi16_pcrel: 109 RelocType = unsigned(macho::RIT_ARM_HalfDifference); 110 // Report as 'long', even though that is not quite accurate. 111 Log2Size = llvm::Log2_32(4); 112 return true; 113 114 case ARM::fixup_arm_movw_lo16: 115 case ARM::fixup_arm_movw_lo16_pcrel: 116 case ARM::fixup_t2_movw_lo16: 117 case ARM::fixup_t2_movw_lo16_pcrel: 118 RelocType = unsigned(macho::RIT_ARM_Half); 119 // Report as 'long', even though that is not quite accurate. 120 Log2Size = llvm::Log2_32(4); 121 return true; 122 } 123} 124 125void ARMMachObjectWriter:: 126RecordARMMovwMovtRelocation(MachObjectWriter *Writer, 127 const MCAssembler &Asm, 128 const MCAsmLayout &Layout, 129 const MCFragment *Fragment, 130 const MCFixup &Fixup, 131 MCValue Target, 132 uint64_t &FixedValue) { 133 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset(); 134 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind()); 135 unsigned Type = macho::RIT_ARM_Half; 136 137 // See <reloc.h>. 138 const MCSymbol *A = &Target.getSymA()->getSymbol(); 139 MCSymbolData *A_SD = &Asm.getSymbolData(*A); 140 141 if (!A_SD->getFragment()) 142 report_fatal_error("symbol '" + A->getName() + 143 "' can not be undefined in a subtraction expression"); 144 145 uint32_t Value = Writer->getSymbolAddress(A_SD, Layout); 146 uint32_t Value2 = 0; 147 uint64_t SecAddr = 148 Writer->getSectionAddress(A_SD->getFragment()->getParent()); 149 FixedValue += SecAddr; 150 151 if (const MCSymbolRefExpr *B = Target.getSymB()) { 152 MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol()); 153 154 if (!B_SD->getFragment()) 155 report_fatal_error("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 229void 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 report_fatal_error("symbol '" + A->getName() + 247 "' can not be undefined in a subtraction expression"); 248 249 uint32_t Value = Writer->getSymbolAddress(A_SD, Layout); 250 uint64_t SecAddr = Writer->getSectionAddress(A_SD->getFragment()->getParent()); 251 FixedValue += SecAddr; 252 uint32_t Value2 = 0; 253 254 if (const MCSymbolRefExpr *B = Target.getSymB()) { 255 MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol()); 256 257 if (!B_SD->getFragment()) 258 report_fatal_error("symbol '" + B->getSymbol().getName() + 259 "' can not be undefined in a subtraction expression"); 260 261 // Select the appropriate difference relocation type. 262 Type = macho::RIT_Difference; 263 Value2 = Writer->getSymbolAddress(B_SD, Layout); 264 FixedValue -= Writer->getSectionAddress(B_SD->getFragment()->getParent()); 265 } 266 267 // Relocations are written out in reverse order, so the PAIR comes first. 268 if (Type == macho::RIT_Difference || 269 Type == macho::RIT_Generic_LocalDifference) { 270 macho::RelocationEntry MRE; 271 MRE.Word0 = ((0 << 0) | 272 (macho::RIT_Pair << 24) | 273 (Log2Size << 28) | 274 (IsPCRel << 30) | 275 macho::RF_Scattered); 276 MRE.Word1 = Value2; 277 Writer->addRelocation(Fragment->getParent(), MRE); 278 } 279 280 macho::RelocationEntry MRE; 281 MRE.Word0 = ((FixupOffset << 0) | 282 (Type << 24) | 283 (Log2Size << 28) | 284 (IsPCRel << 30) | 285 macho::RF_Scattered); 286 MRE.Word1 = Value; 287 Writer->addRelocation(Fragment->getParent(), MRE); 288} 289 290void ARMMachObjectWriter::RecordRelocation(MachObjectWriter *Writer, 291 const MCAssembler &Asm, 292 const MCAsmLayout &Layout, 293 const MCFragment *Fragment, 294 const MCFixup &Fixup, 295 MCValue Target, 296 uint64_t &FixedValue) { 297 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind()); 298 unsigned Log2Size; 299 unsigned RelocType = macho::RIT_Vanilla; 300 if (!getARMFixupKindMachOInfo(Fixup.getKind(), RelocType, Log2Size)) { 301 report_fatal_error("unknown ARM fixup kind!"); 302 return; 303 } 304 305 // If this is a difference or a defined symbol plus an offset, then we need a 306 // scattered relocation entry. Differences always require scattered 307 // relocations. 308 if (Target.getSymB()) { 309 if (RelocType == macho::RIT_ARM_Half || 310 RelocType == macho::RIT_ARM_HalfDifference) 311 return RecordARMMovwMovtRelocation(Writer, Asm, Layout, Fragment, Fixup, 312 Target, FixedValue); 313 return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup, 314 Target, Log2Size, FixedValue); 315 } 316 317 // Get the symbol data, if any. 318 MCSymbolData *SD = 0; 319 if (Target.getSymA()) 320 SD = &Asm.getSymbolData(Target.getSymA()->getSymbol()); 321 322 // FIXME: For other platforms, we need to use scattered relocations for 323 // internal relocations with offsets. If this is an internal relocation with 324 // an offset, it also needs a scattered relocation entry. 325 // 326 // Is this right for ARM? 327 uint32_t Offset = Target.getConstant(); 328 if (IsPCRel && RelocType == macho::RIT_Vanilla) 329 Offset += 1 << Log2Size; 330 if (Offset && SD && !Writer->doesSymbolRequireExternRelocation(SD)) 331 return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup, 332 Target, Log2Size, FixedValue); 333 334 // See <reloc.h>. 335 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset(); 336 unsigned Index = 0; 337 unsigned IsExtern = 0; 338 unsigned Type = 0; 339 340 if (Target.isAbsolute()) { // constant 341 // FIXME! 342 report_fatal_error("FIXME: relocations to absolute targets " 343 "not yet implemented"); 344 } else { 345 // Resolve constant variables. 346 if (SD->getSymbol().isVariable()) { 347 int64_t Res; 348 if (SD->getSymbol().getVariableValue()->EvaluateAsAbsolute( 349 Res, Layout, Writer->getSectionAddressMap())) { 350 FixedValue = Res; 351 return; 352 } 353 } 354 355 // Check whether we need an external or internal relocation. 356 if (Writer->doesSymbolRequireExternRelocation(SD)) { 357 IsExtern = 1; 358 Index = SD->getIndex(); 359 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 = Asm.getSectionData( 368 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 // The type is determined by the fixup kind. 376 Type = RelocType; 377 } 378 379 // struct relocation_info (8 bytes) 380 macho::RelocationEntry MRE; 381 MRE.Word0 = FixupOffset; 382 MRE.Word1 = ((Index << 0) | 383 (IsPCRel << 24) | 384 (Log2Size << 25) | 385 (IsExtern << 27) | 386 (Type << 28)); 387 Writer->addRelocation(Fragment->getParent(), MRE); 388} 389 390MCObjectWriter *llvm::createARMMachObjectWriter(raw_ostream &OS, 391 bool Is64Bit, 392 uint32_t CPUType, 393 uint32_t CPUSubtype) { 394 return createMachObjectWriter(new ARMMachObjectWriter(Is64Bit, 395 CPUType, 396 CPUSubtype), 397 OS, /*IsLittleEndian=*/true); 398} 399