This is an archive of the discontinued LLVM Phabricator instance.

[mips] Correct ELF format for N32.
AbandonedPublic

Authored by atanasyan on Jun 13 2016, 8:07 AM.

Details

Summary

N32 uses ELFCLASS32, RELA relocs, and does not support the 3-in-1 reloc
encoding that N64 has.

Depends on D21131

Patch by: Daniel Sanders

Event Timeline

dsanders updated this revision to Diff 60527.Jun 13 2016, 8:07 AM
dsanders retitled this revision from to [mips] Correct ELF format for N32..
dsanders updated this object.
dsanders added a reviewer: sdardis.
dsanders added a subscriber: llvm-commits.

Apologies for the very long lines in elf_header.s. I'm about to post a patch to FileCheck that helps with this.

dsanders updated this revision to Diff 65935.Jul 28 2016, 7:47 AM

Refresh and ping

One last ping since I need to either commit this series next week or hand over to a colleague to continue it.

sdardis commandeered this revision.Aug 23 2016, 6:33 AM
sdardis edited reviewers, added: dsanders; removed: sdardis.

Taking over this patch series.

sdardis updated this revision to Diff 68986.Aug 23 2016, 6:33 AM
sdardis updated this object.
sdardis edited edge metadata.

Rebase + ping.

sdardis updated this revision to Diff 76842.Nov 3 2016, 5:03 AM

Rebase and ping.

Ping. Able to compile and run non-PIC n32 binaries for my project. However, lld still crashes when linking PIC n32 object files. I think the fixup_Mips_GPOFF_HI/LO in the ELF writer should also have demux logic to differentiate between n64 and n32, otherwise the 3-in-1 relocation entry may be written to n32 and crashes lld.

Able to compile and run non-PIC n32 binaries for my project. However, lld still crashes when linking PIC n32 object files. I think the fixup_Mips_GPOFF_HI/LO in the ELF writer should also have demux logic to differentiate between n64 and n32, otherwise the 3-in-1 relocation entry may be written to n32 and crashes lld.

How do you create the n32 object file and run LLD? I would like to reproduce the crash.

How do you create the n32 object file and run LLD? I would like to reproduce the crash.

Can be reproduced quite easily:

clang -target mips64 -mcpu=mips4 -mabi=n32 -fPIC -mabicalls -integrated-as -c test.c -o test.o
clang -target mips64 -mcpu=mips4 -mabi=n32 -fPIC -mabicalls -integrated-as -c test2.c -o test2.o
ld.lld test.o test2.o -o test
ld.lld: error: test.c:(function main): relocation R_MIPS_GPREL16 out of range

The disassembly shows GPREL16 relocations inserted in the function prologues which I think caused the problem.
Reduced test case files below.

test.c:

extern void foo();

int
main() {
    foo();
    return 0;
}

test2.c:

void
foo() {
    volatile int i;
    i += 3;
}

After compiling the above test code with gcc, it seems mips gcc under n32 PIC keeps the 3-in-1 relocation "R_MIPS_GPREL16/R_MIPS_SUB/R_MIPS_LO16" in $gp function prologues, whereas clang n32 PIC does not write 3-in-1 relocation records and keeps only the "R_MIPS_GPREL16". It will then trigger the ld "out of range" error.

KoviRobi added a subscriber: KoviRobi.EditedAug 3 2017, 4:13 AM

After some out-of-band discussions with @Jerryxia32 we've discovered that the problem is that the Mips backend tries to write the 3-in-1 reloc format for N32, but the 32-bit ELF cannot store it all, so it only ends up with the R_MIPS_GPREL16, e.g.
N32 (Clang)

$ ../build/bin/clang -target mips64 -mabi=n32 -mcpu=mips4 -fpic -fPIC -mabicalls -integrated-as -c test.c -o -|../build/bin/llvm-readobj -r -

File: <stdin>
Format: ELF32-mips
Arch: mips
AddressSize: 32bit
LoadName: 
Relocations [
  Section (3) .rela.text {
    0x14 R_MIPS_GPREL16 main 0x0
    0x1C R_MIPS_GPREL16 main 0x0
    0x24 R_MIPS_CALL16 foo 0x0
  }
  Section (6) .rela.pdr {
    0x0 R_MIPS_32 main 0x0
  }
]

N64 (Clang)

$ ../build/bin/clang -target mips64 -mabi=n64 -mcpu=mips4 -fpic -fPIC -mabicalls -integrated-as -c test.c -o -|../build/bin/llvm-readobj -r -

File: <stdin>
Format: ELF64-mips
Arch: mips64
AddressSize: 64bit
LoadName: 
Relocations [
  Section (3) .rela.text {
    0x14 R_MIPS_GPREL16/R_MIPS_SUB/R_MIPS_HI16 main 0x0
    0x1C R_MIPS_GPREL16/R_MIPS_SUB/R_MIPS_LO16 main 0x0
    0x24 R_MIPS_CALL16/R_MIPS_NONE/R_MIPS_NONE foo 0x0
  }
  Section (6) .rela.pdr {
    0x0 R_MIPS_32/R_MIPS_NONE/R_MIPS_NONE main 0x0
  }
]

(for the test.c above)

One solution would be to make MipsMCCodeEmitter.cpp emit multiple relocations, but the control flow there is set up to emit one, so it would need some reword to not be confusing, and not trip someone up in the future.

@Jerryxia32 suggested detecting the 3-in-1 relocations when writing the ELF32, and split it. Attached

is the diff, with 45 lines of context so it makes more sense and can be read on its own. Perhaps this is better. (And also included here as it does not display attachments.)

diff --git a/lib/MC/ELFObjectWriter.cpp b/lib/MC/ELFObjectWriter.cpp
index c8dd630..7da3ffa 100644
--- a/lib/MC/ELFObjectWriter.cpp
+++ b/lib/MC/ELFObjectWriter.cpp
@@ -1089,90 +1089,111 @@ void ELFObjectWriter::WriteSecHdrEntry(uint32_t Name, uint32_t Type,
   WriteWord(EntrySize); // sh_entsize
 }
 
 void ELFObjectWriter::writeRelocations(const MCAssembler &Asm,
                                        const MCSectionELF &Sec) {
   std::vector<ELFRelocationEntry> &Relocs = Relocations[&Sec];
 
   // We record relocations by pushing to the end of a vector. Reverse the vector
   // to get the relocations in the order they were created.
   // In most cases that is not important, but it can be for special sections
   // (.eh_frame) or specific relocations (TLS optimizations on SystemZ).
   std::reverse(Relocs.begin(), Relocs.end());
 
   // Sort the relocation entries. MIPS needs this.
   TargetObjectWriter->sortRelocs(Asm, Relocs);
 
   for (unsigned i = 0, e = Relocs.size(); i != e; ++i) {
     const ELFRelocationEntry &Entry = Relocs[e - i - 1];
     unsigned Index = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
 
     if (is64Bit()) {
       write(Entry.Offset);
       if (TargetObjectWriter->isN64()) {
         write(uint32_t(Index));
 
         write(TargetObjectWriter->getRSsym(Entry.Type));
         write(TargetObjectWriter->getRType3(Entry.Type));
         write(TargetObjectWriter->getRType2(Entry.Type));
         write(TargetObjectWriter->getRType(Entry.Type));
       } else {
         struct ELF::Elf64_Rela ERE64;
         ERE64.setSymbolAndType(Index, Entry.Type);
         write(ERE64.r_info);
       }
       if (hasRelocationAddend())
         write(Entry.Addend);
     } else {
       write(uint32_t(Entry.Offset));
 
       struct ELF::Elf32_Rela ERE32;
       ERE32.setSymbolAndType(Index, Entry.Type);
       write(ERE32.r_info);
 
       if (hasRelocationAddend())
         write(uint32_t(Entry.Addend));
+
+      uint32_t tempType = TargetObjectWriter->getRType2(Entry.Type);
+      if(tempType) {
+        write(uint32_t(Entry.Offset));
+
+        ERE32.setSymbolAndType(0, tempType);
+        write(ERE32.r_info);
+
+        if (hasRelocationAddend())
+          write(uint32_t(Entry.Addend));
+      }
+      tempType = TargetObjectWriter->getRType3(Entry.Type);
+      if(tempType) {
+        write(uint32_t(Entry.Offset));
+
+        ERE32.setSymbolAndType(0, tempType);
+        write(ERE32.r_info);
+
+        if (hasRelocationAddend())
+          write(uint32_t(Entry.Addend));
+      }
     }
   }
 }
 
 const MCSectionELF *ELFObjectWriter::createStringTable(MCContext &Ctx) {
   const MCSectionELF *StrtabSection = SectionTable[StringTableIndex - 1];
   StrTabBuilder.write(getStream());
   return StrtabSection;
 }
 
 void ELFObjectWriter::writeSection(const SectionIndexMapTy &SectionIndexMap,
                                    uint32_t GroupSymbolIndex, uint64_t Offset,
                                    uint64_t Size, const MCSectionELF &Section) {
   uint64_t sh_link = 0;
   uint64_t sh_info = 0;
 
   switch(Section.getType()) {
   default:
     // Nothing to do.
     break;
 
   case ELF::SHT_DYNAMIC:
     llvm_unreachable("SHT_DYNAMIC in a relocatable object");
 
   case ELF::SHT_REL:
   case ELF::SHT_RELA: {
     sh_link = SymbolTableIndex;
     assert(sh_link && ".symtab not found");
     const MCSection *InfoSection = Section.getAssociatedSection();
     sh_info = SectionIndexMap.lookup(cast<MCSectionELF>(InfoSection));
     break;
   }
 
   case ELF::SHT_SYMTAB:
   case ELF::SHT_DYNSYM:
     sh_link = StringTableIndex;
     sh_info = LastLocalSymbolIndex;
     break;
 
   case ELF::SHT_SYMTAB_SHNDX:
     sh_link = SymbolTableIndex;
     break;
 
   case ELF::SHT_GROUP:
     sh_link = SymbolTableIndex;
atanasyan commandeered this revision.May 5 2019, 12:29 AM
atanasyan added a reviewer: sdardis.
atanasyan abandoned this revision.May 5 2019, 12:29 AM

Now LLVM/Clang generates correct relocations for N32 ABI:

$ clang -target mips64 -mabi=n32 -mcpu=mips4 -fpic -fPIC -mabicalls -integrated-as -c main.c -o -| llvm-readobj -r -                

File: <stdin>
Format: ELF32-mips
Arch: mips
AddressSize: 32bit
LoadName:
Relocations [
  Section (3) .rela.text {
    0x14 R_MIPS_GPREL16 main 0x0
    0x14 R_MIPS_SUB - 0x0
    0x14 R_MIPS_HI16 - 0x0
    0x1C R_MIPS_GPREL16 main 0x0
    0x1C R_MIPS_SUB - 0x0
    0x1C R_MIPS_LO16 - 0x0
    0x30 R_MIPS_GOT_PAGE .rodata.cst8 0x0
    0x34 R_MIPS_GOT_OFST .rodata.cst8 0x0
    0x44 R_MIPS_GOT_PAGE .rodata.str1.1 0x0
    0x48 R_MIPS_GOT_OFST .rodata.str1.1 0x0
    0x50 R_MIPS_CALL16 printf 0x0
    0x60 R_MIPS_JALR printf 0x0
  }
  Section (7) .rela.pdr {
    0x0 R_MIPS_32 main 0x0
  }
]
test/MC/Mips/cpsetup.s