Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -463,6 +463,8 @@ case R_ABS: case R_RELAX_GOT_PC_NOPIC: return Sym.getVA(A); + case R_ADDEND: + return A; case R_ARM_SBREL: return Sym.getVA(A) - getARMStaticBase(Sym); case R_GOT: Index: ELF/Relocations.h =================================================================== --- ELF/Relocations.h +++ ELF/Relocations.h @@ -32,6 +32,7 @@ enum RelExpr { R_INVALID, R_ABS, + R_ADDEND, R_ARM_SBREL, R_GOT, R_GOTONLY_PC, Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -94,7 +94,8 @@ if (InX::MipsGot->addTlsIndex() && Config->Pic) InX::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::MipsGot, InX::MipsGot->getTlsIndexOff(), false, nullptr, - 0}); + 0}, + Type); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } @@ -103,10 +104,11 @@ if (InX::MipsGot->addDynTlsEntry(Sym) && Sym.IsPreemptible) { uint64_t Off = InX::MipsGot->getGlobalDynOffset(Sym); InX::RelaDyn->addReloc( - {Target->TlsModuleIndexRel, InX::MipsGot, Off, false, &Sym, 0}); + {Target->TlsModuleIndexRel, InX::MipsGot, Off, false, &Sym, 0}, Type); if (Sym.IsPreemptible) InX::RelaDyn->addReloc({Target->TlsOffsetRel, InX::MipsGot, - Off + Config->Wordsize, false, &Sym, 0}); + Off + Config->Wordsize, false, &Sym, 0}, + Type); } C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; @@ -140,7 +142,7 @@ auto AddTlsReloc = [&](uint64_t Off, RelType Type, Symbol *Dest, bool Dyn) { if (Dyn) - InX::RelaDyn->addReloc({Type, InX::Got, Off, false, Dest, 0}); + InX::RelaDyn->addReloc({Type, InX::Got, Off, false, Dest, 0}, Type); else InX::Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest}); }; @@ -193,7 +195,8 @@ if (InX::Got->addDynTlsEntry(Sym)) { uint64_t Off = InX::Got->getGlobalDynOffset(Sym); InX::RelaDyn->addReloc( - {Target->TlsDescRel, InX::Got, Off, !Sym.IsPreemptible, &Sym, 0}); + {Target->TlsDescRel, InX::Got, Off, !Sym.IsPreemptible, &Sym, 0}, + Type); } if (Expr != R_TLSDESC_CALL) C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); @@ -209,7 +212,8 @@ } if (InX::Got->addTlsIndex()) InX::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::Got, - InX::Got->getTlsIndexOff(), false, nullptr, 0}); + InX::Got->getTlsIndexOff(), false, nullptr, 0}, + Type); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } @@ -226,14 +230,15 @@ if (InX::Got->addDynTlsEntry(Sym)) { uint64_t Off = InX::Got->getGlobalDynOffset(Sym); InX::RelaDyn->addReloc( - {Target->TlsModuleIndexRel, InX::Got, Off, false, &Sym, 0}); + {Target->TlsModuleIndexRel, InX::Got, Off, false, &Sym, 0}, Type); // If the symbol is preemptible we need the dynamic linker to write // the offset too. uint64_t OffsetOff = Off + Config->Wordsize; if (Sym.IsPreemptible) InX::RelaDyn->addReloc( - {Target->TlsOffsetRel, InX::Got, OffsetOff, false, &Sym, 0}); + {Target->TlsOffsetRel, InX::Got, OffsetOff, false, &Sym, 0}, + Type); else InX::Got->Relocations.push_back( {R_ABS, Target->TlsOffsetRel, OffsetOff, 0, &Sym}); @@ -251,7 +256,8 @@ if (!Sym.isInGot()) { InX::Got->addEntry(Sym); InX::RelaDyn->addReloc( - {Target->TlsGotRel, InX::Got, Sym.getGotOffset(), false, &Sym, 0}); + {Target->TlsGotRel, InX::Got, Sym.getGotOffset(), false, &Sym, 0}, + Type); } } else { C.Relocations.push_back( @@ -531,7 +537,8 @@ Sym->Used = true; } - InX::RelaDyn->addReloc({Target->CopyRel, Sec, 0, false, &SS, 0}); + InX::RelaDyn->addReloc({Target->CopyRel, Sec, 0, false, &SS, 0}, + Target->CopyRel); } // MIPS has an odd notion of "paired" relocations to calculate addends. @@ -695,7 +702,7 @@ Plt->addEntry(Sym); GotPlt->addEntry(Sym); Rel->addReloc( - {Type, GotPlt, Sym.getGotPltOffset(), !Sym.IsPreemptible, &Sym, 0}); + {Type, GotPlt, Sym.getGotPltOffset(), !Sym.IsPreemptible, &Sym, 0}, Type); } template static void addGotEntry(Symbol &Sym) { @@ -727,8 +734,11 @@ Type = Target->RelativeRel; else Type = Target->GotRel; - InX::RelaDyn->addReloc(Type, InX::Got, Off, !Sym.IsPreemptible, &Sym, 0, - R_ABS, Target->GotRel); + + // If the symbol is preemptible we shouldn't write the full value to the GOT + // but only the addend + InX::RelaDyn->addReloc({Type, InX::Got, Off, !Sym.IsPreemptible, &Sym, 0}, + Type, Sym.IsPreemptible ? R_ADDEND : R_ABS); } // Return true if we can define a symbol in the executable that @@ -778,12 +788,12 @@ bool IsPreemptibleValue = Sym.IsPreemptible && Expr != R_GOT; if (!IsPreemptibleValue) { - InX::RelaDyn->addReloc(Target->RelativeRel, &Sec, Offset, true, &Sym, - Addend, Expr, Type); + InX::RelaDyn->addReloc( + {Target->RelativeRel, &Sec, Offset, true, &Sym, Addend}, Type, Expr); return Expr; } else if (Target->isPicRel(Type)) { InX::RelaDyn->addReloc( - {Target->getDynRel(Type), &Sec, Offset, false, &Sym, Addend}); + {Target->getDynRel(Type), &Sec, Offset, false, &Sym, Addend}, Type); // MIPS ABI turns using of GOT and dynamic relocations inside out. // While regular ABI uses dynamic relocations to fill up GOT entries @@ -979,7 +989,8 @@ InX::MipsGot->addEntry(Sym, Addend, Expr); if (Sym.isTls() && Sym.IsPreemptible) InX::RelaDyn->addReloc({Target->TlsGotRel, InX::MipsGot, - Sym.getGotOffset(), false, &Sym, 0}); + Sym.getGotOffset(), false, &Sym, 0}, + Type); } else if (!Sym.isInGot()) { addGotEntry(Sym); } Index: ELF/SyntheticSections.h =================================================================== --- ELF/SyntheticSections.h +++ ELF/SyntheticSections.h @@ -328,6 +328,7 @@ uint64_t OffsetInSec; bool UseSymVA; int64_t Addend; + friend class RelocationBaseSection; }; template class DynamicSection final : public SyntheticSection { @@ -361,10 +362,8 @@ public: RelocationBaseSection(StringRef Name, uint32_t Type, int32_t DynamicTag, int32_t SizeDynamicTag); - void addReloc(uint32_t DynType, InputSectionBase *InputSec, - uint64_t OffsetInSec, bool UseSymVA, Symbol *Sym, - int64_t Addend, RelExpr Expr, RelType Type); - void addReloc(const DynamicReloc &Reloc); + void addReloc(const DynamicReloc &Reloc, RelType Type, + RelExpr Expr = R_ADDEND); bool empty() const override { return Relocs.empty(); } size_t getSize() const override { return Relocs.size() * this->Entsize; } size_t getRelativeRelocCount() const { return NumRelativeRelocs; } Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -1202,20 +1202,17 @@ : SyntheticSection(SHF_ALLOC, Type, Config->Wordsize, Name), DynamicTag(DynamicTag), SizeDynamicTag(SizeDynamicTag) {} -void RelocationBaseSection::addReloc(uint32_t DynType, - InputSectionBase *InputSec, - uint64_t OffsetInSec, bool UseSymVA, - Symbol *Sym, int64_t Addend, RelExpr Expr, - RelType Type) { +void RelocationBaseSection::addReloc(const DynamicReloc &Reloc, RelType Type, + RelExpr Expr) { // REL type relocations don't have addend fields unlike RELAs, and // their addends are stored to the section to which they are applied. // So, store addends if we need to. - if (!Config->IsRela && UseSymVA) - InputSec->Relocations.push_back({Expr, Type, OffsetInSec, Addend, Sym}); - addReloc({DynType, InputSec, OffsetInSec, UseSymVA, Sym, Addend}); -} - -void RelocationBaseSection::addReloc(const DynamicReloc &Reloc) { + if (!Config->IsRela) { + auto *IS = const_cast(Reloc.InputSec); + if (Reloc.Addend != 0 || Expr != R_ADDEND) + IS->Relocations.push_back( + {Expr, Type, Reloc.OffsetInSec, Reloc.Addend, Reloc.Sym}); + } if (Reloc.Type == Target->RelativeRel) ++NumRelativeRelocs; Relocs.push_back(Reloc); Index: test/ELF/convert-rel-rela-addends.s =================================================================== --- /dev/null +++ test/ELF/convert-rel-rela-addends.s @@ -0,0 +1,109 @@ +# REQUIRES: x86 +# Check that we correctly write addends if the output use Elf_Rel but the input +# uses Elf_Rela or the other way around + + +# RUN: llvm-mc -filetype=obj -defsym=IS_64=1 -triple=x86_64-unknown-linux %s -o %t-x86_64-rela.o +# RUN: llvm-objdump --section=.data -s %t-x86_64-rela.o | FileCheck -check-prefix DATA-RELA %s +# DATA-RELA: Contents of section .data: +# DATA-RELA-NEXT: 0000 00000000 00000000 78563412 00efcdab + +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t-i386-rel.o +# RUN: llvm-objdump --section=.data -s %t-i386-rel.o | FileCheck -check-prefix DATA-REL %s +# Rel should have the relocation value 10 in .data: +# DATA-REL: Contents of section .data: +# DATA-REL-NEXT: 0000 44550000 00000000 78563412 00efcdab +# ^---- (addend 0x5544) + +# Create two object files that use the non-default relocation format +# RUN: llvm-mc -filetype=obj -defsym=IS_64=1 -triple=x86_64-unknown-linux %s -elf-relocation-format=rel -o %t-x86_64-rel.o +# RUN: llvm-objdump --section=.data -s %t-x86_64-rel.o | FileCheck -check-prefix DATA-REL %s +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -elf-relocation-format=rela -o %t-i386-rela.o +# RUN: llvm-objdump --section=.data -s %t-i386-rela.o | FileCheck -check-prefix DATA-RELA %s + +# RUN: ld.lld -shared -o %t.so %t-i386-rel.o +# RUN: llvm-readobj -h -s -section-data -relocations %t.so | FileCheck -check-prefix REL %s + +# Note: llvm-readobj prints 0x0 as the relocation addend since it doesn't parse +# REL relocations. In order to verify the addend we need to check the contents of .data +# REL: ElfHeader { +# REL: Class: 32-bit +# REL: DataEncoding: LittleEndian +# REL: Section { +# REL: Name: .data +# REL: SectionData ( +# REL-NEXT: 0000: 44550000 00000000 78563412 00EFCDAB |DU......xV4.....| +# ^--- Addend 0x5544 for the relocation below +# REL-NEXT: ) +# REL: Relocations [ +# REL-NEXT: Section ({{.+}}) .rel.dyn { +# REL-NEXT: 0x1000 R_386_32 foo 0x0 +# REL-NEXT: } +# REL-NEXT: ] + + +# RUN: ld.lld -shared -o %t.so %t-x86_64-rela.o +# RUN: llvm-readobj -h -s -section-data -relocations %t.so | FileCheck -check-prefix RELA %s +# RELA: ElfHeader { +# RELA: Class: 64-bit +# RELA: DataEncoding: LittleEndian +# RELA: Section { +# RELA: Name: .data +# RELA: SectionData ( +# RELA-NEXT: 0000: 00000000 00000000 78563412 00EFCDAB |........xV4.....| +# ^--- No addend written here since it is part of Elf_Rela +# RELA: Relocations [ +# RELA-NEXT: Section ({{.+}}) .rela.dyn { +# RELA-NEXT: 0x1000 R_X86_64_64 foo 0x5544 +# RELA-NEXT: } +# RELA-NEXT: ] + +# Previously we would lose the addend if the output uses REL but a .foo.rela was being +# copied into the relocatable output. This happenend because addends were only added +# to the output section when Config->IsRela was true instead of checking for RelTy::IsRela. +# Check that the relacation addends are copied into the output as expected: +# RUN: ld.lld -shared -o %t.so %t-i386-rela.o +# RUN: llvm-readobj -h -s -section-data -relocations %t.so | FileCheck -check-prefix RELA-TO-REL %s +# RELA-TO-REL: ElfHeader { +# RELA-TO-REL: Class: 32-bit +# RELA-TO-REL: DataEncoding: LittleEndian +# RELA-TO-REL: Section { +# RELA-TO-REL: Name: .data +# RELA-TO-REL: SectionData ( +# RELA-TO-REL-NEXT: 0000: 44550000 00000000 78563412 00EFCDAB |DU......xV4.....| +# ^--- Addend for relocation in .rel.dyn +# RELA-TO-REL: Relocations [ +# RELA-TO-REL-NEXT: Section ({{.+}}) .rel.dyn { +# RELA-TO-REL-NEXT: 0x1000 R_386_32 foo 0x0 +# RELA-TO-REL-NEXT: } +# RELA-TO-REL-NEXT: ] + + +# And also check that RELA output works fine with REL input +# RUN: ld.lld -shared -o %t.so %t-x86_64-rel.o +# RUN: llvm-readobj -h -s -section-data -relocations %t.so | FileCheck -check-prefix REL-TO-RELA %s +# REL-TO-RELA: ElfHeader { +# REL-TO-RELA: Class: 64-bit +# REL-TO-RELA: DataEncoding: LittleEndian +# REL-TO-RELA: Section { +# REL-TO-RELA: Name: .data +# REL-TO-RELA: SectionData ( +# REL-TO-RELA-NEXT: 0000: 44550000 00000000 78563412 00EFCDAB |DU......xV4.....| +# ^--- Addend from the input file stays here even though we use RELA +# REL-TO-RELA-NEXT: ) +# REL-TO-RELA: Relocations [ +# REL-TO-RELA-NEXT: Section ({{.+}}) .rela.dyn { +# REL-TO-RELA-NEXT: 0x1000 R_X86_64_64 foo 0x0 +# REL-TO-RELA-NEXT: } +# REL-TO-RELA-NEXT: ] + +.extern foo + +.data +.ifdef IS_64 +.quad foo + 0x5544 +.else +.long foo + 0x5544 +.long 0 +.endif +.quad 0xabcdef0012345678 \ No newline at end of file