diff --git a/llvm/lib/MC/WinCOFFObjectWriter.cpp b/llvm/lib/MC/WinCOFFObjectWriter.cpp --- a/llvm/lib/MC/WinCOFFObjectWriter.cpp +++ b/llvm/lib/MC/WinCOFFObjectWriter.cpp @@ -56,6 +56,8 @@ namespace { +constexpr int OffsetLabelIntervalBits = 20; + using name = SmallString; enum AuxiliaryType { @@ -120,6 +122,8 @@ relocations Relocations; COFFSection(StringRef Name) : Name(std::string(Name)) {} + + SmallVector OffsetSymbols; }; class WinCOFFObjectWriter : public MCObjectWriter { @@ -149,6 +153,7 @@ symbol_list WeakDefaults; bool UseBigObj; + bool UseOffsetLabels; bool EmitAddrsigSection = false; MCSectionCOFF *AddrsigSection; @@ -174,7 +179,7 @@ COFFSymbol *GetOrCreateCOFFSymbol(const MCSymbol *Symbol); COFFSection *createSection(StringRef Name); - void defineSection(MCSectionCOFF const &Sec); + void defineSection(MCSectionCOFF const &Sec, const MCAsmLayout &Layout); COFFSymbol *getLinkedSymbol(const MCSymbol &Symbol); void DefineSymbol(const MCSymbol &Symbol, MCAssembler &Assembler, @@ -244,6 +249,11 @@ std::unique_ptr MOTW, raw_pwrite_stream &OS) : W(OS, support::little), TargetObjectWriter(std::move(MOTW)) { Header.Machine = TargetObjectWriter->getMachine(); + // Some relocations on ARM64 (the 21 bit ADRP relocations) have a slightly + // limited range for the immediate offset (+/- 1 MB); create extra offset + // label symbols with regular intervals to allow referencing a + // non-temporary symbol that is close enough. + UseOffsetLabels = Header.Machine == COFF::IMAGE_FILE_MACHINE_ARM64; } COFFSymbol *WinCOFFObjectWriter::createSymbol(StringRef Name) { @@ -299,7 +309,8 @@ /// This function takes a section data object from the assembler /// and creates the associated COFF section staging object. -void WinCOFFObjectWriter::defineSection(const MCSectionCOFF &MCSec) { +void WinCOFFObjectWriter::defineSection(const MCSectionCOFF &MCSec, + const MCAsmLayout &Layout) { COFFSection *Section = createSection(MCSec.getName()); COFFSymbol *Symbol = createSymbol(MCSec.getName()); Section->Symbol = Symbol; @@ -329,6 +340,20 @@ // Bind internal COFF section to MC section. Section->MCSection = &MCSec; SectionMap[&MCSec] = Section; + + if (UseOffsetLabels && !MCSec.getFragmentList().empty()) { + const uint32_t Interval = 1 << OffsetLabelIntervalBits; + uint32_t N = 1; + for (uint32_t Off = Interval, E = Layout.getSectionAddressSize(&MCSec); + Off < E; Off += Interval) { + auto Name = ("$L" + MCSec.getName() + "_" + Twine(N++)).str(); + COFFSymbol *Label = createSymbol(Name); + Label->Section = Section; + Label->Data.StorageClass = COFF::IMAGE_SYM_CLASS_LABEL; + Label->Data.Value = Off; + Section->OffsetSymbols.push_back(Label); + } + } } static uint64_t getSymbolValue(const MCSymbol &Symbol, @@ -688,7 +713,7 @@ // "Define" each section & symbol. This creates section & symbol // entries in the staging area. for (const auto &Section : Asm) - defineSection(static_cast(Section)); + defineSection(static_cast(Section), Layout); for (const MCSymbol &Symbol : Asm.symbols()) if (!Symbol.isTemporary()) @@ -774,8 +799,23 @@ assert( SectionMap.find(TargetSection) != SectionMap.end() && "Section must already have been defined in executePostLayoutBinding!"); - Reloc.Symb = SectionMap[TargetSection]->Symbol; + COFFSection *Section = SectionMap[TargetSection]; + Reloc.Symb = Section->Symbol; FixedValue += Layout.getSymbolOffset(A); + // Technically, we should do the final adjustments of FixedValue (below) + // before picking an offset symbol, otherwise we might choose one which + // is slightly too far away. The relocations where it really matters + // (arm64 adrp relocations) don't get any offset though. + if (UseOffsetLabels && !Section->OffsetSymbols.empty()) { + uint64_t LabelIndex = FixedValue >> OffsetLabelIntervalBits; + if (LabelIndex > 0) { + if (LabelIndex <= Section->OffsetSymbols.size()) + Reloc.Symb = Section->OffsetSymbols[LabelIndex - 1]; + else + Reloc.Symb = Section->OffsetSymbols.back(); + FixedValue -= Reloc.Symb->Data.Value; + } + } } else { assert( SymbolMap.find(&A) != SymbolMap.end() && diff --git a/llvm/test/MC/AArch64/coff-relocations-offset.s b/llvm/test/MC/AArch64/coff-relocations-offset.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/AArch64/coff-relocations-offset.s @@ -0,0 +1,49 @@ +// RUN: llvm-mc -triple aarch64-windows -filetype obj -o %t.obj %s +// RUN: llvm-objdump -d -r %t.obj | FileCheck %s +// RUN: llvm-readobj --syms %t.obj | FileCheck %s --check-prefix=SYMBOLS + + .text +main: + adrp x0, .Ltmp0 + adrp x0, .Ltmp1 + adrp x0, .Ltmp2+8 + + .section .rdata + .word 1 +.Ltmp0: + .word 2 + .fill 1048576 +.Ltmp1: // 1 MB + 8 bytes + .fill (1048576-8-4) +.Ltmp2: // 2 MB - 4 bytes + .word 3 + // 2 MB here + .word 4 + // .Ltmp2+8 points here + .word 5 + +// CHECK: 0: 20 00 00 90 adrp x0, 0x4000 +// CHECK-NEXT: 0000000000000000: IMAGE_REL_ARM64_PAGEBASE_REL21 .rdata +// CHECK-NEXT: 4: 40 00 00 90 adrp x0, 0x8000 +// CHECK-NEXT: 0000000000000004: IMAGE_REL_ARM64_PAGEBASE_REL21 $L.rdata_1 +// CHECK-NEXT: 8: 20 00 00 90 adrp x0, 0x4000 +// CHECK-NEXT: 0000000000000008: IMAGE_REL_ARM64_PAGEBASE_REL21 $L.rdata_2 + +// SYMBOLS: Symbol { +// SYMBOLS: Name: $L.rdata_1 +// SYMBOLS-NEXT: Value: 1048576 +// SYMBOLS-NEXT: Section: .rdata (4) +// SYMBOLS-NEXT: BaseType: Null (0x0) +// SYMBOLS-NEXT: ComplexType: Null (0x0) +// SYMBOLS-NEXT: StorageClass: Label (0x6) +// SYMBOLS-NEXT: AuxSymbolCount: 0 +// SYMBOLS-NEXT: } +// SYMBOLS-NEXT: Symbol { +// SYMBOLS-NEXT: Name: $L.rdata_2 +// SYMBOLS-NEXT: Value: 2097152 +// SYMBOLS-NEXT: Section: .rdata (4) +// SYMBOLS-NEXT: BaseType: Null (0x0) +// SYMBOLS-NEXT: ComplexType: Null (0x0) +// SYMBOLS-NEXT: StorageClass: Label (0x6) +// SYMBOLS-NEXT: AuxSymbolCount: 0 +// SYMBOLS-NEXT: }