diff --git a/libwild/src/elf.rs b/libwild/src/elf.rs index df25c833b..45d8d0e15 100644 --- a/libwild/src/elf.rs +++ b/libwild/src/elf.rs @@ -417,7 +417,6 @@ impl platform::Platform for Elf { output_section_id::PROGRAM_HEADERS, output_section_id::SECTION_HEADERS, output_section_id::SHSTRTAB, - output_section_id::RELRO_PADDING, ]; for section_id in FORCE_KEEP_SECTIONS { diff --git a/wild/tests/integration_tests.rs b/wild/tests/integration_tests.rs index 846920979..ddffc4e7e 100644 --- a/wild/tests/integration_tests.rs +++ b/wild/tests/integration_tests.rs @@ -57,6 +57,12 @@ //! ExpectLoadAlignment:{alignment} Checks that the first PT_LOAD segment in the output binary has //! the specified alignment. //! +//! ExpectProgramHeader:{type} Checks that the output binary contains a program header of the +//! specified type. +//! +//! NoProgramHeader:{type} Checks that the output binary contains no program headers of the +//! specified type. +//! //! DoesNotContain:{string} Checks that the output binary doesn't contain the specified string. //! //! Contains:{string} Checks that the output binary does contain the specified string. @@ -814,6 +820,23 @@ enum DriverMode { SaveDirResponse, } +#[derive(Clone, Copy, Debug, Display, PartialEq, Eq, EnumString)] +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] +#[repr(u32)] +enum ProgramHeaderType { + Dynamic = object::elf::PT_DYNAMIC, + Interp = object::elf::PT_INTERP, + GnuEhFrame = object::elf::PT_GNU_EH_FRAME, + GnuProperty = object::elf::PT_GNU_PROPERTY, + GnuRelro = object::elf::PT_GNU_RELRO, + GnuStack = object::elf::PT_GNU_STACK, + Load = object::elf::PT_LOAD, + Note = object::elf::PT_NOTE, + Null = object::elf::PT_NULL, + Phdr = object::elf::PT_PHDR, + Tls = object::elf::PT_TLS, +} + #[derive(Debug, Clone)] struct ErrorMatcher { regex: regex::Regex, @@ -1140,6 +1163,8 @@ struct Assertions { expected_section_bytes: Vec, output_file_matches: Vec, max_thunks: u64, + expected_program_headers: Vec, + absent_program_headers: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -1498,6 +1523,18 @@ fn process_directive( }; config.assertions.expected_load_alignment = Some(alignment); } + "ExpectProgramHeader" => { + let header_type: ProgramHeaderType = arg + .parse() + .with_context(|| format!("Invalid program header type `{arg}`"))?; + config.assertions.expected_program_headers.push(header_type); + } + "NoProgramHeader" => { + let header_type: ProgramHeaderType = arg + .parse() + .with_context(|| format!("Invalid program header type `{arg}`"))?; + config.assertions.absent_program_headers.push(header_type); + } "Mode" => { let mode: Mode = arg .parse() @@ -3430,6 +3467,7 @@ impl Assertions { self.verify_dynamic_entries(&elf_obj)?; self.verify_symbols_absent(&self.no_sym, elf_obj.dynamic_symbols(), "dynsym")?; self.verify_symbols_absent(&self.no_dynsym, elf_obj.dynamic_symbols(), "dynsym")?; + self.verify_program_headers(&elf_obj)?; } object::File::MachO64(_) => { if !self.expected_comments.is_empty() { @@ -3447,6 +3485,12 @@ impl Assertions { if !self.absent_dynamic_entries.is_empty() { bail!("NoDynamic is not supported for MachO",); } + if !self.expected_program_headers.is_empty() { + bail!("ExpectProgramHeader is not supported for MachO",); + } + if !self.absent_program_headers.is_empty() { + bail!("NoProgramHeader is not supported for MachO"); + } } _ => bail!("Unsupported object file format"), } @@ -3676,6 +3720,36 @@ impl Assertions { Ok(()) } + fn verify_program_headers<'data>( + &self, + obj: &object::read::elf::ElfFile64<'data, object::Endianness>, + ) -> Result { + if self.expected_program_headers.is_empty() && self.absent_program_headers.is_empty() { + return Ok(()); + } + + let endian = obj.endian(); + let mut header_types = HashSet::new(); + + for header in obj.elf_program_headers() { + header_types.insert(header.p_type(endian)); + } + + for header in &self.expected_program_headers { + if !header_types.contains(&(*header as u32)) { + bail!("Expected program header `{header}' not found."); + } + } + + for header in &self.absent_program_headers { + if header_types.contains(&(*header as u32)) { + bail!("Program header `{header}' should be absent but was found."); + } + } + + Ok(()) + } + /// Returns whether we have assertions configured that require metrics to be enabled. Even if /// this returns false, if diffing is enabled, we'll collect metrics and check them. fn requires_metrics(&self) -> bool { diff --git a/wild/tests/sources/elf/relro/relro.c b/wild/tests/sources/elf/relro/relro.c new file mode 100644 index 000000000..2585ae61f --- /dev/null +++ b/wild/tests/sources/elf/relro/relro.c @@ -0,0 +1,26 @@ +//#AbstractConfig:default +// Create a .got.plt section to force ld to include a PT_GNU_RELRO program +// header +//#Shared:runtime.c +//#Mode:dynamic +//#DiffIgnore:section.got +//#DiffIgnore:.dynamic.DT_NEEDED +//#DiffIgnore:.dynamic.DT_FLAGS_1.NOW +//#DiffIgnore:.dynamic.DT_RELA +//#DiffIgnore:.dynamic.DT_RELAENT + +//#Config:enabled:default +//#LinkArgs:-z relro +//#ExpectProgramHeader:GNU_RELRO + +//#Config:disabled:default +//#LinkArgs:-z norelro +//#NoProgramHeader:GNU_RELRO +//#DoesNotContain:relro_padding + +#include "../common/runtime.h" + +void _start() { + runtime_init(); + exit_syscall(42); +}