Add comments for the RELRO segment.

RELRO is a feature to make segments read-only after dynamic relocations
are applied. It is different from read-only segments because RELRO is
initially writable. And of course RELRO is different from writable
segments.

RELRO is not a very well known feature. We have a series of checks to
make a decision whether a section should be in a RELRO segment or not,
but we didn't describe why. This patch adds comments to explain how
that decision is made.

llvm-svn: 300176
This commit is contained in:
Rui Ueyama 2017-04-13 05:40:07 +00:00
parent 4365d4e951
commit 9d773f3cb2
1 changed files with 42 additions and 4 deletions

View File

@ -586,25 +586,63 @@ template <class ELFT> bool elf::isRelroSection(const OutputSection *Sec) {
return false;
uint64_t Flags = Sec->Flags;
// Non-allocatable or non-writable sections don't need RELRO because
// they are not writable or not even mapped to memory in the first place.
// RELRO is for sections that are essentially read-only but need to
// be writable only at process startup to allow dynamic linker to
// apply relocations.
if (!(Flags & SHF_ALLOC) || !(Flags & SHF_WRITE))
return false;
// Once initialized, TLS data segments are used as data templates
// for a thread-local storage. For each new thread, runtime
// allocates memory for a TLS and copy templates there. No thread
// are supposed to use templates directly. Thus, it can be in RELRO.
if (Flags & SHF_TLS)
return true;
// .init_array, .preinit_array and .fini_array contain pointers to
// functions that are executed on process startup or exit. These
// pointers are set by the static linker, and they are not expected
// to change at runtime. But if you are an attacker, you could do
// interesting things by manipulating pointers in .fini_array, for
// example. So they are put into RELRO.
uint32_t Type = Sec->Type;
if (Type == SHT_INIT_ARRAY || Type == SHT_FINI_ARRAY ||
Type == SHT_PREINIT_ARRAY)
return true;
if (Sec == In<ELFT>::GotPlt->OutSec)
return Config->ZNow;
if (Sec == In<ELFT>::Dynamic->OutSec)
return true;
// .got contains pointers to external symbols. They are resolved by
// the dynamic linker when a module is loaded into memory, and after
// that they are not expected to change. So, it can be in RELRO.
if (In<ELFT>::Got && Sec == In<ELFT>::Got->OutSec)
return true;
// .got.plt contains pointers to external function symbols. They are
// by default resolved lazily, so we usually cannot put it into RELRO.
// However, if "-z now" is given, the lazy symbol resolution is
// disabled, which enables us to put it into RELRO.
if (Sec == In<ELFT>::GotPlt->OutSec)
return Config->ZNow;
// .dynamic section contains data for the dynamic linker, and
// there's no need to write to it at runtime, so it's better to put
// it into RELRO.
if (Sec == In<ELFT>::Dynamic->OutSec)
return true;
// .bss.rel.ro is used for copy relocations for read-only symbols.
// Since the dynamic linker needs to process copy relocations, the
// section cannot be read-only, but once initialized, they shouldn't
// change.
if (Sec == In<ELFT>::BssRelRo->OutSec)
return true;
// Sections with some special names are put into RELRO. This is a
// bit unfortunate because section names shouldn't be significant in
// ELF in spirit. But in reality many linker features depend on
// magic section names.
StringRef S = Sec->Name;
return S == ".data.rel.ro" || S == ".ctors" || S == ".dtors" || S == ".jcr" ||
S == ".eh_frame" || S == ".openbsd.randomdata";