Index: include/llvm/MC/MCELFObjectWriter.h =================================================================== --- include/llvm/MC/MCELFObjectWriter.h +++ include/llvm/MC/MCELFObjectWriter.h @@ -25,6 +25,17 @@ class MCSymbolData; class MCValue; +struct ELFRelocationEntry { + uint64_t Offset; // Where is the relocation. + const MCSymbol *Symbol; // The symbol to relocate with. + unsigned Type; // The type of the relocation. + uint64_t Addend; // The addend to use. + + ELFRelocationEntry(uint64_t Offset, const MCSymbol *Symbol, unsigned Type, + uint64_t Addend) + : Offset(Offset), Symbol(Symbol), Type(Type), Addend(Addend) {} +}; + class MCELFObjectTargetWriter { const uint8_t OSABI; const uint16_t EMachine; @@ -59,6 +70,9 @@ virtual bool needsRelocateWithSymbol(const MCSymbolData &SD, unsigned Type) const; + virtual void sortRelocs(const MCAssembler &Asm, + std::vector &Relocs); + /// @name Accessors /// @{ uint8_t getOSABI() const { return OSABI; } Index: lib/MC/ELFObjectWriter.cpp =================================================================== --- lib/MC/ELFObjectWriter.cpp +++ lib/MC/ELFObjectWriter.cpp @@ -79,17 +79,6 @@ uint8_t other, uint32_t shndx, bool Reserved); }; -struct ELFRelocationEntry { - uint64_t Offset; // Where is the relocation. - const MCSymbol *Symbol; // The symbol to relocate with. - unsigned Type; // The type of the relocation. - uint64_t Addend; // The addend to use. - - ELFRelocationEntry(uint64_t Offset, const MCSymbol *Symbol, unsigned Type, - uint64_t Addend) - : Offset(Offset), Symbol(Symbol), Type(Type), Addend(Addend) {} -}; - class ELFObjectWriter : public MCObjectWriter { FragmentWriter FWriter; @@ -1338,30 +1327,14 @@ WriteWord(EntrySize); // sh_entsize } -// ELF doesn't require relocations to be in any order. We sort by the r_offset, -// just to match gnu as for easier comparison. The use type is an arbitrary way -// of making the sort deterministic. -static int cmpRel(const ELFRelocationEntry *AP, const ELFRelocationEntry *BP) { - const ELFRelocationEntry &A = *AP; - const ELFRelocationEntry &B = *BP; - if (A.Offset != B.Offset) - return B.Offset - A.Offset; - if (B.Type != A.Type) - return A.Type - B.Type; - llvm_unreachable("ELFRelocs might be unstable!"); -} - -static void sortRelocs(const MCAssembler &Asm, - std::vector &Relocs) { - array_pod_sort(Relocs.begin(), Relocs.end(), cmpRel); -} - void ELFObjectWriter::WriteRelocationsFragment(const MCAssembler &Asm, MCDataFragment *F, const MCSectionData *SD) { std::vector &Relocs = Relocations[SD]; - sortRelocs(Asm, Relocs); + // Sort the relocation entries. Most targets just sort by Offset, but some + // (e.g., MIPS) have additional constraints. + TargetObjectWriter->sortRelocs(Asm, Relocs); for (unsigned i = 0, e = Relocs.size(); i != e; ++i) { const ELFRelocationEntry &Entry = Relocs[e - i - 1]; Index: lib/MC/MCELFObjectTargetWriter.cpp =================================================================== --- lib/MC/MCELFObjectTargetWriter.cpp +++ lib/MC/MCELFObjectTargetWriter.cpp @@ -28,3 +28,23 @@ unsigned Type) const { return false; } + + +// ELF doesn't require relocations to be in any order. We sort by the Offset, +// just to match gnu as for easier comparison. The use type is an arbitrary way +// of making the sort deterministic. +static int cmpRel(const ELFRelocationEntry *AP, const ELFRelocationEntry *BP) { + const ELFRelocationEntry &A = *AP; + const ELFRelocationEntry &B = *BP; + if (A.Offset != B.Offset) + return B.Offset - A.Offset; + if (B.Type != A.Type) + return A.Type - B.Type; + llvm_unreachable("ELFRelocs might be unstable!"); +} + +void +MCELFObjectTargetWriter::sortRelocs(const MCAssembler &Asm, + std::vector &Relocs) { + array_pod_sort(Relocs.begin(), Relocs.end(), cmpRel); +} Index: lib/Target/Mips/MCTargetDesc/MipsELFObjectWriter.cpp =================================================================== --- lib/Target/Mips/MCTargetDesc/MipsELFObjectWriter.cpp +++ lib/Target/Mips/MCTargetDesc/MipsELFObjectWriter.cpp @@ -22,6 +22,13 @@ using namespace llvm; namespace { + struct MipsRelocationEntry { + MipsRelocationEntry(ELFRelocationEntry &R): + Reloc(R), SortOffset(R.Offset) {} + ELFRelocationEntry Reloc; + uint64_t SortOffset; + }; + class MipsELFObjectWriter : public MCELFObjectTargetWriter { public: MipsELFObjectWriter(bool _is64Bit, uint8_t OSABI, @@ -33,6 +40,8 @@ bool IsPCRel) const override; bool needsRelocateWithSymbol(const MCSymbolData &SD, unsigned Type) const override; + virtual void sortRelocs(const MCAssembler &Asm, + std::vector &Relocs); }; } @@ -223,6 +232,75 @@ return Type; } +// Sort relocations by the SortOffset rather than Offset. +static int cmpRelMips(const MipsRelocationEntry *AP, + const MipsRelocationEntry *BP) { + const MipsRelocationEntry &A = *AP; + const MipsRelocationEntry &B = *BP; + if (A.SortOffset != B.SortOffset) + return B.SortOffset - A.SortOffset; + if (A.Reloc.Offset != B.Reloc.Offset) + return B.Reloc.Offset - A.Reloc.Offset; + if (B.Reloc.Type != A.Reloc.Type) + return B.Reloc.Type - A.Reloc.Type; + llvm_unreachable("ELFRelocs might be unstable!"); +} + +// Return true if R is either a GOT16 against a local symbol or HI16. +static bool needsMatchingLo(const MCAssembler &Asm, + const MipsRelocationEntry &R) { + if (!R.Reloc.Symbol) + return false; + + if (R.Reloc.Type == ELF::R_MIPS_HI16) + return true; + + if (R.Reloc.Type != ELF::R_MIPS_GOT16) + return false; + + const MCSymbolData &SD = Asm.getSymbolData(*(R.Reloc).Symbol); + return !SD.isExternal(); +} + +// For the relocation entry at Hi16Index in MipsRelocs, find the first matching +// LO16 relocation (if any), and set HI16's SortOffset to the found LO16's +// Offset minus one. +static void findMatchingLo(unsigned Hi16Index, + std::vector &MipsRelocs) { + MipsRelocationEntry &Hi16Reloc = MipsRelocs[Hi16Index]; + for (unsigned J = 1, E = MipsRelocs.size(); J != E; ++J) { + MipsRelocationEntry &Lo16Reloc = MipsRelocs[(Hi16Index + J) % E]; + if (Lo16Reloc.Reloc.Type == ELF::R_MIPS_LO16 && + Lo16Reloc.Reloc.Symbol == Hi16Reloc.Reloc.Symbol) { + Hi16Reloc.SortOffset = Lo16Reloc.Reloc.Offset -1; + break; + } + } +} + +// Put HI16 and local GOT16 relocation records right before the corresponding +// LO16 record. Sort all others by offset. +void MipsELFObjectWriter::sortRelocs(const MCAssembler &Asm, + std::vector &Relocs) { + if (Relocs.size() < 2) return; + + // Init MipsRelocs from Relocs. + std::vector MipsRelocs; + for (unsigned I = 0, E = Relocs.size(); I != E; ++I) + MipsRelocs.push_back(MipsRelocationEntry(Relocs[I])); + + // Find entries that need a matching LO16 relocation and set their SortOffset. + for (unsigned I = 0, E = MipsRelocs.size(); I != E; ++I) + if (needsMatchingLo(Asm, MipsRelocs[I])) + findMatchingLo(I, MipsRelocs); + + array_pod_sort(MipsRelocs.begin(), MipsRelocs.end(), cmpRelMips); + + // Copy sorted MipsRelocs back to Relocs. + for (unsigned I = 0, E = MipsRelocs.size(); I != E; ++I) + Relocs[I] = MipsRelocs[I].Reloc; +} + bool MipsELFObjectWriter::needsRelocateWithSymbol(const MCSymbolData &SD, unsigned Type) const { Index: test/MC/Mips/sort-relocs.s =================================================================== --- /dev/null +++ test/MC/Mips/sort-relocs.s @@ -0,0 +1,40 @@ +# RUN: llvm-mc -filetype=obj -arch mipsel %s | llvm-readobj -r | FileCheck %s + +# Test the order of records in the relocation table. +# HI16 and local GOT16 relocations should be immediately followed by a LO16 +# relocation against the same symbol. + +# CHECK: Relocations [ +# CHECK-NEXT: { + + lui $2, %hi(func1) + addiu $2, $2, %lo(func1) + lui $2, %hi(func1) + +# CHECK-NEXT: 0x{{[0-9,A-F]+}} R_MIPS_HI16 func1 +# CHECK-NEXT: 0x{{[0-9,A-F]+}} R_MIPS_HI16 func1 +# CHECK-NEXT: 0x{{[0-9,A-F]+}} R_MIPS_LO16 func1 + + lw $2, %lo(func2)($2) + lui $2, %hi(func2) + +# CHECK-NEXT: 0x{{[0-9,A-F]+}} R_MIPS_HI16 func2 +# CHECK-NEXT: 0x{{[0-9,A-F]+}} R_MIPS_LO16 func2 + + lui $2, %hi(func31) + lw $2, %lo(func32)($2) + lw $2, %lo(func31)($2) + +# CHECK-NEXT: 0x{{[0-9,A-F]+}} R_MIPS_LO16 func32 +# CHECK-NEXT: 0x{{[0-9,A-F]+}} R_MIPS_HI16 func31 +# CHECK-NEXT: 0x{{[0-9,A-F]+}} R_MIPS_LO16 func31 + + lw $2, %got(func41)($gp) + addiu $3, $2, %lo(func42) + addiu $4, $2, %lo(func41) + +# CHECK-NEXT: 0x{{[0-9,A-F]+}} R_MIPS_LO16 func42 +# CHECK-NEXT: 0x{{[0-9,A-F]+}} R_MIPS_GOT16 func41 +# CHECK-NEXT: 0x{{[0-9,A-F]+}} R_MIPS_LO16 func41 + +# CHECK-NEXT: } Index: test/MC/Mips/xgot.s =================================================================== --- test/MC/Mips/xgot.s +++ test/MC/Mips/xgot.s @@ -9,8 +9,8 @@ // CHECK: 0x14 R_MIPS_GOT_HI16 ext_1 // CHECK: 0x1C R_MIPS_GOT_LO16 ext_1 // CHECK: 0x24 R_MIPS_CALL_HI16 printf -// CHECK: 0x2C R_MIPS_GOT16 $.str // CHECK: 0x30 R_MIPS_CALL_LO16 printf +// CHECK: 0x2C R_MIPS_GOT16 $.str // CHECK: 0x38 R_MIPS_LO16 $.str // CHECK: ]