[ELF] Add -z start-stop-gc to let __start_/__stop_ not retain C identifier name sections

For one metadata section usage, each text section references a metadata section.
The metadata sections have a C identifier name to allow the runtime to collect them via `__start_/__stop_` symbols.

Since `__start_`/`__stop_` references are always present from live sections, the
C identifier name sections appear like GC roots, which means they cannot be
discarded by `ld --gc-sections`.

To make such sections GCable, either SHF_LINK_ORDER or a section group is needed.

SHF_LINK_ORDER is not suitable for the references can be inlined into other functions
(See D97430:
Function A (in the section .text.A) references its `__sancov_guard` section.
Function B inlines A (so now .text.B references `__sancov_guard` - this is invalid with the semantics of SHF_LINK_ORDER).

In the linking stage,
if `.text.A` gets discarded, and `__sancov_guard` is retained via the reference from `.text.B`,
the output will be invalid because `__sancov_guard` references the discarded `.text.A`.
LLD errors "sh_link points to discarded section".
)

A section group have size overhead, and is cumbersome when there is just one metadata section.

Add `-z start-stop-gc` to drop the "__start_/__stop_ references retain
non-SHF_LINK_ORDER non-SHF_GROUP C identifier name sections" rule.
We reserve the rights to switch the default in the future.

Reviewed By: phosek, jrtc27

Differential Revision: https://reviews.llvm.org/D96914
This commit is contained in:
Fangrui Song 2021-02-25 15:46:37 -08:00
parent 6d102f15a3
commit 4bbcd63eea
5 changed files with 26 additions and 5 deletions

View File

@ -239,6 +239,7 @@ struct Configuration {
bool zRelro;
bool zRodynamic;
bool zShstk;
bool zStartStopGC;
uint8_t zStartStopVisibility;
bool zText;
bool zRetpolineplt;

View File

@ -451,10 +451,11 @@ static bool isKnownZFlag(StringRef s) {
s == "initfirst" || s == "interpose" ||
s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" ||
s == "separate-code" || s == "separate-loadable-segments" ||
s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" ||
s == "nodelete" || s == "nodlopen" || s == "noexecstack" ||
s == "nognustack" || s == "nokeep-text-section-prefix" ||
s == "norelro" || s == "noseparate-code" || s == "notext" ||
s == "start-stop-gc" || s == "nocombreloc" || s == "nocopyreloc" ||
s == "nodefaultlib" || s == "nodelete" || s == "nodlopen" ||
s == "noexecstack" || s == "nognustack" ||
s == "nokeep-text-section-prefix" || s == "norelro" ||
s == "noseparate-code" || s == "nostart-stop-gc" || s == "notext" ||
s == "now" || s == "origin" || s == "pac-plt" || s == "rel" ||
s == "rela" || s == "relro" || s == "retpolineplt" ||
s == "rodynamic" || s == "shstk" || s == "text" || s == "undefs" ||
@ -1119,6 +1120,8 @@ static void readConfigs(opt::InputArgList &args) {
config->zSeparate = getZSeparate(args);
config->zShstk = hasZOption(args, "shstk");
config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0);
config->zStartStopGC =
getZFlag(args, "start-stop-gc", "nostart-stop-gc", false);
config->zStartStopVisibility = getZStartStopVisibility(args);
config->zText = getZFlag(args, "text", "notext", true);
config->zWxneeded = hasZOption(args, "wxneeded");

View File

@ -270,7 +270,8 @@ template <class ELFT> void MarkLive<ELFT>::run() {
if (isReserved(sec) || script->shouldKeep(sec)) {
enqueue(sec, 0);
} else if (isValidCIdentifier(sec->name) && !sec->nextInSectionGroup) {
} else if (!config->zStartStopGC && isValidCIdentifier(sec->name) &&
!sec->nextInSectionGroup) {
cNamedSections[saver.save("__start_" + sec->name)].push_back(sec);
cNamedSections[saver.save("__stop_" + sec->name)].push_back(sec);
}

View File

@ -802,6 +802,8 @@ The stack size is recorded as the size of the
.Dv PT_GNU_STACK
program segment.
.Pp
.It Cm start-stop-gc
Don't let __start_/__stop_ references retain non-SHF_LINK_ORDER non-SHF_GROUP C identifier name sections.
.It Cm text
Do not allow relocations against read-only segments.
This is the default.

View File

@ -4,6 +4,13 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
# RUN: ld.lld --gc-sections %t.o -o %t
# RUN: llvm-objdump --section-headers -t %t | FileCheck %s
# RUN: ld.lld --gc-sections -z start-stop-gc -z nostart-stop-gc %t.o -o %t
# RUN: llvm-objdump --section-headers -t %t | FileCheck %s
## With -z start-stop-gc, non-SHF_LINK_ORDER non-SHF_GROUP C identifier name
## sections are not retained by __start_/__stop_ references.
# RUN: ld.lld --gc-sections -z start-stop-gc %t.o -o %t1
# RUN: llvm-readelf -S -s %t1 | FileCheck %s --check-prefix=GC
# CHECK: Sections:
# CHECK-NOT: yy
@ -14,6 +21,13 @@
# CHECK: xx 0000000000000000 .protected __start_xx
# CHECK: w *UND* 0000000000000000 __start_yy
# GC: Section Headers:
# GC-NOT: xx
# GC-NOT: yy
# GC: WEAK DEFAULT UND __start_xx
# GC: WEAK DEFAULT UND __start_yy
.weak __start_xx
.weak __start_yy