Skip to content

Commit a01d762

Browse files
committed
Add options to print, clear and set executable stack state
Add options the modify the state of the executable flag of the GNU_STACK program header. That header indicates whether the object is requiring an executable stack.
1 parent 5908e16 commit a01d762

File tree

7 files changed

+396
-4
lines changed

7 files changed

+396
-4
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Makefile
3232
/tests/libbig-dynstr.debug
3333
/tests/contiguous-note-sections
3434
/tests/simple-pie
35+
/tests/simple-execstack
3536

3637
.direnv/
3738
.vscode/

patchelf.1

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,15 @@ This means that when a shared library has an entry point (so that it
114114
can be run as an executable), the debugger does not connect to it correctly and
115115
symbols are not resolved.
116116

117+
.IP "--print-execstack"
118+
Prints the state of the executable flag of the GNU_STACK program header, if present.
119+
120+
.IP "--clear-execstack"
121+
Clears the executable flag of the GNU_STACK program header, or adds a new header.
122+
123+
.IP "--set-execstack"
124+
Sets the executable flag of the GNU_STACK program header, or adds a new header.
125+
117126
.IP "--output FILE"
118127
Set the output file name. If not specified, the input will be modified in place.
119128

src/patchelf.cc

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,104 @@ void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string>
18901890
this->rewriteSections();
18911891
}
18921892

1893+
template<ElfFileParams>
1894+
void ElfFile<ElfFileParamNames>::modifyExecstack(ExecstackMode op)
1895+
{
1896+
if (op == ExecstackMode::clear || op == ExecstackMode::set) {
1897+
size_t nullhdr = (size_t)-1;
1898+
1899+
for (size_t i = 0; i < phdrs.size(); i++) {
1900+
auto & header = phdrs[i];
1901+
const auto type = rdi(header.p_type);
1902+
if (type != PT_GNU_STACK) {
1903+
if (!nullhdr && type == PT_NULL)
1904+
nullhdr = i;
1905+
continue;
1906+
}
1907+
1908+
if (op == ExecstackMode::clear && (rdi(header.p_flags) & PF_X) == PF_X) {
1909+
debug("simple execstack clear of header %zu\n", i);
1910+
1911+
wri(header.p_flags, rdi(header.p_flags) & ~PF_X);
1912+
1913+
* ((Elf_Phdr *) (fileContents->data() + rdi(hdr()->e_phoff)) + i) = header;
1914+
changed = true;
1915+
} else if (op == ExecstackMode::set && (rdi(header.p_flags) & PF_X) != PF_X) {
1916+
debug("simple execstack set of header %zu\n", i);
1917+
1918+
wri(header.p_flags, rdi(header.p_flags) | PF_X);
1919+
1920+
* ((Elf_Phdr *) (fileContents->data() + rdi(hdr()->e_phoff)) + i) = header;
1921+
changed = true;
1922+
} else {
1923+
debug("execstack already in requested state\n");
1924+
}
1925+
1926+
return;
1927+
}
1928+
1929+
if (nullhdr != (size_t)-1) {
1930+
debug("replacement execstack of header %zu\n", nullhdr);
1931+
1932+
auto & header = phdrs[nullhdr];
1933+
wri(header.p_type, PT_GNU_STACK);
1934+
wri(header.p_offset, 0);
1935+
wri(header.p_vaddr, 0);
1936+
wri(header.p_paddr, 0);
1937+
wri(header.p_filesz, 0);
1938+
wri(header.p_memsz, 0);
1939+
wri(header.p_flags, PF_R | PF_W | (op == ExecstackMode::set ? PF_X : 0));
1940+
wri(header.p_align, getPageSize());
1941+
1942+
* ((Elf_Phdr *) (fileContents->data() + rdi(hdr()->e_phoff)) + nullhdr) = header;
1943+
changed = true;
1944+
return;
1945+
}
1946+
1947+
debug("header addition for execstack\n");
1948+
1949+
Elf_Addr firstPage = 0;
1950+
for (const auto & phdr : phdrs) {
1951+
if (rdi(phdr.p_type) == PT_PHDR) {
1952+
firstPage = rdi(phdr.p_vaddr) - rdi(phdr.p_offset);
1953+
break;
1954+
}
1955+
}
1956+
1957+
Elf_Phdr new_phdr;
1958+
wri(new_phdr.p_type, PT_GNU_STACK);
1959+
wri(new_phdr.p_offset, 0);
1960+
wri(new_phdr.p_vaddr, 0);
1961+
wri(new_phdr.p_paddr, 0);
1962+
wri(new_phdr.p_filesz, 0);
1963+
wri(new_phdr.p_memsz, 0);
1964+
wri(new_phdr.p_flags, PF_R | PF_W | (op == ExecstackMode::set ? PF_X : 0));
1965+
wri(new_phdr.p_align, getPageSize());
1966+
phdrs.push_back(new_phdr);
1967+
1968+
wri(hdr()->e_phnum, rdi(hdr()->e_phnum) + 1);
1969+
1970+
changed = true;
1971+
rewriteHeaders(firstPage + rdi(hdr()->e_phoff));
1972+
return;
1973+
}
1974+
1975+
char result = '?';
1976+
1977+
for (const auto & header : phdrs) {
1978+
if (rdi(header.p_type) != PT_GNU_STACK)
1979+
continue;
1980+
1981+
if ((rdi(header.p_flags) & PF_X) == PF_X)
1982+
result = 'X';
1983+
else
1984+
result = '-';
1985+
break;
1986+
}
1987+
1988+
printf("execstack: %c\n", result);
1989+
}
1990+
18931991
static bool printInterpreter = false;
18941992
static bool printOsAbi = false;
18951993
static bool setOsAbi = false;
@@ -1912,6 +2010,9 @@ static std::set<std::string> neededLibsToAdd;
19122010
static std::set<std::string> symbolsToClearVersion;
19132011
static bool printNeeded = false;
19142012
static bool noDefaultLib = false;
2013+
static bool printExecstack = false;
2014+
static bool clearExecstack = false;
2015+
static bool setExecstack = false;
19152016

19162017
template<class ElfFile>
19172018
static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, const std::string & fileName)
@@ -1937,6 +2038,13 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
19372038
if (printRPath)
19382039
elfFile.modifyRPath(elfFile.rpPrint, {}, "");
19392040

2041+
if (printExecstack)
2042+
elfFile.modifyExecstack(ElfFile::ExecstackMode::print);
2043+
else if (clearExecstack)
2044+
elfFile.modifyExecstack(ElfFile::ExecstackMode::clear);
2045+
else if (setExecstack)
2046+
elfFile.modifyExecstack(ElfFile::ExecstackMode::set);
2047+
19402048
if (shrinkRPath)
19412049
elfFile.modifyRPath(elfFile.rpShrink, allowedRpathPrefixes, "");
19422050
else if (removeRPath)
@@ -2019,6 +2127,9 @@ void showHelp(const std::string & progName)
20192127
[--no-sort]\t\tDo not sort program+section headers; useful for debugging patchelf.\n\
20202128
[--clear-symbol-version SYMBOL]\n\
20212129
[--add-debug-tag]\n\
2130+
[--print-execstack]\t\tPrints whether the object requests an executable stack\n\
2131+
[--clear-execstack]\n\
2132+
[--set-execstack]\n\
20222133
[--output FILE]\n\
20232134
[--debug]\n\
20242135
[--version]\n\
@@ -2127,6 +2238,15 @@ int mainWrapped(int argc, char * * argv)
21272238
if (++i == argc) error("missing argument");
21282239
symbolsToClearVersion.insert(resolveArgument(argv[i]));
21292240
}
2241+
else if (arg == "--print-execstack") {
2242+
printExecstack = true;
2243+
}
2244+
else if (arg == "--clear-execstack") {
2245+
clearExecstack = true;
2246+
}
2247+
else if (arg == "--set-execstack") {
2248+
setExecstack = true;
2249+
}
21302250
else if (arg == "--output") {
21312251
if (++i == argc) error("missing argument");
21322252
outputFileName = resolveArgument(argv[i]);

src/patchelf.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ class ElfFile
139139

140140
void clearSymbolVersions(const std::set<std::string> & syms);
141141

142+
enum class ExecstackMode { print, set, clear };
143+
144+
void modifyExecstack(ExecstackMode op);
145+
142146
private:
143147

144148
/* Convert an integer in big or little endian representation (as

tests/Makefile.am

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
LIBS =
22

3-
check_PROGRAMS = simple-pie simple main too-many-strtab main-scoped big-dynstr no-rpath contiguous-note-sections
3+
check_PROGRAMS = simple-pie simple simple-execstack main too-many-strtab main-scoped big-dynstr no-rpath contiguous-note-sections
44

55
no_rpath_arch_TESTS = \
66
no-rpath-amd64.sh \
@@ -43,7 +43,9 @@ src_TESTS = \
4343
replace-needed.sh \
4444
replace-add-needed.sh \
4545
add-debug-tag.sh \
46-
empty-note.sh
46+
empty-note.sh \
47+
print-execstack.sh \
48+
modify-execstack.sh
4749

4850
build_TESTS = \
4951
$(no_rpath_arch_TESTS)
@@ -71,10 +73,15 @@ export NIX_LDFLAGS=
7173
simple_SOURCES = simple.c
7274
# no -fpic for simple.o
7375
simple_CFLAGS =
76+
simple_LDFLAGS = -Wl,-z,noexecstack
7477

7578
simple_pie_SOURCES = simple.c
7679
simple_pie_CFLAGS = -fPIC -pie
7780

81+
simple_execstack_SOURCES = simple.c
82+
simple_execstack_CFLAGS =
83+
simple_execstack_LDFLAGS = -Wl,-z,execstack
84+
7885
main_SOURCES = main.c
7986
main_LDADD = -lfoo $(AM_LDADD)
8087
main_DEPENDENCIES = libfoo.so
@@ -108,7 +115,7 @@ check_DATA = libbig-dynstr.debug
108115
# - without libtool, only archives (static libraries) can be built by automake
109116
# - with libtool, it is difficult to control options
110117
# - with libtool, it is not possible to compile convenience *dynamic* libraries :-(
111-
check_PROGRAMS += libfoo.so libfoo-scoped.so libbar.so libbar-scoped.so libsimple.so libbuildid.so libtoomanystrtab.so \
118+
check_PROGRAMS += libfoo.so libfoo-scoped.so libbar.so libbar-scoped.so libsimple.so libsimple-execstack.so libbuildid.so libtoomanystrtab.so \
112119
phdr-corruption.so
113120

114121
libbuildid_so_SOURCES = simple.c
@@ -131,7 +138,10 @@ libbar_scoped_so_SOURCES = bar.c
131138
libbar_scoped_so_LDFLAGS = $(LDFLAGS_sharedlib)
132139

133140
libsimple_so_SOURCES = simple.c
134-
libsimple_so_LDFLAGS = $(LDFLAGS_sharedlib)
141+
libsimple_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,-z,noexecstack
142+
143+
libsimple_execstack_so_SOURCES = simple.c
144+
libsimple_execstack_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,-z,execstack
135145

136146
too_many_strtab_SOURCES = too-many-strtab.c too-many-strtab2.s
137147
libtoomanystrtab_so_SOURCES = too-many-strtab.c too-many-strtab2.s

0 commit comments

Comments
 (0)