diff options
author | Fangrui Song <i@maskray.me> | 2022-05-04 11:10:45 +0300 |
---|---|---|
committer | Fangrui Song <i@maskray.me> | 2022-05-04 11:10:46 +0300 |
commit | 5a44980f0a8bb2c7dafe9a9f5e5a17699e65cc3d (patch) | |
tree | 294b93d09fed1ab21f0d669f0e7bff15b999092f /lld | |
parent | 37a147352457c690a6130a5d20ef381037fc3548 (diff) |
[ELF] Support custom sections between DATA_SEGMENT_ALIGN and DATA_SEGMENT_RELRO_END
We currently hard code RELRO sections. When a custom section is between
DATA_SEGMENT_ALIGN and DATA_SEGMENT_RELRO_END, we may report a spurious
`error: section: ... is not contiguous with other relro sections`. GNU ld
makes such sections RELRO.
glibc recently switched to default --with-default-link=no. This configuration
places `__libc_atexit` and others between DATA_SEGMENT_ALIGN and
DATA_SEGMENT_RELRO_END. This patch allows such a ld.bfd --verbose
linker script to be fed into lld.
Reviewed By: peter.smith
Differential Revision: https://reviews.llvm.org/D124656
Diffstat (limited to 'lld')
-rw-r--r-- | lld/ELF/OutputSections.h | 4 | ||||
-rw-r--r-- | lld/ELF/ScriptParser.cpp | 15 | ||||
-rw-r--r-- | lld/ELF/Writer.cpp | 2 | ||||
-rw-r--r-- | lld/test/ELF/linkerscript/data-segment-relro.test | 25 |
4 files changed, 39 insertions, 7 deletions
diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h index 9179d14aef07..020eeaec368e 100644 --- a/lld/ELF/OutputSections.h +++ b/lld/ELF/OutputSections.h @@ -100,6 +100,10 @@ public: // that wasn't needed). This is needed for orphan placement. bool hasInputSections = false; + // The output section description is specified between DATA_SEGMENT_ALIGN and + // DATA_RELRO_END. + bool relro = false; + void finalize(); template <class ELFT> void writeTo(uint8_t *buf); // Check that the addends for dynamic relocations were written correctly. diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp index c58309b12e57..51792da64b82 100644 --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -135,6 +135,9 @@ private: // True if a script being read is in the --sysroot directory. bool isUnderSysroot = false; + bool seenDataAlign = false; + bool seenRelroEnd = false; + // A set to detect an INCLUDE() cycle. StringSet<> seen; }; @@ -583,6 +586,14 @@ void ScriptParser::readSections() { else v.push_back(readOutputSectionDescription(tok)); } + + // If DATA_SEGMENT_RELRO_END is absent, for sections after DATA_SEGMENT_ALIGN, + // the relro fields should be cleared. + if (!seenRelroEnd) + for (SectionCommand *cmd : v) + if (auto *osd = dyn_cast<OutputDesc>(cmd)) + osd->osec.relro = false; + script->sectionCommands.insert(script->sectionCommands.end(), v.begin(), v.end()); @@ -887,6 +898,8 @@ OutputDesc *ScriptParser::readOverlaySectionDescription() { OutputDesc *ScriptParser::readOutputSectionDescription(StringRef outSec) { OutputDesc *cmd = script->createOutputSection(outSec, getCurrentLocation()); OutputSection *osec = &cmd->osec; + // Maybe relro. Will reset to false if DATA_SEGMENT_RELRO_END is absent. + osec->relro = seenDataAlign && !seenRelroEnd; size_t symbolsReferenced = script->referencedSymbols.size(); @@ -1358,6 +1371,7 @@ Expr ScriptParser::readPrimary() { expect(","); readExpr(); expect(")"); + seenDataAlign = true; return [=] { return alignTo(script->getDot(), std::max((uint64_t)1, e().getValue())); }; @@ -1377,6 +1391,7 @@ Expr ScriptParser::readPrimary() { expect(","); readExpr(); expect(")"); + seenRelroEnd = true; Expr e = getPageSize(); return [=] { return alignTo(script->getDot(), e().getValue()); }; } diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 125d7189a447..649958b74798 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -761,6 +761,8 @@ template <class ELFT> void Writer<ELFT>::addSectionSymbols() { static bool isRelroSection(const OutputSection *sec) { if (!config->zRelro) return false; + if (sec->relro) + return true; uint64_t flags = sec->flags; diff --git a/lld/test/ELF/linkerscript/data-segment-relro.test b/lld/test/ELF/linkerscript/data-segment-relro.test index bc24ad48d932..8aba8acb466f 100644 --- a/lld/test/ELF/linkerscript/data-segment-relro.test +++ b/lld/test/ELF/linkerscript/data-segment-relro.test @@ -15,10 +15,12 @@ # CHECK: Name Type Address Off Size ES Flg # CHECK-NEXT: NULL {{.*}} -# CHECK: .dynamic DYNAMIC 0000000000001000 001000 0000f0 10 WA -# CHECK-NEXT: .got PROGBITS 00000000000010f0 0010f0 000008 00 WA -# CHECK-NEXT: __libc_atexit PROGBITS 0000000000002000 002000 000008 00 WA -# CHECK-NEXT: .got.plt PROGBITS 0000000000002008 002008 000020 00 WA +# CHECK: .orphan.ro PROGBITS {{.*}} A +# CHECK: .dynamic DYNAMIC {{.*}} WA +# CHECK-NEXT: __libc_atexit PROGBITS {{.*}} WA +# CHECK-NEXT: .got PROGBITS {{.*}} WA +# CHECK-NEXT: .got.plt PROGBITS {{.*}} WA +# CHECK: .orphan.rw PROGBITS {{.*}} WA # CHECK1: Program Headers: # CHECK1-NOT: GNU_RELRO @@ -35,10 +37,12 @@ # CHECK2-NEXT: GNU_STACK # CHECK2: Section to Segment mapping: -# CHECK2: 06 .dynamic .got {{$}} +# CHECK2: 06 .dynamic __libc_atexit .got {{$}} # RUN: sed '/DATA_SEGMENT_RELRO_END/d' %t/1.t > %t/2.t -# RUN: ld.lld %t/a.o %t/b.so -T %t/2.t -o /dev/null +# RUN: not ld.lld %t/a.o %t/b.so -T %t/2.t -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR + +# ERR: error: section: .got is not contiguous with other relro sections #--- a.s .global _start @@ -56,6 +60,12 @@ _start: .section __libc_atexit,"aw",@progbits .dc.a __libc_atexit +.section .orphan.ro,"a",@progbits +.dc.a 0 + +.section .orphan.rw,"aw",@progbits +.dc.a .orphan.rw + #--- 1.t SECTIONS { . = SIZEOF_HEADERS; @@ -66,11 +76,12 @@ SECTIONS { . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); .dynamic : { *(.dynamic) } + ## The custom section __libc_atexit is made relro. + __libc_atexit : { *(__libc_atexit) } .got : { *(.got) } . = DATA_SEGMENT_RELRO_END (1 ? 24 : 0, .); - __libc_atexit : { *(__libc_atexit) } .got.plt : { *(.got.plt) } .data : { *(.data) } .bss : { *(.bss) } |