Index: ELF/Arch/X86_64.cpp =================================================================== --- ELF/Arch/X86_64.cpp +++ ELF/Arch/X86_64.cpp @@ -318,6 +318,7 @@ case R_X86_64_PC64: case R_X86_64_SIZE64: case R_X86_64_GOT64: + case R_X86_64_RELATIVE: write64le(Loc, Val); break; default: 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}); }; @@ -192,8 +191,9 @@ Config->Shared) { if (InX::Got->addDynTlsEntry(Sym)) { uint64_t Off = InX::Got->getGlobalDynOffset(Sym); - InX::RelaDyn->addReloc( - {Target->TlsDescRel, InX::Got, Off, !Sym.IsPreemptible, &Sym, 0}); + InX::RelaDyn->addComplexReloc( + {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. @@ -694,8 +693,9 @@ RelocationBaseSection *Rel, RelType Type, Symbol &Sym) { Plt->addEntry(Sym); GotPlt->addEntry(Sym); - Rel->addReloc( - {Type, GotPlt, Sym.getGotPltOffset(), !Sym.IsPreemptible, &Sym, 0}); + Rel->addComplexReloc( + {Type, GotPlt, Sym.getGotPltOffset(), !Sym.IsPreemptible, &Sym, 0}, Type, + R_ABS); } template static void addGotEntry(Symbol &Sym) { @@ -727,8 +727,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->addComplexReloc( + {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 +781,13 @@ bool IsPreemptibleValue = Sym.IsPreemptible && Expr != R_GOT; if (!IsPreemptibleValue) { - InX::RelaDyn->addReloc(Target->RelativeRel, &Sec, Offset, true, &Sym, - Addend, Expr, Type); + InX::RelaDyn->addComplexReloc( + {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}); + InX::RelaDyn->addComplexReloc( + {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 +982,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,29 @@ 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; uint32_t getSymIndex() const; const InputSectionBase *getInputSec() const { return InputSec; } - uint32_t Type; + // 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; -private: + RelType Type; Symbol *Sym; const InputSectionBase *InputSec = nullptr; uint64_t OffsetInSec; + // If this member is true we will add the dynamic relocation against the load + // address and use the symbol virtual address as the addend bool UseSymVA; int64_t Addend; }; @@ -361,10 +368,23 @@ 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); + // 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 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 *IS, uint64_t OffsetInSec, + Symbol *Sym); + + // 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 output if we are using + // REL output. + void addComplexReloc(const DynamicReloc &Reloc, RelType Type, RelExpr Expr); + bool empty() const override { return Relocs.empty(); } size_t getSize() const override { return Relocs.size() * this->Entsize; } size_t getRelativeRelocCount() const { return NumRelativeRelocs; } @@ -372,6 +392,8 @@ int32_t DynamicTag, SizeDynamicTag; protected: + // Add Reloc to the list of dynamic relocations and count NumRelativeRelocs + void addDynamicReloc(const DynamicReloc &Reloc); std::vector Relocs; size_t NumRelativeRelocs = 0; }; 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; @@ -1202,21 +1202,36 @@ : 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) { - // We store the addends for dynamic relocations for both REL and RELA - // relocations for compatibility with GNU Linkers. There is some system - // software such as the Bionic dynamic linker that uses the addend prior - // to dynamic relocation resolution. - if (Config->WriteAddends && UseSymVA) - InputSec->Relocations.push_back({Expr, Type, OffsetInSec, Addend, Sym}); - addReloc({DynType, InputSec, OffsetInSec, UseSymVA, Sym, Addend}); -} - -void RelocationBaseSection::addReloc(const DynamicReloc &Reloc) { +void RelocationBaseSection::addReloc(uint32_t DynType, InputSectionBase *IS, + uint64_t OffsetInSec, Symbol *Sym) { + addDynamicReloc({DynType, IS, OffsetInSec, false, Sym, 0}); +} + +void RelocationBaseSection::addComplexReloc(const DynamicReloc &Reloc, + RelType Type, RelExpr Expr) { + addDynamicReloc(Reloc); + + // If the output uses REL relocations we must store the dynamic relocation + // addends to the output sections. We also store addends for RELA relocations + // if --apply-dynamic-relocs is used (for compatibility with GNU linkers). + // However, we can't do this by default since there is some system software + // such as the Bionic dynamic linker that uses the addend from the target + // location prior to dynamic relocation resolution (even when processing RELA relocations). + if (Config->WriteAddends) { + 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. To avoid + // unnecessary vector appends and calls to Target->relocateOne() we can skip + // it if the addend is zero since it will already be zero in the file. + if (Reloc.UseSymVA || Reloc.Addend != 0) + IS->Relocations.push_back( + {Expr, Type, Reloc.OffsetInSec, Reloc.Addend, Reloc.Sym}); + } +} + +void RelocationBaseSection::addDynamicReloc(const DynamicReloc &Reloc) { if (Reloc.Type == Target->RelativeRel) ++NumRelativeRelocs; Relocs.push_back(Reloc); @@ -1236,7 +1251,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/rel-addend-with-rela-input.s =================================================================== --- /dev/null +++ test/ELF/rel-addend-with-rela-input.s @@ -0,0 +1,43 @@ +# REQUIRES: mips +# Check that we correctly write addends if the output use Elf_Rel but the input +# uses Elf_Rela + +# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t-rela.o +# RUN: llvm-readobj -h -s -section-data -relocations %t-rela.o | FileCheck -check-prefix INPUT-RELA %s +# INPUT-RELA: ElfHeader { +# INPUT-RELA: Class: 64-bit +# INPUT-RELA: DataEncoding: BigEndian +# INPUT-RELA: Section { +# INPUT-RELA: Name: .data +# INPUT-RELA: SectionData ( +# INPUT-RELA-NEXT: 0000: 00000000 00000000 ABCDEF00 12345678 |.............4Vx| +# ^--- No addend here since it uses RELA +# INPUT-RELA: Relocations [ +# INPUT-RELA-NEXT: Section ({{.+}}) .rela.data { +# INPUT-RELA-NEXT: 0x0 R_MIPS_64/R_MIPS_NONE/R_MIPS_NONE foo 0x5544 +# INPUT-RELA-NEXT: } +# INPUT-RELA-NEXT: ] + +# Previously the addend to the dynamic relocation in the .data section was not copied if +# the input file used RELA and the output uses REL. Check that it works now: +# RUN: ld.lld -shared -o %t.so %t-rela.o -verbose +# 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: 64-bit +# RELA-TO-REL: DataEncoding: BigEndian +# RELA-TO-REL: Section { +# RELA-TO-REL: Name: .data +# RELA-TO-REL: SectionData ( +# RELA-TO-REL-NEXT: 0000: 00000000 00005544 ABCDEF00 12345678 |......UD.....4Vx| +# ^--- Addend for relocation in .rel.dyn +# RELA-TO-REL: Relocations [ +# RELA-TO-REL-NEXT: Section ({{.+}}) .rel.dyn { +# RELA-TO-REL-NEXT: 0x10000 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE foo 0x0 +# RELA-TO-REL-NEXT: } +# RELA-TO-REL-NEXT: ] + +.extern foo + +.data +.quad foo + 0x5544 +.quad 0xabcdef0012345678