Index: lld/ELF/OutputSections.h =================================================================== --- lld/ELF/OutputSections.h +++ lld/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; @@ -253,6 +254,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/ELF/Relocations.cpp =================================================================== --- lld/ELF/Relocations.cpp +++ lld/ELF/Relocations.cpp @@ -399,22 +399,36 @@ return 1 << TrailingZeros; } -// Reserve space in .bss for copy relocation. +// 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; + typedef typename ELFT::Phdr Elf_Phdr; // Copy relocation against zero-sized symbol doesn't make sense. uintX_t SymSize = SS->template getSize(); if (SymSize == 0) fatal("cannot create a copy relocation for symbol " + toString(*SS)); - uintX_t Alignment = getAlignment(SS); - uintX_t Off = alignTo(Out::Bss->Size, Alignment); - Out::Bss->Size = Off + SymSize; - Out::Bss->updateAlignment(Alignment); + // Scan the DSO's program headers to 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, which will be covered by relro. uintX_t Shndx = SS->Sym.st_shndx; uintX_t Value = SS->Sym.st_value; + OutputSection *BssSec = Out::Bss; + 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) { + BssSec = Out::BssRelRo; + break; + } + } + + uintX_t Alignment = getAlignment(SS); + uintX_t Off = alignTo(BssSec->Size, Alignment); + BssSec->Size = Off + SymSize; + BssSec->updateAlignment(Alignment); // Look through the DSO's dynamic symbol table for aliases and create a // dynamic symbol for each one. This causes the copy relocation to correctly // interpose any aliases. @@ -426,11 +440,12 @@ if (!Alias) continue; Alias->OffsetInBss = Off; + Alias->IsInBssRelRo = (BssSec == Out::BssRelRo); Alias->NeedsCopyOrPltAddr = true; Alias->symbol()->IsUsedInRegularObj = true; } In::RelaDyn->addReloc( - {Target->CopyRel, Out::Bss, SS->OffsetInBss, false, SS, 0}); + {Target->CopyRel, BssSec, SS->OffsetInBss, false, SS, 0}); } template Index: lld/ELF/Symbols.h =================================================================== --- lld/ELF/Symbols.h +++ lld/ELF/Symbols.h @@ -16,6 +16,7 @@ #define LLD_ELF_SYMBOLS_H #include "InputSection.h" +#include "OutputSections.h" #include "Strings.h" #include "lld/Core/LLVM.h" @@ -30,8 +31,6 @@ class InputFile; class LazyObjectFile; template class ObjectFile; -template class OutputSection; -class OutputSectionBase; template class SharedFile; struct Symbol; @@ -123,6 +122,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 IsInBssRelRo : 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 @@ -283,6 +287,11 @@ // destination for callers of this Symbol. Thunk *ThunkData = nullptr; bool needsCopy() const { return this->NeedsCopyOrPltAddr && !this->isFunc(); } + + OutputSection *getBssSectionForCopy() const { + assert(needsCopy()); + return IsInBssRelRo ? Out::BssRelRo : Out::Bss; + } }; // This class represents a symbol defined in an archive file. It is Index: lld/ELF/Symbols.cpp =================================================================== --- lld/ELF/Symbols.cpp +++ lld/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.OffsetInBss; } 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), IsInBssRelRo(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. Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/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/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/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"; @@ -1074,6 +1078,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/test/ELF/Inputs/copy-rel-pie.s =================================================================== --- lld/test/ELF/Inputs/copy-rel-pie.s +++ lld/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/test/ELF/Inputs/relocation-copy-relro.s =================================================================== --- /dev/null +++ lld/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/test/ELF/relocation-copy-relro.s =================================================================== --- /dev/null +++ lld/test/ELF/relocation-copy-relro.s @@ -0,0 +1,33 @@ +// 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: Index: 7 +// CHECK-NEXT: 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