mirror of
https://github.com/intel/llvm.git
synced 2026-01-19 09:31:59 +08:00
[ELF] Align the end of PT_GNU_RELRO associated PT_LOAD to a common-page-size boundary (#66042)
Close #57618: currently we align the end of PT_GNU_RELRO to a common-page-size boundary, but do not align the end of the associated PT_LOAD. This is benign when runtime_page_size >= common-page-size. However, when runtime_page_size < common-page-size, it is possible that `alignUp(end(PT_LOAD), page_size) < alignDown(end(PT_GNU_RELRO), page_size)`. In this case, rtld's mprotect call for PT_GNU_RELRO will apply to unmapped regions and lead to an error, e.g. ``` error while loading shared libraries: cannot apply additional memory protection after relocation: Cannot allocate memory ``` To fix the issue, add a padding section .relro_padding like mold, which is contained in the PT_GNU_RELRO segment and the associated PT_LOAD segment. The section also prevents strip from corrupting PT_LOAD program headers. .relro_padding has the largest `sortRank` among RELRO sections. Therefore, it is naturally placed at the end of `PT_GNU_RELRO` segment in the absence of `PHDRS`/`SECTIONS` commands. In the presence of `SECTIONS` commands, we place .relro_padding immediately before a symbol assignment using DATA_SEGMENT_RELRO_END (see also https://reviews.llvm.org/D124656), if present. DATA_SEGMENT_RELRO_END is changed to align to max-page-size instead of common-page-size. Some edge cases worth mentioning: * ppc64-toc-addis-nop.s: when PHDRS is present, do not append .relro_padding * avoid-empty-program-headers.s: when the only RELRO section is .tbss, it is not part of PT_LOAD segment, therefore we do not append .relro_padding. --- Close #65002: GNU ld from 2.39 onwards aligns the end of PT_GNU_RELRO to a max-page-size boundary (https://sourceware.org/PR28824) so that the last page is protected even if runtime_page_size > common-page-size. In my opinion, losing protection for the last page when the runtime page size is larger than common-page-size is not really an issue. Double mapping a page of up to max-common-page for the protection could cause undesired VM waste. Internally we had users complaining about 2MiB max-page-size applying to shared objects. Therefore, the end of .relro_padding is padded to a common-page-size boundary. Users who are really anxious can set common-page-size to match their runtime page size. --- 17 tests need updating as there are lots of change detectors.
This commit is contained in:
@@ -887,6 +887,10 @@ void LinkerScript::diagnoseOrphanHandling() const {
|
||||
if (config->orphanHandling == OrphanHandlingPolicy::Place)
|
||||
return;
|
||||
for (const InputSectionBase *sec : orphanSections) {
|
||||
// .relro_padding is inserted before DATA_SEGMENT_RELRO_END, if present,
|
||||
// automatically. The section is not supposed to be specified by scripts.
|
||||
if (sec == in.relroPadding.get())
|
||||
continue;
|
||||
// Input SHT_REL[A] retained by --emit-relocs are ignored by
|
||||
// computeInputSections(). Don't warn/error.
|
||||
if (isa<InputSection>(sec) &&
|
||||
@@ -1079,6 +1083,11 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
|
||||
}
|
||||
}
|
||||
|
||||
// If .relro_padding is present, round up the end to a common-page-size
|
||||
// boundary to protect the last page.
|
||||
if (in.relroPadding && sec == in.relroPadding->getParent())
|
||||
expandOutputSection(alignToPowerOf2(dot, config->commonPageSize) - dot);
|
||||
|
||||
// Non-SHF_ALLOC sections do not affect the addresses of other OutputSections
|
||||
// as they are not part of the process image.
|
||||
if (!(sec->flags & SHF_ALLOC)) {
|
||||
@@ -1160,6 +1169,7 @@ void LinkerScript::adjustOutputSections() {
|
||||
uint64_t flags = SHF_ALLOC;
|
||||
|
||||
SmallVector<StringRef, 0> defPhdrs;
|
||||
bool seenRelro = false;
|
||||
for (SectionCommand *&cmd : sectionCommands) {
|
||||
if (!isa<OutputDesc>(cmd))
|
||||
continue;
|
||||
@@ -1196,9 +1206,17 @@ void LinkerScript::adjustOutputSections() {
|
||||
if (sec->sectionIndex != UINT32_MAX)
|
||||
maybePropagatePhdrs(*sec, defPhdrs);
|
||||
|
||||
// Discard .relro_padding if we have not seen one RELRO section. Note: when
|
||||
// .tbss is the only RELRO section, there is no associated PT_LOAD segment
|
||||
// (needsPtLoad), so we don't append .relro_padding in the case.
|
||||
if (in.relroPadding && in.relroPadding->getParent() == sec && !seenRelro)
|
||||
discardable = true;
|
||||
if (discardable) {
|
||||
sec->markDead();
|
||||
cmd = nullptr;
|
||||
} else {
|
||||
seenRelro |=
|
||||
sec->relro && !(sec->type == SHT_NOBITS && (sec->flags & SHF_TLS));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user