Index: lld/trunk/ELF/OutputSections.h =================================================================== --- lld/trunk/ELF/OutputSections.h +++ lld/trunk/ELF/OutputSections.h @@ -206,6 +206,7 @@ static uint8_t First; static EhOutputSection *EhFrame; static OutputSection *Bss; + static OutputSection *BssRelRo; static OutputSectionBase *Opd; static uint8_t *OpdBuf; static PhdrEntry *TlsPhdr; @@ -252,6 +253,7 @@ template uint8_t Out::First; template EhOutputSection *Out::EhFrame; template OutputSection *Out::Bss; +template OutputSection *Out::BssRelRo; template OutputSectionBase *Out::Opd; template uint8_t *Out::OpdBuf; template PhdrEntry *Out::TlsPhdr; Index: lld/trunk/ELF/Relocations.cpp =================================================================== --- lld/trunk/ELF/Relocations.cpp +++ lld/trunk/ELF/Relocations.cpp @@ -399,7 +399,21 @@ return 1 << TrailingZeros; } -// Reserve space in .bss for copy relocation. +template static bool isReadOnly(SharedSymbol *SS) { + typedef typename ELFT::uint uintX_t; + typedef typename ELFT::Phdr Elf_Phdr; + + // Determine if the symbol is read-only by scanning the DSO's program headers. + uintX_t Value = SS->Sym.st_value; + for (const Elf_Phdr &Phdr : check(SS->file()->getObj().program_headers())) + if ((Phdr.p_type == ELF::PT_LOAD || Phdr.p_type == ELF::PT_GNU_RELRO) && + !(Phdr.p_flags & ELF::PF_W) && Value >= Phdr.p_vaddr && + Value < Phdr.p_vaddr + Phdr.p_memsz) + return true; + return false; +} + +// Reserve space in .bss or .bss.rel.ro for copy relocation. template static void addCopyRelSymbol(SharedSymbol *SS) { typedef typename ELFT::uint uintX_t; typedef typename ELFT::Sym Elf_Sym; @@ -409,10 +423,16 @@ if (SymSize == 0) fatal("cannot create a copy relocation for symbol " + toString(*SS)); + // See if this symbol is in a read-only segment. If so, preserve the symbol's + // memory protection by reserving space in the .bss.rel.ro section. + bool IsReadOnly = isReadOnly(SS); + OutputSection *CopySec = + IsReadOnly ? Out::BssRelRo : Out::Bss; + uintX_t Alignment = getAlignment(SS); - uintX_t Off = alignTo(Out::Bss->Size, Alignment); - Out::Bss->Size = Off + SymSize; - Out::Bss->updateAlignment(Alignment); + uintX_t Off = alignTo(CopySec->Size, Alignment); + CopySec->Size = Off + SymSize; + CopySec->updateAlignment(Alignment); uintX_t Shndx = SS->Sym.st_shndx; uintX_t Value = SS->Sym.st_value; // Look through the DSO's dynamic symbol table for aliases and create a @@ -425,12 +445,12 @@ Symtab::X->find(check(S.getName(SS->file()->getStringTable())))); if (!Alias) continue; - Alias->OffsetInBss = Off; + Alias->CopyIsInBssRelRo = IsReadOnly; + Alias->CopyOffset = Off; Alias->NeedsCopyOrPltAddr = true; Alias->symbol()->IsUsedInRegularObj = true; } - In::RelaDyn->addReloc( - {Target->CopyRel, Out::Bss, SS->OffsetInBss, false, SS, 0}); + In::RelaDyn->addReloc({Target->CopyRel, CopySec, Off, false, SS, 0}); } template Index: lld/trunk/ELF/Symbols.h =================================================================== --- lld/trunk/ELF/Symbols.h +++ lld/trunk/ELF/Symbols.h @@ -123,6 +123,11 @@ // True if this symbol is in the Igot sub-section of the .got.plt or .got. unsigned IsInIgot : 1; + // True if this is a shared symbol in a read-only segment which requires a + // copy relocation. This causes space for the symbol to be allocated in the + // .bss.rel.ro section. + unsigned CopyIsInBssRelRo : 1; + // The following fields have the same meaning as the ELF symbol attributes. uint8_t Type; // symbol type uint8_t StOther; // st_other field value @@ -282,13 +287,15 @@ // This field is a pointer to the symbol's version definition. const Elf_Verdef *Verdef; - // OffsetInBss is significant only when needsCopy() is true. - uintX_t OffsetInBss = 0; + // CopyOffset is significant only when needsCopy() is true. + uintX_t CopyOffset = 0; // If non-null the symbol has a Thunk that may be used as an alternative // destination for callers of this Symbol. Thunk *ThunkData = nullptr; bool needsCopy() const { return this->NeedsCopyOrPltAddr && !this->isFunc(); } + + OutputSection *getBssSectionForCopy() const; }; // This class represents a symbol defined in an archive file. It is Index: lld/trunk/ELF/Symbols.cpp =================================================================== --- lld/trunk/ELF/Symbols.cpp +++ lld/trunk/ELF/Symbols.cpp @@ -81,7 +81,7 @@ return 0; if (SS.isFunc()) return Body.getPltVA(); - return Out::Bss->Addr + SS.OffsetInBss; + return SS.getBssSectionForCopy()->Addr + SS.CopyOffset; } case SymbolBody::UndefinedKind: return 0; @@ -97,7 +97,8 @@ uint8_t Type) : SymbolKind(K), NeedsCopyOrPltAddr(false), IsLocal(IsLocal), IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false), - IsInIgot(false), Type(Type), StOther(StOther), Name(Name) {} + IsInIgot(false), CopyIsInBssRelRo(false), Type(Type), StOther(StOther), + Name(Name) {} // Returns true if a symbol can be replaced at load-time by a symbol // with the same name defined in other ELF executable or DSO. @@ -245,6 +246,12 @@ this->File = File; } +template +OutputSection *SharedSymbol::getBssSectionForCopy() const { + assert(needsCopy()); + return CopyIsInBssRelRo ? Out::BssRelRo : Out::Bss; +} + DefinedCommon::DefinedCommon(StringRef Name, uint64_t Size, uint64_t Alignment, uint8_t StOther, uint8_t Type, InputFile *File) : Defined(SymbolBody::DefinedCommonKind, Name, /*IsLocal=*/false, StOther, @@ -366,6 +373,11 @@ template class elf::Undefined; template class elf::Undefined; +template class elf::SharedSymbol; +template class elf::SharedSymbol; +template class elf::SharedSymbol; +template class elf::SharedSymbol; + template class elf::DefinedRegular; template class elf::DefinedRegular; template class elf::DefinedRegular; Index: lld/trunk/ELF/SyntheticSections.cpp =================================================================== --- lld/trunk/ELF/SyntheticSections.cpp +++ lld/trunk/ELF/SyntheticSections.cpp @@ -1201,10 +1201,12 @@ } case SymbolBody::DefinedCommonKind: return In::Common->OutSec; - case SymbolBody::SharedKind: - if (cast>(Sym)->needsCopy()) - return Out::Bss; + case SymbolBody::SharedKind: { + auto &SS = cast>(*Sym); + if (SS.needsCopy()) + return SS.getBssSectionForCopy(); break; + } case SymbolBody::UndefinedKind: case SymbolBody::LazyArchiveKind: case SymbolBody::LazyObjectKind: Index: lld/trunk/ELF/Writer.cpp =================================================================== --- lld/trunk/ELF/Writer.cpp +++ lld/trunk/ELF/Writer.cpp @@ -250,6 +250,8 @@ // Create singleton output sections. Out::Bss = make>(".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE); + Out::BssRelRo = make>(".bss.rel.ro", SHT_NOBITS, + SHF_ALLOC | SHF_WRITE); In::DynStrTab = make>(".dynstr", true); In::Dynamic = make>(); Out::EhFrame = make>(); @@ -498,6 +500,8 @@ return true; if (In::MipsGot && Sec == In::MipsGot->OutSec) return true; + if (Sec == Out::BssRelRo) + return true; StringRef S = Sec->getName(); return S == ".data.rel.ro" || S == ".ctors" || S == ".dtors" || S == ".jcr" || S == ".eh_frame" || S == ".openbsd.randomdata"; @@ -1079,6 +1083,8 @@ template void Writer::addPredefinedSections() { if (Out::Bss->Size > 0) OutputSections.push_back(Out::Bss); + if (Out::BssRelRo->Size > 0) + OutputSections.push_back(Out::BssRelRo); auto OS = dyn_cast_or_null>(findSection(".ARM.exidx")); if (OS && !OS->Sections.empty() && !Config->Relocatable) Index: lld/trunk/test/ELF/Inputs/copy-rel-pie.s =================================================================== --- lld/trunk/test/ELF/Inputs/copy-rel-pie.s +++ lld/trunk/test/ELF/Inputs/copy-rel-pie.s @@ -1,9 +1,11 @@ +.data .global foo .type foo, @object .size foo, 4 foo: .long 0 +.text .global bar .type bar, @function bar: Index: lld/trunk/test/ELF/Inputs/relocation-copy-relro.s =================================================================== --- lld/trunk/test/ELF/Inputs/relocation-copy-relro.s +++ lld/trunk/test/ELF/Inputs/relocation-copy-relro.s @@ -0,0 +1,13 @@ +.rodata +.globl a +.size a, 4 +.type a, @object +a: +.word 1 + +.section .data.rel.ro,"aw",%progbits +.globl b +.size b, 4 +.type b, @object +b: +.word 2 Index: lld/trunk/test/ELF/relocation-copy-relro.s =================================================================== --- lld/trunk/test/ELF/relocation-copy-relro.s +++ lld/trunk/test/ELF/relocation-copy-relro.s @@ -0,0 +1,32 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/relocation-copy-relro.s -o %t2.o +// RUN: ld.lld -shared %t2.o -o %t.so +// RUN: ld.lld %t.o %t.so -o %t3 +// RUN: llvm-readobj -program-headers -s -r %t3 | FileCheck %s + +// CHECK: Name: .bss.rel.ro (48) +// CHECK-NEXT: Type: SHT_NOBITS (0x8) +// CHECK-NEXT: Flags [ (0x3) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: SHF_WRITE (0x1) +// CHECK-NEXT: ] +// CHECK-NEXT: Address: 0x2020B0 +// CHECK-NEXT: Offset: 0x20B0 +// CHECK-NEXT: Size: 8 + +// CHECK: 0x2020B0 R_X86_64_COPY a 0x0 +// CHECK: 0x2020B4 R_X86_64_COPY b 0x0 + +// CHECK: Type: PT_GNU_RELRO (0x6474E552) +// CHECK-NEXT: Offset: 0x2000 +// CHECK-NEXT: VirtualAddress: 0x202000 +// CHECK-NEXT: PhysicalAddress: 0x202000 +// CHECK-NEXT: FileSize: 176 +// CHECK-NEXT: MemSize: 4096 + +.text +.global _start +_start: +movl $1, a +movl $2, b