Skip to content

Commit 0264950

Browse files
committed
[ELF] Add -z separate-loadable-segments to complement separate-code and noseparate-code
D64906 allows PT_LOAD to have overlapping p_offset ranges. In the default R RX RW RW layout + -z noseparate-code case, we do not tail pad segments when transiting to another segment. This can save at most 3*maxPageSize bytes. a) Before D64906, we tail pad R, RX and the first RW. b) With -z separate-code, we tail pad R and RX, but not the first RW (RELRO). In some cases, b) saves one file page. In some cases, b) wastes one virtual memory page. The waste is a concern on Fuchsia. Because it uses compressed binaries, it doesn't benefit from the saved file page. This patch adds -z separate-loadable-segments to restore the behavior before D64906. It can affect section addresses and can thus be used as a debugging mechanism (see PR43214 and ld.so partition bug in crbug.com/998712). Reviewed By: jakehehrlich, ruiu Differential Revision: https://reviews.llvm.org/D67481 llvm-svn: 372807
1 parent 5e359a9 commit 0264950

File tree

6 files changed

+100
-14
lines changed

6 files changed

+100
-14
lines changed

lld/ELF/Config.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ enum class Target2Policy { Abs, Rel, GotRel };
6161
// For tracking ARM Float Argument PCS
6262
enum class ARMVFPArgKind { Default, Base, VFP, ToolChain };
6363

64+
// For -z noseparate-code, -z separate-code and -z separate-loadable-segments.
65+
enum class SeparateSegmentKind { None, Code, Loadable };
66+
6467
struct SymbolVersion {
6568
llvm::StringRef name;
6669
bool isExternCpp;
@@ -209,7 +212,6 @@ struct Configuration {
209212
bool zOrigin;
210213
bool zRelro;
211214
bool zRodynamic;
212-
bool zSeparateCode;
213215
bool zText;
214216
bool zRetpolineplt;
215217
bool zWxneeded;
@@ -222,6 +224,7 @@ struct Configuration {
222224
Target2Policy target2;
223225
ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default;
224226
BuildIdKind buildId = BuildIdKind::None;
227+
SeparateSegmentKind zSeparate;
225228
ELFKind ekind = ELFNoneKind;
226229
uint16_t emachine = llvm::ELF::EM_NONE;
227230
llvm::Optional<uint64_t> imageBase;

lld/ELF/Driver.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -380,16 +380,30 @@ static bool getZFlag(opt::InputArgList &args, StringRef k1, StringRef k2,
380380
return Default;
381381
}
382382

383+
static SeparateSegmentKind getZSeparate(opt::InputArgList &args) {
384+
for (auto *arg : args.filtered_reverse(OPT_z)) {
385+
StringRef v = arg->getValue();
386+
if (v == "noseparate-code")
387+
return SeparateSegmentKind::None;
388+
if (v == "separate-code")
389+
return SeparateSegmentKind::Code;
390+
if (v == "separate-loadable-segments")
391+
return SeparateSegmentKind::Loadable;
392+
}
393+
return SeparateSegmentKind::None;
394+
}
395+
383396
static bool isKnownZFlag(StringRef s) {
384397
return s == "combreloc" || s == "copyreloc" || s == "defs" ||
385398
s == "execstack" || s == "global" || s == "hazardplt" ||
386399
s == "ifunc-noplt" || s == "initfirst" || s == "interpose" ||
387400
s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" ||
388-
s == "separate-code" || s == "nocombreloc" || s == "nocopyreloc" ||
389-
s == "nodefaultlib" || s == "nodelete" || s == "nodlopen" ||
390-
s == "noexecstack" || s == "nokeep-text-section-prefix" ||
391-
s == "norelro" || s == "noseparate-code" || s == "notext" ||
392-
s == "now" || s == "origin" || s == "relro" || s == "retpolineplt" ||
401+
s == "separate-code" || s == "separate-loadable-segments" ||
402+
s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" ||
403+
s == "nodelete" || s == "nodlopen" || s == "noexecstack" ||
404+
s == "nokeep-text-section-prefix" || s == "norelro" ||
405+
s == "noseparate-code" || s == "notext" || s == "now" ||
406+
s == "origin" || s == "relro" || s == "retpolineplt" ||
393407
s == "rodynamic" || s == "text" || s == "undefs" || s == "wxneeded" ||
394408
s.startswith("common-page-size=") || s.startswith("max-page-size=") ||
395409
s.startswith("stack-size=");
@@ -950,7 +964,7 @@ static void readConfigs(opt::InputArgList &args) {
950964
config->zRelro = getZFlag(args, "relro", "norelro", true);
951965
config->zRetpolineplt = hasZOption(args, "retpolineplt");
952966
config->zRodynamic = hasZOption(args, "rodynamic");
953-
config->zSeparateCode = getZFlag(args, "separate-code", "noseparate-code", false);
967+
config->zSeparate = getZSeparate(args);
954968
config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0);
955969
config->zText = getZFlag(args, "text", "notext", true);
956970
config->zWxneeded = hasZOption(args, "wxneeded");

lld/ELF/Writer.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,8 @@ template <class ELFT> void Writer<ELFT>::run() {
589589
return;
590590

591591
if (!config->oFormatBinary) {
592-
writeTrapInstr();
592+
if (config->zSeparate != SeparateSegmentKind::None)
593+
writeTrapInstr();
593594
writeHeader();
594595
writeSections();
595596
} else {
@@ -2233,7 +2234,8 @@ template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
22332234
// maximum page size boundary so that we can find the ELF header at the
22342235
// start. We cannot benefit from overlapping p_offset ranges with the
22352236
// previous segment anyway.
2236-
if ((config->zSeparateCode && prev &&
2237+
if (config->zSeparate == SeparateSegmentKind::Loadable ||
2238+
(config->zSeparate == SeparateSegmentKind::Code && prev &&
22372239
(prev->p_flags & PF_X) != (p->p_flags & PF_X)) ||
22382240
cmd->type == SHT_LLVM_PART_EHDR)
22392241
cmd->addrExpr = [] {
@@ -2342,7 +2344,8 @@ template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
23422344
// If this is a last section of the last executable segment and that
23432345
// segment is the last loadable segment, align the offset of the
23442346
// following section to avoid loading non-segments parts of the file.
2345-
if (config->zSeparateCode && lastRX && lastRX->lastSec == sec)
2347+
if (config->zSeparate != SeparateSegmentKind::None && lastRX &&
2348+
lastRX->lastSec == sec)
23462349
off = alignTo(off, config->commonPageSize);
23472350
}
23482351

@@ -2614,9 +2617,6 @@ static void fillTrap(uint8_t *i, uint8_t *end) {
26142617
// We'll leave other pages in segments as-is because the rest will be
26152618
// overwritten by output sections.
26162619
template <class ELFT> void Writer<ELFT>::writeTrapInstr() {
2617-
if (!config->zSeparateCode)
2618-
return;
2619-
26202620
for (Partition &part : partitions) {
26212621
// Fill the last page.
26222622
for (PhdrEntry *p : part.phdrs)

lld/docs/ld.lld.1

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,18 +591,21 @@ Force load of all members in a static library.
591591
Use wrapper functions for symbol.
592592
.It Fl z Ar option
593593
Linker option extensions.
594-
.Bl -tag -width indent
594+
.Bl -tag -width indent -compact
595+
.Pp
595596
.It Cm execstack
596597
Make the main stack executable.
597598
Stack permissions are recorded in the
598599
.Dv PT_GNU_STACK
599600
segment.
601+
.Pp
600602
.It Cm global
601603
Sets the
602604
.Dv DF_1_GLOBAL flag in the
603605
.Dv DYNAMIC
604606
section.
605607
Different loaders can decide how to handle this flag on their own.
608+
.Pp
606609
.It Cm ifunc-noplt
607610
Do not emit PLT entries for ifunc symbols.
608611
Instead, emit text relocations referencing the resolver.
@@ -611,81 +614,109 @@ environments where text relocations do not have the usual drawbacks.
611614
This option must be combined with the
612615
.Fl z Li notext
613616
option.
617+
.Pp
614618
.It Cm initfirst
615619
Sets the
616620
.Dv DF_1_INITFIRST
617621
flag to indicate the module should be initialized first.
622+
.Pp
618623
.It Cm interpose
619624
Set the
620625
.Dv DF_1_INTERPOSE
621626
flag to indicate to the runtime linker that the object is an interposer.
622627
During symbol resolution interposers are searched after the application
623628
but before other dependencies.
629+
.Pp
624630
.It Cm muldefs
625631
Do not error if a symbol is defined multiple times.
626632
The first definition will be used.
627633
This is a synonym for
628634
.Fl -allow-multiple-definition.
635+
.Pp
629636
.It Cm nocombreloc
630637
Disable combining and sorting multiple relocation sections.
638+
.Pp
631639
.It Cm nocopyreloc
632640
Disable the creation of copy relocations.
641+
.Pp
633642
.It Cm nodefaultlib
634643
Set the
635644
.Dv DF_1_NODEFLIB
636645
flag to indicate that default library search paths should be ignored.
646+
.Pp
637647
.It Cm nodelete
638648
Set the
639649
.Dv DF_1_NODELETE
640650
flag to indicate that the object cannot be unloaded from a process.
651+
.Pp
641652
.It Cm nodlopen
642653
Set the
643654
.Dv DF_1_NOOPEN
644655
flag to indicate that the object may not be opened by
645656
.Xr dlopen 3 .
657+
.Pp
646658
.It Cm norelro
647659
Do not indicate that portions of the object shold be mapped read-only
648660
after initial relocation processing.
649661
The object will omit the
650662
.Dv PT_GNU_RELRO
651663
segment.
664+
.Pp
652665
.It Cm notext
653666
Allow relocations against read-only segments.
654667
Sets the
655668
.Dv DT_TEXTREL flag in the
656669
.Dv DYNAMIC
657670
section.
671+
.Pp
658672
.It Cm now
659673
Set the
660674
.Dv DF_BIND_NOW
661675
flag to indicate that the run-time loader should perform all relocation
662676
processing as part of object initialization.
663677
By default relocations may be performed on demand.
678+
.Pp
664679
.It Cm origin
665680
Set the
666681
.Dv DF_ORIGIN
667682
flag to indicate that the object requires
668683
$ORIGIN
669684
processing.
685+
.Pp
670686
.It Cm retpolineplt
671687
Emit retpoline format PLT entries as a mitigation for CVE-2017-5715.
688+
.Pp
672689
.It Cm rodynamic
673690
Make the
674691
.Li .dynamic
675692
section read-only.
676693
The
677694
.Dv DT_DEBUG
678695
tag will not be emitted.
696+
.Pp
697+
.It Cm separate-loadable-segments
698+
.It Cm separate-code
699+
.It Cm noseparate-code
700+
Specify whether two adjacent PT_LOAD segments are allowed to overlap in pages.
701+
.Cm noseparate-code
702+
(default) allows overlap.
703+
.Cm separate-code
704+
allows overlap between two executable segments, or two non-executable segments.
705+
.Cm separate-loadable-segments
706+
disallows overlap.
707+
.Pp
679708
.It Cm stack-size Ns = Ns Ar size
680709
Set the main thread's stack size to
681710
.Ar size .
682711
The stack size is recorded as the size of the
683712
.Ar size .
684713
.Dv PT_GNU_STACK
685714
program segment.
715+
.Pp
686716
.It Cm text
687717
Do not allow relocations against read-only segments.
688718
This is the default.
719+
.Pp
689720
.It Cm wxneeded
690721
Create a
691722
.Dv PT_OPENBSD_WXNEEDED

lld/test/ELF/fill-trap.s

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
# RUN: llvm-readobj -l %t | FileCheck %s --check-prefixes=CHECK,PAD
1313
# RUN: od -Ax -x -N16 -j0x1ff0 %t | FileCheck %s --check-prefix=FILL
1414

15+
## -z separate-loadable-segments pads all segments, including the text segment.
16+
# RUN: ld.lld %t.o -z separate-loadable-segments -o %t
17+
# RUN: llvm-readobj -l %t | FileCheck %s --check-prefixes=CHECK,PAD
18+
# RUN: od -Ax -x -N16 -j0x1ff0 %t | FileCheck %s --check-prefix=FILL
19+
1520
# RUN: ld.lld %t.o -z separate-code -z noseparate-code -o %t
1621
# RUN: llvm-readobj -l %t | FileCheck %s --check-prefixes=CHECK,NOPAD
1722

lld/test/ELF/separate-segments.s

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# REQUIRES: x86
2+
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
3+
4+
## -z noseparate-code is the default. All PT_LOAD can have overlapping p_offset
5+
## ranges at runtime.
6+
# RUN: ld.lld -pie %t.o -o %t
7+
# RUN: llvm-readelf -l %t | FileCheck --check-prefix=NONE %s
8+
# NONE: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000245 0x000245 R 0x1000
9+
# NONE-NEXT: LOAD 0x000248 0x0000000000001248 0x0000000000001248 0x000001 0x000001 R E 0x1000
10+
# NONE-NEXT: LOAD 0x000250 0x0000000000002250 0x0000000000002250 0x000080 0x000080 RW 0x1000
11+
# NONE-NEXT: LOAD 0x0002d0 0x00000000000032d0 0x00000000000032d0 0x000001 0x000001 RW 0x1000
12+
13+
## -z separate-code makes text segment (RX) separate.
14+
## The two RW can have overlapping p_offset ranges at runtime.
15+
# RUN: ld.lld -pie %t.o -z separate-code -o %t
16+
# RUN: llvm-readelf -l %t | FileCheck --check-prefix=CODE %s
17+
# CODE: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000245 0x000245 R 0x1000
18+
# CODE-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000001 0x000001 R E 0x1000
19+
# CODE-NEXT: LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000080 0x000080 RW 0x1000
20+
# CODE-NEXT: LOAD 0x002080 0x0000000000003080 0x0000000000003080 0x000001 0x000001 RW 0x1000
21+
22+
## -z separate-loadable-segments makes all segments separate.
23+
# RUN: ld.lld -pie %t.o -z separate-loadable-segments -o %t
24+
# RUN: llvm-readelf -l %t | FileCheck --check-prefix=ALL %s
25+
# ALL: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000245 0x000245 R 0x1000
26+
# ALL-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000001 0x000001 R E 0x1000
27+
# ALL-NEXT: LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000080 0x000080 RW 0x1000
28+
# ALL-NEXT: LOAD 0x003000 0x0000000000003000 0x0000000000003000 0x000001 0x000001 RW 0x1000
29+
30+
nop
31+
32+
.data
33+
.byte 0

0 commit comments

Comments
 (0)