diff --git a/src/write/coff.rs b/src/write/coff.rs index 5e44a06f..36c70b05 100644 --- a/src/write/coff.rs +++ b/src/write/coff.rs @@ -61,6 +61,10 @@ impl<'a> Object<'a> { // Unsupported section. (&[], &[], SectionKind::Common) } + StandardSection::GnuProperty => { + // Unsupported section. + (&[], &[], SectionKind::Note) + } } } diff --git a/src/write/elf/object.rs b/src/write/elf/object.rs index 305d522e..d1ed9961 100644 --- a/src/write/elf/object.rs +++ b/src/write/elf/object.rs @@ -1,10 +1,10 @@ use alloc::vec::Vec; -use crate::elf; use crate::write::elf::writer::*; use crate::write::string::StringId; use crate::write::*; use crate::AddressSize; +use crate::{elf, pod}; #[derive(Clone, Copy)] struct ComdatOffsets { @@ -27,6 +27,42 @@ struct SymbolOffsets { str_id: Option, } +// Public methods. +impl<'a> Object<'a> { + /// Add a property with a u32 value to the ELF ".note.gnu.property" section. + /// + /// Requires `feature = "elf"`. + pub fn add_elf_gnu_property_u32(&mut self, property: u32, value: u32) { + if self.format != BinaryFormat::Elf { + return; + } + + let align = if self.elf_is_64() { 8 } else { 4 }; + let n_name = b"GNU\0"; + // pr_type + pr_datasz + pr_data + let n_descsz = util::align(12, align) as u32; + + let mut data = Vec::with_capacity(32); + data.extend_from_slice(pod::bytes_of(&elf::NoteHeader32 { + n_namesz: U32::new(self.endian, n_name.len() as u32), + n_descsz: U32::new(self.endian, n_descsz), + n_type: U32::new(self.endian, elf::NT_GNU_PROPERTY_TYPE_0), + })); + data.extend_from_slice(n_name); + // This happens to already be aligned correctly. + debug_assert_eq!(util::align(data.len(), align), data.len()); + data.extend_from_slice(pod::bytes_of(&U32::new(self.endian, property))); + // Value size + data.extend_from_slice(pod::bytes_of(&U32::new(self.endian, 4))); + data.extend_from_slice(pod::bytes_of(&U32::new(self.endian, value))); + util::write_align(&mut data, align); + + let section = self.section_id(StandardSection::GnuProperty); + self.append_section_data(section, &data, align as u64); + } +} + +// Private methods. impl<'a> Object<'a> { pub(crate) fn elf_section_info( &self, @@ -56,6 +92,7 @@ impl<'a> Object<'a> { // Unsupported section. (&[], &[], SectionKind::Common) } + StandardSection::GnuProperty => (&[], &b".note.gnu.property"[..], SectionKind::Note), } } @@ -153,6 +190,13 @@ impl<'a> Object<'a> { } } + pub(crate) fn elf_is_64(&self) -> bool { + match self.architecture.address_size().unwrap() { + AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => false, + AddressSize::U64 => true, + } + } + pub(crate) fn elf_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { // Create reloc section header names so we can reference them. let is_rela = self.elf_has_relocation_addend()?; @@ -176,11 +220,7 @@ impl<'a> Object<'a> { .collect(); // Start calculating offsets of everything. - let is_64 = match self.architecture.address_size().unwrap() { - AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => false, - AddressSize::U64 => true, - }; - let mut writer = Writer::new(self.endian, is_64, buffer); + let mut writer = Writer::new(self.endian, self.elf_is_64(), buffer); writer.reserve_file_header(); // Calculate size of section data. diff --git a/src/write/macho.rs b/src/write/macho.rs index cbbc6916..b5e51c90 100644 --- a/src/write/macho.rs +++ b/src/write/macho.rs @@ -46,6 +46,18 @@ impl MachOBuildVersion { } } +// Public methods. +impl<'a> Object<'a> { + /// Specify information for a Mach-O `LC_BUILD_VERSION` command. + /// + /// Requires `feature = "macho"`. + #[inline] + pub fn set_macho_build_version(&mut self, info: MachOBuildVersion) { + self.macho_build_version = Some(info); + } +} + +// Private methods. impl<'a> Object<'a> { pub(crate) fn macho_set_subsections_via_symbols(&mut self) { let flags = match self.flags { @@ -102,6 +114,10 @@ impl<'a> Object<'a> { SectionKind::TlsVariables, ), StandardSection::Common => (&b"__DATA"[..], &b"__common"[..], SectionKind::Common), + StandardSection::GnuProperty => { + // Unsupported section. + (&[], &[], SectionKind::Note) + } } } diff --git a/src/write/mod.rs b/src/write/mod.rs index da1a7de6..f2474257 100644 --- a/src/write/mod.rs +++ b/src/write/mod.rs @@ -124,15 +124,6 @@ impl<'a> Object<'a> { self.mangling = mangling; } - /// Specify information for a Mach-O `LC_BUILD_VERSION` command. - /// - /// Requires `feature = "macho"`. - #[inline] - #[cfg(feature = "macho")] - pub fn set_macho_build_version(&mut self, info: MachOBuildVersion) { - self.macho_build_version = Some(info); - } - /// Return the name for a standard segment. /// /// This will vary based on the file format. @@ -631,6 +622,8 @@ pub enum StandardSection { TlsVariables, /// Common data. Only supported for Mach-O. Common, + /// Notes for GNU properties. Only supported for ELF. + GnuProperty, } impl StandardSection { @@ -647,6 +640,7 @@ impl StandardSection { StandardSection::UninitializedTls => SectionKind::UninitializedTls, StandardSection::TlsVariables => SectionKind::TlsVariables, StandardSection::Common => SectionKind::Common, + StandardSection::GnuProperty => SectionKind::Note, } } @@ -663,6 +657,7 @@ impl StandardSection { StandardSection::UninitializedTls, StandardSection::TlsVariables, StandardSection::Common, + StandardSection::GnuProperty, ] } } diff --git a/src/write/xcoff.rs b/src/write/xcoff.rs index 56ed1029..70d8d594 100644 --- a/src/write/xcoff.rs +++ b/src/write/xcoff.rs @@ -48,6 +48,10 @@ impl<'a> Object<'a> { // Unsupported section. (&[], &[], SectionKind::Common) } + StandardSection::GnuProperty => { + // Unsupported section. + (&[], &[], SectionKind::Note) + } } } diff --git a/tests/round_trip/elf.rs b/tests/round_trip/elf.rs index 048db579..ee30b3ea 100644 --- a/tests/round_trip/elf.rs +++ b/tests/round_trip/elf.rs @@ -216,3 +216,42 @@ fn note() { assert_eq!(note.n_type(endian), 2); assert!(notes.next().unwrap().is_none()); } + +#[test] +fn gnu_property() { + gnu_property_inner::>(Architecture::I386); + gnu_property_inner::>(Architecture::X86_64); +} + +fn gnu_property_inner>(architecture: Architecture) { + let endian = Endianness::Little; + let mut object = write::Object::new(BinaryFormat::Elf, architecture, endian); + object.add_elf_gnu_property_u32( + elf::GNU_PROPERTY_X86_FEATURE_1_AND, + elf::GNU_PROPERTY_X86_FEATURE_1_IBT | elf::GNU_PROPERTY_X86_FEATURE_1_SHSTK, + ); + + let bytes = &*object.write().unwrap(); + + //std::fs::write(&"note.o", &bytes).unwrap(); + + let header = Elf::parse(bytes).unwrap(); + assert_eq!(header.endian().unwrap(), endian); + let sections = header.sections(endian, bytes).unwrap(); + let section = sections.section(SectionIndex(1)).unwrap(); + assert_eq!( + sections.section_name(endian, section).unwrap(), + b".note.gnu.property" + ); + let mut notes = section.notes(endian, bytes).unwrap().unwrap(); + let note = notes.next().unwrap().unwrap(); + let mut props = note.gnu_properties(endian).unwrap(); + let prop = props.next().unwrap().unwrap(); + assert_eq!(prop.pr_type(), elf::GNU_PROPERTY_X86_FEATURE_1_AND); + assert_eq!( + prop.data_u32(endian).unwrap(), + elf::GNU_PROPERTY_X86_FEATURE_1_IBT | elf::GNU_PROPERTY_X86_FEATURE_1_SHSTK + ); + assert!(props.next().unwrap().is_none()); + assert!(notes.next().unwrap().is_none()); +}