diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -64,6 +64,9 @@ // For -z noseparate-code, -z separate-code and -z separate-loadable-segments. enum class SeparateSegmentKind { None, Code, Loadable }; +// For -z *stack +enum class GnuStackKind { None, Exec, NoExec }; + struct SymbolVersion { llvm::StringRef name; bool isExternCpp; @@ -216,6 +219,7 @@ bool zRetpolineplt; bool zWxneeded; DiscardPolicy discard; + GnuStackKind zGnustack; ICFLevel icf; OrphanHandlingPolicy orphanHandling; SortSectionPolicy sortSection; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -394,6 +394,20 @@ return SeparateSegmentKind::None; } +static GnuStackKind getZGnuStack(opt::InputArgList &args) { + for (auto *arg : args.filtered_reverse(OPT_z)) { + if (StringRef("execstack") == arg->getValue()) + return GnuStackKind::Exec; + if (StringRef("noexecstack") == arg->getValue()) + return GnuStackKind::NoExec; + if (StringRef("nognustack") == arg->getValue()) + return GnuStackKind::None; + } + + // default + return GnuStackKind::NoExec; +} + static bool isKnownZFlag(StringRef s) { return s == "combreloc" || s == "copyreloc" || s == "defs" || s == "execstack" || s == "global" || s == "hazardplt" || @@ -402,6 +416,7 @@ 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 == "now" || s == "origin" || s == "relro" || s == "retpolineplt" || @@ -951,6 +966,7 @@ config->zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true); config->zExecstack = getZFlag(args, "execstack", "noexecstack", false); config->zGlobal = hasZOption(args, "global"); + config->zGnustack = getZGnuStack(args); config->zHazardplt = hasZOption(args, "hazardplt"); config->zIfuncNoplt = hasZOption(args, "ifunc-noplt"); config->zInitfirst = hasZOption(args, "initfirst"); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -2172,14 +2172,16 @@ if (OutputSection *cmd = findSection(".openbsd.randomdata", partNo)) 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->zGnustack != GnuStackKind::None) { + // 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->zGnustack == GnuStackKind::Exec) + 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 diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -655,6 +655,11 @@ flag to indicate that the object may not be opened by .Xr dlopen 3 . .Pp +.It Cm nognustack +Do not emit the +.Dv PT_GNU_STACK +segment. +.Pp .It Cm norelro Do not indicate that portions of the object shold be mapped read-only after initial relocation processing. diff --git a/lld/test/ELF/gnustack.s b/lld/test/ELF/gnustack.s --- a/lld/test/ELF/gnustack.s +++ b/lld/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: