Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -193,6 +193,7 @@ bool ZNodefaultlib; bool ZNodelete; bool ZNodlopen; + bool ZNognustack; bool ZNow; bool ZOrigin; bool ZRelro; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -352,6 +352,7 @@ S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" || S == "nocombreloc" || S == "nocopyreloc" || S == "nodefaultlib" || S == "nodelete" || S == "nodlopen" || S == "noexecstack" || + S == "nognustack" || S == "nokeep-text-section-prefix" || S == "norelro" || S == "notext" || S == "now" || S == "origin" || S == "relro" || S == "retpolineplt" || S == "rodynamic" || S == "text" || S == "wxneeded" || @@ -870,6 +871,7 @@ Config->ZCopyreloc = getZFlag(Args, "copyreloc", "nocopyreloc", true); Config->ZExecstack = getZFlag(Args, "execstack", "noexecstack", false); Config->ZGlobal = hasZOption(Args, "global"); + Config->ZNognustack = hasZOption(Args, "nognustack"); Config->ZHazardplt = hasZOption(Args, "hazardplt"); Config->ZInitfirst = hasZOption(Args, "initfirst"); Config->ZInterpose = hasZOption(Args, "interpose"); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1976,14 +1976,16 @@ if (OutputSection *Cmd = findSection(".openbsd.randomdata")) AddHdr(PT_OPENBSD_RANDOMIZE, Cmd->getPhdrFlags())->add(Cmd); - // PT_GNU_STACK is a special section to tell the loader to make the - // pages for the stack non-executable. If you really want an executable - // stack, you can pass -z execstack, but that's not recommended for - // security reasons. - unsigned Perm = PF_R | PF_W; - if (Config->ZExecstack) - Perm |= PF_X; - AddHdr(PT_GNU_STACK, Perm)->p_memsz = Config->ZStackSize; + if (!Config->ZNognustack) { + // PT_GNU_STACK is a special section to tell the loader to make the + // pages for the stack non-executable. If you really want an executable + // stack, you can pass -z execstack, but that's not recommended for + // security reasons. + unsigned Perm = PF_R | PF_W; + if (Config->ZExecstack) + Perm |= PF_X; + AddHdr(PT_GNU_STACK, Perm)->p_memsz = Config->ZStackSize; + } // PT_OPENBSD_WXNEEDED is a OpenBSD-specific header to mark the executable // is expected to perform W^X violations, such as calling mprotect(2) or Index: docs/ld.lld.1 =================================================================== --- docs/ld.lld.1 +++ docs/ld.lld.1 @@ -509,6 +509,10 @@ .Dv DF_1_NOOPEN flag to indicate that the object may not be opened by .Xr dlopen 3 . +.It Cm nognustack +Do not emit the +.Dv PT_GNU_STACK +segment. .It Cm norelro Do not indicate that portions of the object shold be mapped read-only after initial relocation processing. Index: test/ELF/gnustack.s =================================================================== --- test/ELF/gnustack.s +++ test/ELF/gnustack.s @@ -10,6 +10,9 @@ # RUN: ld.lld %t1 -o %t -z noexecstack # RUN: llvm-readobj --program-headers -s %t | FileCheck --check-prefix=RW %s +# RUN: ld.lld %t1 -o %t -z nognustack +# RUN: llvm-readobj --program-headers -s %t | FileCheck --check-prefix=NOGNUSTACK %s + # RW: Type: PT_GNU_STACK # RW-NEXT: Offset: 0x0 # RW-NEXT: VirtualAddress: 0x0 @@ -35,5 +38,7 @@ # RWX-NEXT: ] # RWX-NEXT: Alignment: 0 +# NOGNUSTACK-NOT: Type: PT_GNU_STACK + .globl _start _start: