symcheck: Only ELFv1 ppc64 doesn't set .note.GNU-stack

`powerpc64-unknown-linux-gnu` currently is an ELFv1 target, while
`powerpc64-unknown-linux-musl` is an ELFv2 target.

Big-endian and little-endian ELFv2 targets both behave normally: they emit
`.note.GNU-stack`. Therefore, currently the tests would fail on big-endian
powerpc64 with ELFv2 ABI.

To determine whether we need to special-case powerpc64, we should check
the ABI instead of the endianness. The problem here is that the `e_flags`
part of the ELF header is actually `0` in the output of `cc -O0
-ffunction-sections -fdata-sections -fPIC -m64 -mabi=elfv2 -Wall -Wextra
-o missing_gnu_stack_section.o -c missing_gnu_stack_section.S`, the output
of that command is bit-for-bit identical on ELFv1 and ELFv2 hosts. In
order to know when to allow an unset `.note.GNU-stack` we therefore must
set `.abiversion 2` to be able to tell them apart from the ELF header.

This makes all tests pass on `powerpc64-unknown-linux-musl`.
This commit is contained in:
Aelin
2026-05-26 13:03:19 +02:00
committed by GitHub
parent 534a510804
commit 79bb9f71e7
3 changed files with 25 additions and 9 deletions
@@ -13,8 +13,8 @@
use object::read::archive::ArchiveFile;
use object::{
Architecture, BinaryFormat, Endianness, File as ObjFile, Object, ObjectSection, ObjectSymbol,
Result as ObjResult, SectionFlags, Symbol, SymbolKind, SymbolScope, U32, elf,
Architecture, BinaryFormat, Endianness, File as ObjFile, FileFlags, Object, ObjectSection,
ObjectSymbol, Result as ObjResult, SectionFlags, Symbol, SymbolKind, SymbolScope, U32, elf,
};
use regex::Regex;
use serde_json::Value;
@@ -526,12 +526,15 @@ fn check_elf_exe_stack(obj: &ObjFile) -> Result<(), ExeStack> {
return Ok(());
}
let FileFlags::Elf { e_flags, .. } = obj.flags() else {
unreachable!("only elf files are being checked");
};
// If there is no `.note.GNU-stack` and no executable sections, behavior differs by platform.
match obj.architecture() {
// PPC64 doesn't set `.note.GNU-stack` since GNU nested functions don't need a trampoline,
// <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=21098>. From experimentation, it seems
// like this only applies to big endian.
Architecture::PowerPc64 if obj.endianness() == Endianness::Big => Ok(()),
// <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=21098>. This only applies to ELFv1.
Architecture::PowerPc64 if e_flags & elf::EF_PPC64_ABI != 2 => Ok(()),
_ => Err(ExeStack::MissingGnuStackSec),
}
@@ -111,8 +111,11 @@ fn test_missing_gnu_stack_section() {
let assert = t.symcheck_exe().arg(obj).arg("--no-visibility").assert();
if t.is_ppc64be() || t.no_os() || t.binary_obj_format() != BinaryFormat::Elf {
// Ppc64be doesn't emit `.note.GNU-stack`, not relevant without an OS, and non-elf
if (t.is_ppc64be() && t.is_glibc())
|| t.no_os()
|| t.binary_obj_format() != BinaryFormat::Elf
{
// Ppc64be ELFv1 doesn't emit `.note.GNU-stack`, not relevant without an OS, and non-elf
// targets don't use `.note.GNU-stack`.
assert.success();
return;
@@ -143,8 +146,8 @@ fn test_exe_gnu_stack_section() {
let assert = t.symcheck_exe().arg(obj).arg("--no-visibility").assert();
if t.is_ppc64be() || t.no_os() {
// Ppc64be doesn't emit `.note.GNU-stack`, not relevant without an OS.
if (t.is_ppc64be() && t.is_glibc()) || t.no_os() {
// Ppc64be ELFv1 doesn't emit `.note.GNU-stack`, not relevant without an OS.
assert.success();
return;
}
@@ -316,6 +319,10 @@ fn is_ppc64be(&self) -> bool {
self.triple.starts_with("powerpc64-")
}
fn is_glibc(&self) -> bool {
self.triple.contains("-linux-gnu")
}
/// True if the target needs `--no-os` passed to symcheck.
fn no_os(&self) -> bool {
self.triple.contains("-none")
@@ -4,6 +4,12 @@
* executability is implied (usually yes on Linux).
*/
/* Unless we set the ABI version, we have no way to differentiate between
big-endian ppc64 ELFv1 or ELFv2 since e_flags is otherwise unspecified */
#if defined(__powerpc64__) && _CALL_ELF == 2
.abiversion 2
#endif
.global func
#ifdef __wasm__