elfcpp/elfcpp.h | 17 ++++++++++++++++- gold/layout.cc | 18 ++++++++++++++++-- gold/options.cc | 8 ++++++++ gold/options.h | 31 +++++++++++++++++++++++++++++++ gold/script.cc | 1 + 5 files changed, 72 insertions(+), 3 deletions(-) diff --git a/elfcpp/elfcpp.h b/elfcpp/elfcpp.h index e4f9f1f..29b71a8 100644 --- a/elfcpp/elfcpp.h +++ b/elfcpp/elfcpp.h @@ -479,6 +479,8 @@ enum PT PT_GNU_STACK = 0x6474e551, // Read only after relocation. PT_GNU_RELRO = 0x6474e552, + // PaX flags + PT_PAX_FLAGS = 0x65041580, // Platform architecture compatibility information PT_ARM_ARCHEXT = 0x70000000, // Exception unwind tables @@ -499,7 +501,20 @@ enum PF PF_W = 0x2, PF_R = 0x4, PF_MASKOS = 0x0ff00000, - PF_MASKPROC = 0xf0000000 + PF_MASKPROC = 0xf0000000, + /* Flags to control PaX behavior. */ + PF_PAGEEXEC = (1 << 4), /* Enable PAGEEXEC */ + PF_NOPAGEEXEC = (1 << 5), /* Disable PAGEEXEC */ + PF_SEGMEXEC = (1 << 6), /* Enable SEGMEXEC */ + PF_NOSEGMEXEC = (1 << 7), /* Disable SEGMEXEC */ + PF_MPROTECT = (1 << 8), /* Enable MPROTECT */ + PF_NOMPROTECT = (1 << 9), /* Disable MPROTECT */ + PF_RANDEXEC = (1 << 10), /* Enable RANDEXEC */ + PF_NORANDEXEC = (1 << 11), /* Disable RANDEXEC */ + PF_EMUTRAMP = (1 << 12), /* Enable EMUTRAMP */ + PF_NOEMUTRAMP = (1 << 13), /* Disable EMUTRAMP */ + PF_RANDMMAP = (1 << 14), /* Enable RANDMMAP */ + PF_NORANDMMAP = (1 << 15) /* Disable RANDMMAP */ }; // Symbol binding from Sym st_info field. diff --git a/gold/layout.cc b/gold/layout.cc index ad667ab..b5f0b48 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -1878,7 +1878,7 @@ Layout::layout_gnu_stack(bool seen_gnu_stack, uint64_t gnu_stack_flags, else { this->input_with_gnu_stack_note_ = true; - if ((gnu_stack_flags & elfcpp::SHF_EXECINSTR) != 0) + if ((gnu_stack_flags & elfcpp::SHF_EXECINSTR) != 0) { this->input_requires_executable_stack_ = true; if (parameters->options().warn_execstack() @@ -2731,7 +2731,7 @@ Layout::create_executable_stack_info() return; else { - if (this->input_requires_executable_stack_) + if (this->input_requires_executable_stack_) is_stack_executable = true; else if (this->input_without_gnu_stack_note_) is_stack_executable = @@ -2757,6 +2757,20 @@ Layout::create_executable_stack_info() if (is_stack_executable) flags |= elfcpp::PF_X; this->make_output_segment(elfcpp::PT_GNU_STACK, flags); + + int pax_flags = elfcpp::PF_NORANDEXEC; + if (parameters->options().is_execheap_set()) { + if (parameters->options().is_heap_executable()) + pax_flags |= elfcpp::PF_NOMPROTECT; + else + pax_flags |= elfcpp::PF_MPROTECT; + } + + if (is_stack_executable) + pax_flags |= elfcpp::PF_EMUTRAMP; + else + pax_flags |= elfcpp::PF_NOEMUTRAMP; + this->make_output_segment(elfcpp::PT_PAX_FLAGS, pax_flags); } } diff --git a/gold/options.cc b/gold/options.cc index fe9a00e..6db9e5c 100644 --- a/gold/options.cc +++ b/gold/options.cc @@ -912,6 +912,7 @@ namespace gold General_options::General_options() : printed_version_(false), execstack_status_(EXECSTACK_FROM_INPUT), + execheap_status_(EXECHEAP_FROM_INPUT), icf_status_(ICF_NONE), static_(false), do_demangle_(false), @@ -1051,6 +1052,13 @@ General_options::finalize() else if (this->noexecstack()) this->set_execstack_status(EXECSTACK_NO); + // execheap_status_ is a three-state variable; update it based on + // -z [no]execheap. + if (this->execheap()) + this->set_execheap_status(EXECHEAP_YES); + else if (this->noexecheap()) + this->set_execheap_status(EXECHEAP_NO); + // icf_status_ is a three-state variable; update it based on the // value of this->icf(). if (strcmp(this->icf(), "none") == 0) diff --git a/gold/options.h b/gold/options.h index 50762a5..378ae6c 100644 --- a/gold/options.h +++ b/gold/options.h @@ -1190,6 +1190,8 @@ class General_options NULL); DEFINE_bool(execstack, options::DASH_Z, '\0', false, N_("Mark output as requiring executable stack"), NULL); + DEFINE_bool(execheap, options::DASH_Z, '\0', false, + N_("Mark output as requiring executable heap"), NULL); DEFINE_bool(initfirst, options::DASH_Z, '\0', false, N_("Mark DSO to be initialized first at runtime"), NULL); @@ -1226,6 +1228,8 @@ class General_options NULL); DEFINE_bool(noexecstack, options::DASH_Z, '\0', false, N_("Mark output as not requiring executable stack"), NULL); + DEFINE_bool(noexecheap, options::DASH_Z, '\0', false, + N_("Mark output as not requiring executable heap"), NULL); DEFINE_bool(now, options::DASH_Z, '\0', false, N_("Mark object for immediate function binding"), NULL); @@ -1327,6 +1331,16 @@ class General_options is_stack_executable() const { return this->execstack_status_ == EXECSTACK_YES; } + // These are the best way to get access to the execheap state, + // not execheap() and noexecheap() which are hard to use properly. + bool + is_execheap_set() const + { return this->execheap_status_ != EXECHEAP_FROM_INPUT; } + + bool + is_heap_executable() const + { return this->execheap_status_ == EXECHEAP_YES; } + bool icf_enabled() const { return this->icf_status_ != ICF_NONE; } @@ -1451,6 +1465,17 @@ class General_options EXECSTACK_NO }; + // Whether to mark the heap as executable. + enum Execheap + { + // Not set on command line. + EXECHEAP_FROM_INPUT, + // Mark the heap as executable (-z execheap). + EXECHEAP_YES, + // Mark the heap as not executable (-z noexecheap). + EXECHEAP_NO + }; + enum Icf_status { // Do not fold any functions (Default or --icf=none). @@ -1470,6 +1495,10 @@ class General_options { this->execstack_status_ = value; } void + set_execheap_status(Execheap value) + { this->execheap_status_ = value; } + + void set_do_demangle(bool value) { this->do_demangle_ = value; } @@ -1498,6 +1527,8 @@ class General_options bool printed_version_; // Whether to mark the stack as executable. Execstack execstack_status_; + // Whether to mark the heap as executable. + Execheap execheap_status_; // Whether to do code folding. Icf_status icf_status_; // Whether to do a static link. diff --git a/gold/script.cc b/gold/script.cc index 6a10c40..f6b1ca1 100644 --- a/gold/script.cc +++ b/gold/script.cc @@ -3262,6 +3262,7 @@ static struct PHDR_TYPE(PT_TLS), PHDR_TYPE(PT_GNU_EH_FRAME), PHDR_TYPE(PT_GNU_STACK), + PHDR_TYPE(PT_PAX_FLAGS), PHDR_TYPE(PT_GNU_RELRO) };