Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -37,6 +37,9 @@ // For --discard-{all,locals,none}. enum class DiscardPolicy { Default, All, Locals, None }; +// For --norosegment, --rosegment, --omagic. +enum class SegmentsPolicy { NoRoSegment, OMagic, RoSegment }; + // For --strip-{all,debug}. enum class StripPolicy { None, All, Debug }; @@ -136,6 +139,7 @@ bool ExitEarly; bool ZWxneeded; DiscardPolicy Discard; + SegmentsPolicy Segments; SortSectionPolicy SortSection; StripPolicy Strip = StripPolicy::None; UnresolvedPolicy UnresolvedSymbols; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -431,6 +431,15 @@ return Ret; } +static SegmentsPolicy getSegmentsOption(opt::InputArgList &Args) { + if (auto *Arg = Args.getLastArg(OPT_omagic, OPT_norosegment)) { + if (Arg->getOption().getID() == OPT_omagic) + return SegmentsPolicy::OMagic; + return SegmentsPolicy::NoRoSegment; + } + return SegmentsPolicy::RoSegment; +} + static SortSectionPolicy getSortKind(opt::InputArgList &Args) { StringRef S = getString(Args, OPT_sort_section); if (S == "alignment") @@ -537,6 +546,10 @@ Config->Target2 = getTarget2Option(Args); Config->UnresolvedSymbols = getUnresolvedSymbolOption(Args); + Config->Segments = getSegmentsOption(Args); + if (Config->Segments == SegmentsPolicy::OMagic) + Config->Static = true; + if (!Config->Relocatable) Config->Strip = getStripOption(Args); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -138,6 +138,8 @@ def nopie: F<"nopie">, HelpText<"Do not create a position independent executable">; +def norosegment: F<"norosegment">, HelpText<"Do not put read-only non-executable sections in their own segment">; + def no_undefined: F<"no-undefined">, HelpText<"Report unresolved symbols even if the linker is creating a shared library">; @@ -150,6 +152,9 @@ def oformat: Separate<["--"], "oformat">, MetaVarName<"">, HelpText<"Specify the binary format for the output object file">; +def omagic: F<"omagic">, MetaVarName<"">, + HelpText<"Set the text and data sections to be readable and writable">; + def pie: F<"pie">, HelpText<"Create a position independent executable">; def print_gc_sections: F<"print-gc-sections">, @@ -246,6 +251,7 @@ def alias_hash_style_hash_style: J<"hash-style=">, Alias; def alias_init_init: J<"init=">, Alias; def alias_l__library: J<"library=">, Alias; +def alias_omagic: F<"N">, Alias; def alias_o_output: Joined<["--"], "output=">, Alias; def alias_o_output2 : Separate<["--"], "output">, Alias; def alias_pie_pic_executable: F<"pic-executable">, Alias; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1095,6 +1095,18 @@ return F; } +// We apply -rosegment by default. -omagic can be used to create just a single +// PT_LOAD. -norosegment is used to avoid placing read only non-executable +// sections in their own segment. +template +static bool shouldEmitLoad(typename ELFT::uint Old, typename ELFT::uint New) { + if (Config->Segments == SegmentsPolicy::OMagic) + return false; + if (Config->Segments == SegmentsPolicy::NoRoSegment) + return (Old & PF_W) != (New & PF_W); + return Old != New; +} + // Decide which program headers to create and which sections to include in each // one. template std::vector> Writer::createPhdrs() { @@ -1141,13 +1153,16 @@ // Segments are contiguous memory regions that has the same attributes // (e.g. executable or writable). There is one phdr for each segment. - // Therefore, we need to create a new phdr when the next section has + // Therefore, usually we need to create a new phdr when the next section has // different flags or is loaded at a discontiguous address using AT linker - // script command. + // script command. This can be changed using -norosegment/-omagic options. uintX_t NewFlags = computeFlags(Sec->getPhdrFlags()); - if (Script::X->hasLMA(Sec->getName()) || Flags != NewFlags) { + if (Script::X->hasLMA(Sec->getName()) || + shouldEmitLoad(Flags, NewFlags)) { Load = AddHdr(PT_LOAD, NewFlags); Flags = NewFlags; + } else { + Load->H.p_flags |= NewFlags; } Load->add(Sec); Index: test/ELF/segments.s =================================================================== --- test/ELF/segments.s +++ test/ELF/segments.s @@ -0,0 +1,109 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t1 +# RUN: llvm-readobj --program-headers %t1 | FileCheck --check-prefix=ROSEGMENT %s + +# ROSEGMENT: ProgramHeader { +# ROSEGMENT: Type: PT_LOAD +# ROSEGMENT-NEXT: Offset: 0x0 +# ROSEGMENT-NEXT: VirtualAddress: 0x10000 +# ROSEGMENT-NEXT: PhysicalAddress: 0x10000 +# ROSEGMENT-NEXT: FileSize: +# ROSEGMENT-NEXT: MemSize: +# ROSEGMENT-NEXT: Flags [ +# ROSEGMENT-NEXT: PF_R +# ROSEGMENT-NEXT: ] +# ROSEGMENT-NEXT: Alignment: 4096 +# ROSEGMENT-NEXT: } +# ROSEGMENT-NEXT: ProgramHeader { +# ROSEGMENT-NEXT: Type: PT_LOAD +# ROSEGMENT-NEXT: Offset: 0x1000 +# ROSEGMENT-NEXT: VirtualAddress: 0x11000 +# ROSEGMENT-NEXT: PhysicalAddress: 0x11000 +# ROSEGMENT-NEXT: FileSize: +# ROSEGMENT-NEXT: MemSize: +# ROSEGMENT-NEXT: Flags [ +# ROSEGMENT-NEXT: PF_R +# ROSEGMENT-NEXT: PF_X +# ROSEGMENT-NEXT: ] +# ROSEGMENT-NEXT: Alignment: 4096 +# ROSEGMENT-NEXT: } +# ROSEGMENT-NEXT: ProgramHeader { +# ROSEGMENT-NEXT: Type: PT_LOAD +# ROSEGMENT-NEXT: Offset: 0x2000 +# ROSEGMENT-NEXT: VirtualAddress: 0x12000 +# ROSEGMENT-NEXT: PhysicalAddress: 0x12000 +# ROSEGMENT-NEXT: FileSize: 1 +# ROSEGMENT-NEXT: MemSize: 1 +# ROSEGMENT-NEXT: Flags [ +# ROSEGMENT-NEXT: PF_R +# ROSEGMENT-NEXT: PF_W +# ROSEGMENT-NEXT: ] +# ROSEGMENT-NEXT: Alignment: 4096 +# ROSEGMENT-NEXT: } + +# RUN: ld.lld -N %t -o %t2 +# RUN: llvm-readobj --program-headers %t2 | FileCheck --check-prefix=OMAGIC %s + +# OMAGIC: ProgramHeader { +# OMAGIC: Type: PT_LOAD +# OMAGIC-NEXT: Offset: 0x0 +# OMAGIC-NEXT: VirtualAddress: 0x10000 +# OMAGIC-NEXT: PhysicalAddress: 0x10000 +# OMAGIC-NEXT: FileSize: +# OMAGIC-NEXT: MemSize: +# OMAGIC-NEXT: Flags [ +# OMAGIC-NEXT: PF_R +# OMAGIC-NEXT: PF_W +# OMAGIC-NEXT: PF_X +# OMAGIC-NEXT: ] +# OMAGIC-NEXT: Alignment: 4096 +# OMAGIC-NEXT: } +# OMAGIC-NEXT: ProgramHeader { +# OMAGIC-NEXT: Type: PT_GNU_STACK + +# RUN: ld.lld -norosegment %t -o %t3 +# RUN: llvm-readobj --program-headers %t3 | FileCheck --check-prefix=NOROSEGMENT %s + +# NOROSEGMENT: ProgramHeader { +# NOROSEGMENT: Type: PT_LOAD +# NOROSEGMENT-NEXT: Offset: 0x0 +# NOROSEGMENT-NEXT: VirtualAddress: 0x10000 +# NOROSEGMENT-NEXT: PhysicalAddress: 0x10000 +# NOROSEGMENT-NEXT: FileSize: +# NOROSEGMENT-NEXT: MemSize: +# NOROSEGMENT-NEXT: Flags [ +# NOROSEGMENT-NEXT: PF_R +# NOROSEGMENT-NEXT: PF_X +# NOROSEGMENT-NEXT: ] +# NOROSEGMENT-NEXT: Alignment: 4096 +# NOROSEGMENT-NEXT: } +# NOROSEGMENT-NEXT: ProgramHeader { +# NOROSEGMENT-NEXT: Type: PT_LOAD +# NOROSEGMENT-NEXT: Offset: 0x1000 +# NOROSEGMENT-NEXT: VirtualAddress: 0x11000 +# NOROSEGMENT-NEXT: PhysicalAddress: 0x11000 +# NOROSEGMENT-NEXT: FileSize: +# NOROSEGMENT-NEXT: MemSize: +# NOROSEGMENT-NEXT: Flags [ +# NOROSEGMENT-NEXT: PF_R +# NOROSEGMENT-NEXT: PF_W +# NOROSEGMENT-NEXT: ] +# NOROSEGMENT-NEXT: Alignment: 4096 +# NOROSEGMENT-NEXT: } +# NOROSEGMENT-NEXT: ProgramHeader { +# NOROSEGMENT-NEXT: Type: PT_GNU_STACK +# NOROSEGMENT-NEXT: Offset: 0x0 + +.global _start +_start: + nop + +.section .ro,"a" +nop + +.section .rw,"aw" +nop + +.section .rx,"ax" +nop