答案是链接器警告目标文件或库要求可执行栈,因系统默认禁用该权限;需在链接时加-wl,-z,noexecstack强制关闭,并用readelf验证gnu_stack显示rw而非rwe。

这是链接器在警告你:某个目标文件(.o)或库要求可执行栈,而系统默认禁用了该权限——不是编译失败,但可能被 SELinux / PaX / modern kernel 拒绝加载。
为什么会出现“requires executable stack”提示
GCC 生成的代码本身通常不主动请求可执行栈,但以下情况会触发该标记:
- 内联汇编中显式使用了
xor %rax, %rax等可能被误判为 shellcode 的模式(尤其在某些老版本 binutils 中) - 链接了第三方静态库(如旧版 OpenSSL、某些加密库),其构建时未加
-z noexecstack - 用了
-mpreferred-stack-boundary=2或手动对齐栈帧,导致 linker 推断“可能需要执行栈” - Go 编译的 cgo 对象(
import "C")若含汇编 stub,也可能带此属性
关键点:readelf -l your_binary | grep STACK 若显示 GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10(末尾是 RWE),就确认栈被标记为可执行。
如何让 GCC / ld 不生成 executable stack
最直接有效的方式是在链接阶段强制关闭该标记:
- 加
-Wl,-z,noexecstack到你的gcc命令末尾(推荐) - 若用
ld直接链接:加-z noexecstack - 在 Makefile 或构建脚本里统一追加:
LDFLAGS += -Wl,-z,noexecstack
注意:仅加 -z noexecstack 不够,必须通过 -Wl, 透传给 linker;否则 gcc 可能忽略它。验证是否生效:readelf -l a.out | grep GNU_STACK 应显示 RW(无 E)。
如果加了 -z noexecstack 还报 warning,说明依赖库硬编码了该属性
此时 linker 无法覆盖已有标记,只能从源头修复或替换库:
- 用
readelf -l libxxx.a | grep GNU_STACK找出哪个静态库带RWE - 重新编译那个库,确保其编译时加了
-Wa,--noexecstack(assembler 层)和链接时加-z noexecstack - 临时绕过:用
execstack -c your_binary清除标记(仅限开发调试,生产环境不推荐) - 若用的是预编译二进制 SDK(如某硬件厂商提供),联系对方提供
noexecstack版本
特别注意:Android NDK r21+ 默认启用 -z noexecstack,但如果你在交叉编译时混用了旧版 toolchain,仍可能漏掉。
检查与验证的最小命令集
快速定位和确认:
-
gcc -v your.c -o test 2>&1 | grep "ld"看 linker 调用是否含-z noexecstack -
readelf -l test | grep GNU_STACK—— 必须是RW,不能有E -
execstack -q test—— 输出- test表示已禁用;+ test表示仍启用 -
gcc -dumpspecs | grep noexecstack查看默认 specs 是否内置该选项(新版 GCC 通常已内置)
真正麻烦的从来不是加一个 flag,而是你得确认所有参与链接的目标文件、归档库、共享库都一致遵守 noexecstack —— 混入一个旧库,整个二进制就“被污染”了。











