forked from OSchip/llvm-project
[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
This commit is contained in:
parent
5e359a9a0e
commit
0264950697
|
@ -61,6 +61,9 @@ enum class Target2Policy { Abs, Rel, GotRel };
|
|||
// For tracking ARM Float Argument PCS
|
||||
enum class ARMVFPArgKind { Default, Base, VFP, ToolChain };
|
||||
|
||||
// For -z noseparate-code, -z separate-code and -z separate-loadable-segments.
|
||||
enum class SeparateSegmentKind { None, Code, Loadable };
|
||||
|
||||
struct SymbolVersion {
|
||||
llvm::StringRef name;
|
||||
bool isExternCpp;
|
||||
|
@ -209,7 +212,6 @@ struct Configuration {
|
|||
bool zOrigin;
|
||||
bool zRelro;
|
||||
bool zRodynamic;
|
||||
bool zSeparateCode;
|
||||
bool zText;
|
||||
bool zRetpolineplt;
|
||||
bool zWxneeded;
|
||||
|
@ -222,6 +224,7 @@ struct Configuration {
|
|||
Target2Policy target2;
|
||||
ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default;
|
||||
BuildIdKind buildId = BuildIdKind::None;
|
||||
SeparateSegmentKind zSeparate;
|
||||
ELFKind ekind = ELFNoneKind;
|
||||
uint16_t emachine = llvm::ELF::EM_NONE;
|
||||
llvm::Optional<uint64_t> imageBase;
|
||||
|
|
|
@ -380,16 +380,30 @@ static bool getZFlag(opt::InputArgList &args, StringRef k1, StringRef k2,
|
|||
return Default;
|
||||
}
|
||||
|
||||
static SeparateSegmentKind getZSeparate(opt::InputArgList &args) {
|
||||
for (auto *arg : args.filtered_reverse(OPT_z)) {
|
||||
StringRef v = arg->getValue();
|
||||
if (v == "noseparate-code")
|
||||
return SeparateSegmentKind::None;
|
||||
if (v == "separate-code")
|
||||
return SeparateSegmentKind::Code;
|
||||
if (v == "separate-loadable-segments")
|
||||
return SeparateSegmentKind::Loadable;
|
||||
}
|
||||
return SeparateSegmentKind::None;
|
||||
}
|
||||
|
||||
static bool isKnownZFlag(StringRef s) {
|
||||
return s == "combreloc" || s == "copyreloc" || s == "defs" ||
|
||||
s == "execstack" || s == "global" || s == "hazardplt" ||
|
||||
s == "ifunc-noplt" || s == "initfirst" || s == "interpose" ||
|
||||
s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" ||
|
||||
s == "separate-code" || s == "nocombreloc" || s == "nocopyreloc" ||
|
||||
s == "nodefaultlib" || s == "nodelete" || s == "nodlopen" ||
|
||||
s == "noexecstack" || s == "nokeep-text-section-prefix" ||
|
||||
s == "norelro" || s == "noseparate-code" || s == "notext" ||
|
||||
s == "now" || s == "origin" || s == "relro" || s == "retpolineplt" ||
|
||||
s == "separate-code" || s == "separate-loadable-segments" ||
|
||||
s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" ||
|
||||
s == "nodelete" || s == "nodlopen" || s == "noexecstack" ||
|
||||
s == "nokeep-text-section-prefix" || s == "norelro" ||
|
||||
s == "noseparate-code" || s == "notext" || s == "now" ||
|
||||
s == "origin" || s == "relro" || s == "retpolineplt" ||
|
||||
s == "rodynamic" || s == "text" || s == "undefs" || s == "wxneeded" ||
|
||||
s.startswith("common-page-size=") || s.startswith("max-page-size=") ||
|
||||
s.startswith("stack-size=");
|
||||
|
@ -950,7 +964,7 @@ static void readConfigs(opt::InputArgList &args) {
|
|||
config->zRelro = getZFlag(args, "relro", "norelro", true);
|
||||
config->zRetpolineplt = hasZOption(args, "retpolineplt");
|
||||
config->zRodynamic = hasZOption(args, "rodynamic");
|
||||
config->zSeparateCode = getZFlag(args, "separate-code", "noseparate-code", false);
|
||||
config->zSeparate = getZSeparate(args);
|
||||
config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0);
|
||||
config->zText = getZFlag(args, "text", "notext", true);
|
||||
config->zWxneeded = hasZOption(args, "wxneeded");
|
||||
|
|
|
@ -589,7 +589,8 @@ template <class ELFT> void Writer<ELFT>::run() {
|
|||
return;
|
||||
|
||||
if (!config->oFormatBinary) {
|
||||
writeTrapInstr();
|
||||
if (config->zSeparate != SeparateSegmentKind::None)
|
||||
writeTrapInstr();
|
||||
writeHeader();
|
||||
writeSections();
|
||||
} else {
|
||||
|
@ -2233,7 +2234,8 @@ template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
|
|||
// maximum page size boundary so that we can find the ELF header at the
|
||||
// start. We cannot benefit from overlapping p_offset ranges with the
|
||||
// previous segment anyway.
|
||||
if ((config->zSeparateCode && prev &&
|
||||
if (config->zSeparate == SeparateSegmentKind::Loadable ||
|
||||
(config->zSeparate == SeparateSegmentKind::Code && prev &&
|
||||
(prev->p_flags & PF_X) != (p->p_flags & PF_X)) ||
|
||||
cmd->type == SHT_LLVM_PART_EHDR)
|
||||
cmd->addrExpr = [] {
|
||||
|
@ -2342,7 +2344,8 @@ template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
|
|||
// If this is a last section of the last executable segment and that
|
||||
// segment is the last loadable segment, align the offset of the
|
||||
// following section to avoid loading non-segments parts of the file.
|
||||
if (config->zSeparateCode && lastRX && lastRX->lastSec == sec)
|
||||
if (config->zSeparate != SeparateSegmentKind::None && lastRX &&
|
||||
lastRX->lastSec == sec)
|
||||
off = alignTo(off, config->commonPageSize);
|
||||
}
|
||||
|
||||
|
@ -2614,9 +2617,6 @@ static void fillTrap(uint8_t *i, uint8_t *end) {
|
|||
// We'll leave other pages in segments as-is because the rest will be
|
||||
// overwritten by output sections.
|
||||
template <class ELFT> void Writer<ELFT>::writeTrapInstr() {
|
||||
if (!config->zSeparateCode)
|
||||
return;
|
||||
|
||||
for (Partition &part : partitions) {
|
||||
// Fill the last page.
|
||||
for (PhdrEntry *p : part.phdrs)
|
||||
|
|
|
@ -591,18 +591,21 @@ Force load of all members in a static library.
|
|||
Use wrapper functions for symbol.
|
||||
.It Fl z Ar option
|
||||
Linker option extensions.
|
||||
.Bl -tag -width indent
|
||||
.Bl -tag -width indent -compact
|
||||
.Pp
|
||||
.It Cm execstack
|
||||
Make the main stack executable.
|
||||
Stack permissions are recorded in the
|
||||
.Dv PT_GNU_STACK
|
||||
segment.
|
||||
.Pp
|
||||
.It Cm global
|
||||
Sets the
|
||||
.Dv DF_1_GLOBAL flag in the
|
||||
.Dv DYNAMIC
|
||||
section.
|
||||
Different loaders can decide how to handle this flag on their own.
|
||||
.Pp
|
||||
.It Cm ifunc-noplt
|
||||
Do not emit PLT entries for ifunc symbols.
|
||||
Instead, emit text relocations referencing the resolver.
|
||||
|
@ -611,64 +614,78 @@ environments where text relocations do not have the usual drawbacks.
|
|||
This option must be combined with the
|
||||
.Fl z Li notext
|
||||
option.
|
||||
.Pp
|
||||
.It Cm initfirst
|
||||
Sets the
|
||||
.Dv DF_1_INITFIRST
|
||||
flag to indicate the module should be initialized first.
|
||||
.Pp
|
||||
.It Cm interpose
|
||||
Set the
|
||||
.Dv DF_1_INTERPOSE
|
||||
flag to indicate to the runtime linker that the object is an interposer.
|
||||
During symbol resolution interposers are searched after the application
|
||||
but before other dependencies.
|
||||
.Pp
|
||||
.It Cm muldefs
|
||||
Do not error if a symbol is defined multiple times.
|
||||
The first definition will be used.
|
||||
This is a synonym for
|
||||
.Fl -allow-multiple-definition.
|
||||
.Pp
|
||||
.It Cm nocombreloc
|
||||
Disable combining and sorting multiple relocation sections.
|
||||
.Pp
|
||||
.It Cm nocopyreloc
|
||||
Disable the creation of copy relocations.
|
||||
.Pp
|
||||
.It Cm nodefaultlib
|
||||
Set the
|
||||
.Dv DF_1_NODEFLIB
|
||||
flag to indicate that default library search paths should be ignored.
|
||||
.Pp
|
||||
.It Cm nodelete
|
||||
Set the
|
||||
.Dv DF_1_NODELETE
|
||||
flag to indicate that the object cannot be unloaded from a process.
|
||||
.Pp
|
||||
.It Cm nodlopen
|
||||
Set the
|
||||
.Dv DF_1_NOOPEN
|
||||
flag to indicate that the object may not be opened by
|
||||
.Xr dlopen 3 .
|
||||
.Pp
|
||||
.It Cm norelro
|
||||
Do not indicate that portions of the object shold be mapped read-only
|
||||
after initial relocation processing.
|
||||
The object will omit the
|
||||
.Dv PT_GNU_RELRO
|
||||
segment.
|
||||
.Pp
|
||||
.It Cm notext
|
||||
Allow relocations against read-only segments.
|
||||
Sets the
|
||||
.Dv DT_TEXTREL flag in the
|
||||
.Dv DYNAMIC
|
||||
section.
|
||||
.Pp
|
||||
.It Cm now
|
||||
Set the
|
||||
.Dv DF_BIND_NOW
|
||||
flag to indicate that the run-time loader should perform all relocation
|
||||
processing as part of object initialization.
|
||||
By default relocations may be performed on demand.
|
||||
.Pp
|
||||
.It Cm origin
|
||||
Set the
|
||||
.Dv DF_ORIGIN
|
||||
flag to indicate that the object requires
|
||||
$ORIGIN
|
||||
processing.
|
||||
.Pp
|
||||
.It Cm retpolineplt
|
||||
Emit retpoline format PLT entries as a mitigation for CVE-2017-5715.
|
||||
.Pp
|
||||
.It Cm rodynamic
|
||||
Make the
|
||||
.Li .dynamic
|
||||
|
@ -676,6 +693,18 @@ section read-only.
|
|||
The
|
||||
.Dv DT_DEBUG
|
||||
tag will not be emitted.
|
||||
.Pp
|
||||
.It Cm separate-loadable-segments
|
||||
.It Cm separate-code
|
||||
.It Cm noseparate-code
|
||||
Specify whether two adjacent PT_LOAD segments are allowed to overlap in pages.
|
||||
.Cm noseparate-code
|
||||
(default) allows overlap.
|
||||
.Cm separate-code
|
||||
allows overlap between two executable segments, or two non-executable segments.
|
||||
.Cm separate-loadable-segments
|
||||
disallows overlap.
|
||||
.Pp
|
||||
.It Cm stack-size Ns = Ns Ar size
|
||||
Set the main thread's stack size to
|
||||
.Ar size .
|
||||
|
@ -683,9 +712,11 @@ The stack size is recorded as the size of the
|
|||
.Ar size .
|
||||
.Dv PT_GNU_STACK
|
||||
program segment.
|
||||
.Pp
|
||||
.It Cm text
|
||||
Do not allow relocations against read-only segments.
|
||||
This is the default.
|
||||
.Pp
|
||||
.It Cm wxneeded
|
||||
Create a
|
||||
.Dv PT_OPENBSD_WXNEEDED
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
# RUN: llvm-readobj -l %t | FileCheck %s --check-prefixes=CHECK,PAD
|
||||
# RUN: od -Ax -x -N16 -j0x1ff0 %t | FileCheck %s --check-prefix=FILL
|
||||
|
||||
## -z separate-loadable-segments pads all segments, including the text segment.
|
||||
# RUN: ld.lld %t.o -z separate-loadable-segments -o %t
|
||||
# RUN: llvm-readobj -l %t | FileCheck %s --check-prefixes=CHECK,PAD
|
||||
# RUN: od -Ax -x -N16 -j0x1ff0 %t | FileCheck %s --check-prefix=FILL
|
||||
|
||||
# RUN: ld.lld %t.o -z separate-code -z noseparate-code -o %t
|
||||
# RUN: llvm-readobj -l %t | FileCheck %s --check-prefixes=CHECK,NOPAD
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# REQUIRES: x86
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
|
||||
|
||||
## -z noseparate-code is the default. All PT_LOAD can have overlapping p_offset
|
||||
## ranges at runtime.
|
||||
# RUN: ld.lld -pie %t.o -o %t
|
||||
# RUN: llvm-readelf -l %t | FileCheck --check-prefix=NONE %s
|
||||
# NONE: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000245 0x000245 R 0x1000
|
||||
# NONE-NEXT: LOAD 0x000248 0x0000000000001248 0x0000000000001248 0x000001 0x000001 R E 0x1000
|
||||
# NONE-NEXT: LOAD 0x000250 0x0000000000002250 0x0000000000002250 0x000080 0x000080 RW 0x1000
|
||||
# NONE-NEXT: LOAD 0x0002d0 0x00000000000032d0 0x00000000000032d0 0x000001 0x000001 RW 0x1000
|
||||
|
||||
## -z separate-code makes text segment (RX) separate.
|
||||
## The two RW can have overlapping p_offset ranges at runtime.
|
||||
# RUN: ld.lld -pie %t.o -z separate-code -o %t
|
||||
# RUN: llvm-readelf -l %t | FileCheck --check-prefix=CODE %s
|
||||
# CODE: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000245 0x000245 R 0x1000
|
||||
# CODE-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000001 0x000001 R E 0x1000
|
||||
# CODE-NEXT: LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000080 0x000080 RW 0x1000
|
||||
# CODE-NEXT: LOAD 0x002080 0x0000000000003080 0x0000000000003080 0x000001 0x000001 RW 0x1000
|
||||
|
||||
## -z separate-loadable-segments makes all segments separate.
|
||||
# RUN: ld.lld -pie %t.o -z separate-loadable-segments -o %t
|
||||
# RUN: llvm-readelf -l %t | FileCheck --check-prefix=ALL %s
|
||||
# ALL: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000245 0x000245 R 0x1000
|
||||
# ALL-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000001 0x000001 R E 0x1000
|
||||
# ALL-NEXT: LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000080 0x000080 RW 0x1000
|
||||
# ALL-NEXT: LOAD 0x003000 0x0000000000003000 0x0000000000003000 0x000001 0x000001 RW 0x1000
|
||||
|
||||
nop
|
||||
|
||||
.data
|
||||
.byte 0
|
Loading…
Reference in New Issue