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 @@ -92,9 +92,8 @@ int64_t Addend, RelExpr Expr) { if (Expr == R_MIPS_TLSLD) { if (InX::MipsGot->addTlsIndex() && Config->Pic) - InX::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::MipsGot, - InX::MipsGot->getTlsIndexOff(), false, nullptr, - 0}); + InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, InX::MipsGot, + InX::MipsGot->getTlsIndexOff(), nullptr); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } @@ -102,11 +101,11 @@ if (Expr == R_MIPS_TLSGD) { 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}); + InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, InX::MipsGot, Off, + &Sym); if (Sym.IsPreemptible) - InX::RelaDyn->addReloc({Target->TlsOffsetRel, InX::MipsGot, - Off + Config->Wordsize, false, &Sym, 0}); + InX::RelaDyn->addReloc(Target->TlsOffsetRel, InX::MipsGot, + Off + Config->Wordsize, &Sym); } C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; @@ -140,7 +139,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, Dest); else InX::Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest}); }; @@ -193,7 +192,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}, + Target->TlsDescRel, Expr); } if (Expr != R_TLSDESC_CALL) C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); @@ -208,8 +208,8 @@ return 2; } if (InX::Got->addTlsIndex()) - InX::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::Got, - InX::Got->getTlsIndexOff(), false, nullptr, 0}); + InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, InX::Got, + InX::Got->getTlsIndexOff(), nullptr); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } @@ -225,15 +225,14 @@ if (Config->Shared) { if (InX::Got->addDynTlsEntry(Sym)) { uint64_t Off = InX::Got->getGlobalDynOffset(Sym); - InX::RelaDyn->addReloc( - {Target->TlsModuleIndexRel, InX::Got, Off, false, &Sym, 0}); + InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, InX::Got, Off, &Sym); // 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}); + InX::RelaDyn->addReloc(Target->TlsOffsetRel, InX::Got, OffsetOff, + &Sym); else InX::Got->Relocations.push_back( {R_ABS, Target->TlsOffsetRel, OffsetOff, 0, &Sym}); @@ -250,8 +249,8 @@ Offset, Addend, &Sym}); if (!Sym.isInGot()) { InX::Got->addEntry(Sym); - InX::RelaDyn->addReloc( - {Target->TlsGotRel, InX::Got, Sym.getGotOffset(), false, &Sym, 0}); + InX::RelaDyn->addReloc(Target->TlsGotRel, InX::Got, Sym.getGotOffset(), + &Sym); } } else { C.Relocations.push_back( @@ -531,7 +530,7 @@ Sym->Used = true; } - InX::RelaDyn->addReloc({Target->CopyRel, Sec, 0, false, &SS, 0}); + InX::RelaDyn->addReloc(Target->CopyRel, Sec, 0, &SS); } // MIPS has an odd notion of "paired" relocations to calculate addends. @@ -695,7 +694,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, R_ABS); } template static void addGotEntry(Symbol &Sym) { @@ -727,8 +726,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 not preemptible, we can just write the virtual address to + // the GOT and add a relocation against the load address instead of using Sym + InX::RelaDyn->addReloc({Type, InX::Got, Off, !Sym.IsPreemptible, &Sym, 0}, + Type, R_ABS); } // Return true if we can define a symbol in the executable that @@ -778,12 +780,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, Expr); // MIPS ABI turns using of GOT and dynamic relocations inside out. // While regular ABI uses dynamic relocations to fill up GOT entries @@ -978,8 +980,8 @@ // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf InX::MipsGot->addEntry(Sym, Addend, Expr); if (Sym.isTls() && Sym.IsPreemptible) - InX::RelaDyn->addReloc({Target->TlsGotRel, InX::MipsGot, - Sym.getGotOffset(), false, &Sym, 0}); + InX::RelaDyn->addReloc(Target->TlsGotRel, InX::MipsGot, + Sym.getGotOffset(), &Sym); } else if (!Sym.isInGot()) { addGotEntry(Sym); } Index: ELF/SyntheticSections.h =================================================================== --- ELF/SyntheticSections.h +++ ELF/SyntheticSections.h @@ -310,22 +310,30 @@ class DynamicReloc { public: - DynamicReloc(uint32_t Type, const InputSectionBase *InputSec, + DynamicReloc(RelType Type, const InputSectionBase *InputSec, uint64_t OffsetInSec, bool UseSymVA, Symbol *Sym, int64_t Addend) : Type(Type), Sym(Sym), InputSec(InputSec), OffsetInSec(OffsetInSec), UseSymVA(UseSymVA), Addend(Addend) {} uint64_t getOffset() const; - int64_t getAddend() const; + // Return the value that should be written to the Elf_Rela r_addend field. + // This is not the same as the addend used when creating this dynamic + // relocation since we may have converted a static relocation against a + // non-preemptible symbol into a relocation against the load address. In that + // case we write the virtual address of the symbol plus the addend into the + // r_addend field. + int64_t getRelaAddend() const; uint32_t getSymIndex() const; const InputSectionBase *getInputSec() const { return InputSec; } - uint32_t Type; - -private: + RelType Type; Symbol *Sym; const InputSectionBase *InputSec = nullptr; uint64_t OffsetInSec; + // If this member is true we add the dynamic relocation against the load + // address and write the symbol virtual address as the addend + // TODO: I found `UseSymVA` to be non-obvious as there are no comments, how + // about `IsAgainstLoadAddr`? bool UseSymVA; int64_t Addend; }; @@ -361,10 +369,21 @@ public: RelocationBaseSection(StringRef Name, uint32_t Type, int32_t DynamicTag, int32_t SizeDynamicTag); + // TODO: We could merge these two functions if we can assume that the + // dynamic relocation and the static one touch the same bits. This does not + // always seem to be the case as e.g. on MIPS32 a R_MIPS_64 will be converted + // to a R_MIPS_REL32, but this may just be a bug in the MIPS code. + + // Add a dynamic relocation that may have been converted to a relocation + // against the load address. In that case the addend will be the virtual + // address of the symbol and must be written to the input section in the + // case of REL output + void addReloc(const DynamicReloc &Reloc, RelType Type, RelExpr Expr); + // Add a dynamic relocation without an addend that will be processed by the + // the runtime linker. Since there is no addend this does the same for REL + // and RELA outputs 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); + uint64_t OffsetInSec, Symbol *Sym); 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 @@ -1184,7 +1184,7 @@ return InputSec->getOutputSection()->Addr + InputSec->getOffset(OffsetInSec); } -int64_t DynamicReloc::getAddend() const { +int64_t DynamicReloc::getRelaAddend() const { if (UseSymVA) return Sym->getVA(Addend); return Addend; @@ -1204,18 +1204,28 @@ void RelocationBaseSection::addReloc(uint32_t DynType, InputSectionBase *InputSec, - uint64_t OffsetInSec, bool UseSymVA, - Symbol *Sym, int64_t Addend, RelExpr Expr, - RelType Type) { + uint64_t OffsetInSec, Symbol *Sym) { + addReloc({DynType, InputSec, OffsetInSec, false, Sym, 0}, DynType, R_INVALID); +} + +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 we are adding a relocation against the load address we need to write + // the full virtual address as the addend, otherwise just the addend + Expr = Reloc.UseSymVA ? Expr : R_ADDEND; + // Otherwise we only need to write the addend to the input file. We can skip + // that if the addend is zero since it should already be 0 in the file. + // TODO: should we also write zero addends? Since this is the common case + // it will probably slow down adding dynamic relocations significantly and + // I can't think of a case where we would turn a non-zero addend into zero. + if (Reloc.UseSymVA || Reloc.Addend != 0) + IS->Relocations.push_back( + {Expr, Type, Reloc.OffsetInSec, Reloc.Addend, Reloc.Sym}); + } if (Reloc.Type == Target->RelativeRel) ++NumRelativeRelocs; Relocs.push_back(Reloc); @@ -1235,7 +1245,7 @@ static void encodeDynamicReloc(typename ELFT::Rela *P, const DynamicReloc &Rel) { if (Config->IsRela) - P->r_addend = Rel.getAddend(); + P->r_addend = Rel.getRelaAddend(); P->r_offset = Rel.getOffset(); if (Config->EMachine == EM_MIPS && Rel.getInputSec() == InX::MipsGot) // The MIPS GOT section contains dynamic relocations that correspond to TLS 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