diff options
Diffstat (limited to 'third_party/rust/goblin/src/mach/segment.rs')
-rw-r--r-- | third_party/rust/goblin/src/mach/segment.rs | 558 |
1 files changed, 558 insertions, 0 deletions
diff --git a/third_party/rust/goblin/src/mach/segment.rs b/third_party/rust/goblin/src/mach/segment.rs new file mode 100644 index 0000000000..3c87bd62ed --- /dev/null +++ b/third_party/rust/goblin/src/mach/segment.rs @@ -0,0 +1,558 @@ +use scroll::ctx::{self, SizeWith}; +use scroll::{Pread, Pwrite}; + +use log::{debug, warn}; + +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::fmt; +use core::ops::{Deref, DerefMut}; + +use crate::container; +use crate::error; + +use crate::mach::constants::{SECTION_TYPE, S_GB_ZEROFILL, S_THREAD_LOCAL_ZEROFILL, S_ZEROFILL}; +use crate::mach::load_command::{ + Section32, Section64, SegmentCommand32, SegmentCommand64, LC_SEGMENT, LC_SEGMENT_64, + SIZEOF_SECTION_32, SIZEOF_SECTION_64, SIZEOF_SEGMENT_COMMAND_32, SIZEOF_SEGMENT_COMMAND_64, +}; +use crate::mach::relocation::RelocationInfo; + +pub struct RelocationIterator<'a> { + data: &'a [u8], + nrelocs: usize, + offset: usize, + count: usize, + ctx: scroll::Endian, +} + +impl<'a> Iterator for RelocationIterator<'a> { + type Item = error::Result<RelocationInfo>; + fn next(&mut self) -> Option<Self::Item> { + if self.count >= self.nrelocs { + None + } else { + self.count += 1; + match self.data.gread_with(&mut self.offset, self.ctx) { + Ok(res) => Some(Ok(res)), + Err(e) => Some(Err(e.into())), + } + } + } +} + +/// Generalized 32/64 bit Section +#[derive(Default)] +pub struct Section { + /// name of this section + pub sectname: [u8; 16], + /// segment this section goes in + pub segname: [u8; 16], + /// memory address of this section + pub addr: u64, + /// size in bytes of this section + pub size: u64, + /// file offset of this section + pub offset: u32, + /// section alignment (power of 2) + pub align: u32, + /// file offset of relocation entries + pub reloff: u32, + /// number of relocation entries + pub nreloc: u32, + /// flags (section type and attributes + pub flags: u32, +} + +impl Section { + /// The name of this section + pub fn name(&self) -> error::Result<&str> { + Ok(self.sectname.pread::<&str>(0)?) + } + /// The containing segment's name + pub fn segname(&self) -> error::Result<&str> { + Ok(self.segname.pread::<&str>(0)?) + } + /// Iterate this sections relocations given `data`; `data` must be the original binary + pub fn iter_relocations<'b>( + &self, + data: &'b [u8], + ctx: container::Ctx, + ) -> RelocationIterator<'b> { + let offset = self.reloff as usize; + debug!( + "Relocations for {} starting at offset: {:#x}", + self.name().unwrap_or("BAD_SECTION_NAME"), + offset + ); + RelocationIterator { + offset, + nrelocs: self.nreloc as usize, + count: 0, + data, + ctx: ctx.le, + } + } +} + +impl From<Section> for Section64 { + fn from(section: Section) -> Self { + Section64 { + sectname: section.sectname, + segname: section.segname, + addr: section.addr as u64, + size: section.size as u64, + offset: section.offset, + align: section.align, + reloff: section.reloff, + nreloc: section.nreloc, + flags: section.flags, + reserved1: 0, + reserved2: 0, + reserved3: 0, + } + } +} + +impl From<Section> for Section32 { + fn from(section: Section) -> Self { + Section32 { + sectname: section.sectname, + segname: section.segname, + addr: section.addr as u32, + size: section.size as u32, + offset: section.offset, + align: section.align, + reloff: section.reloff, + nreloc: section.nreloc, + flags: section.flags, + reserved1: 0, + reserved2: 0, + } + } +} + +impl fmt::Debug for Section { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Section") + .field("sectname", &self.name().unwrap()) + .field("segname", &self.segname().unwrap()) + .field("addr", &self.addr) + .field("size", &self.size) + .field("offset", &self.offset) + .field("align", &self.align) + .field("reloff", &self.reloff) + .field("nreloc", &self.nreloc) + .field("flags", &self.flags) + .finish() + } +} + +impl From<Section32> for Section { + fn from(section: Section32) -> Self { + Section { + sectname: section.sectname, + segname: section.segname, + addr: u64::from(section.addr), + size: u64::from(section.size), + offset: section.offset, + align: section.align, + reloff: section.reloff, + nreloc: section.nreloc, + flags: section.flags, + } + } +} + +impl From<Section64> for Section { + fn from(section: Section64) -> Self { + Section { + sectname: section.sectname, + segname: section.segname, + addr: section.addr, + size: section.size, + offset: section.offset, + align: section.align, + reloff: section.reloff, + nreloc: section.nreloc, + flags: section.flags, + } + } +} + +impl<'a> ctx::TryFromCtx<'a, container::Ctx> for Section { + type Error = crate::error::Error; + fn try_from_ctx(bytes: &'a [u8], ctx: container::Ctx) -> Result<(Self, usize), Self::Error> { + match ctx.container { + container::Container::Little => { + let section = Section::from(bytes.pread_with::<Section32>(0, ctx.le)?); + Ok((section, SIZEOF_SECTION_32)) + } + container::Container::Big => { + let section = Section::from(bytes.pread_with::<Section64>(0, ctx.le)?); + Ok((section, SIZEOF_SECTION_64)) + } + } + } +} + +impl ctx::SizeWith<container::Ctx> for Section { + fn size_with(ctx: &container::Ctx) -> usize { + match ctx.container { + container::Container::Little => SIZEOF_SECTION_32, + container::Container::Big => SIZEOF_SECTION_64, + } + } +} + +impl ctx::TryIntoCtx<container::Ctx> for Section { + type Error = crate::error::Error; + fn try_into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) -> Result<usize, Self::Error> { + if ctx.is_big() { + bytes.pwrite_with::<Section64>(self.into(), 0, ctx.le)?; + } else { + bytes.pwrite_with::<Section32>(self.into(), 0, ctx.le)?; + } + Ok(Self::size_with(&ctx)) + } +} + +impl ctx::IntoCtx<container::Ctx> for Section { + fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) { + bytes.pwrite_with(self, 0, ctx).unwrap(); + } +} + +pub struct SectionIterator<'a> { + data: &'a [u8], + count: usize, + offset: usize, + idx: usize, + ctx: container::Ctx, +} + +pub type SectionData<'a> = &'a [u8]; + +impl<'a> ::core::iter::ExactSizeIterator for SectionIterator<'a> { + fn len(&self) -> usize { + self.count + } +} + +impl<'a> Iterator for SectionIterator<'a> { + type Item = error::Result<(Section, SectionData<'a>)>; + fn next(&mut self) -> Option<Self::Item> { + if self.idx >= self.count { + None + } else { + self.idx += 1; + match self.data.gread_with::<Section>(&mut self.offset, self.ctx) { + Ok(section) => { + let section_type = section.flags & SECTION_TYPE; + let data = if section_type == S_ZEROFILL + || section_type == S_GB_ZEROFILL + || section_type == S_THREAD_LOCAL_ZEROFILL + { + &[] + } else { + // it's not uncommon to encounter macho files where files are + // truncated but the sections are still remaining in the header. + // Because of this we want to not panic here but instead just + // slice down to a empty data slice. This way only if code + // actually needs to access those sections it will fall over. + self.data + .get(section.offset as usize..) + .unwrap_or_else(|| { + warn!( + "section #{} offset {} out of bounds", + self.idx, section.offset + ); + &[] + }) + .get(..section.size as usize) + .unwrap_or_else(|| { + warn!("section #{} size {} out of bounds", self.idx, section.size); + &[] + }) + }; + Some(Ok((section, data))) + } + Err(e) => Some(Err(e)), + } + } + } +} + +impl<'a, 'b> IntoIterator for &'b Segment<'a> { + type Item = error::Result<(Section, SectionData<'a>)>; + type IntoIter = SectionIterator<'a>; + fn into_iter(self) -> Self::IntoIter { + SectionIterator { + data: self.raw_data, + count: self.nsects as usize, + offset: self.offset + Segment::size_with(&self.ctx), + idx: 0, + ctx: self.ctx, + } + } +} + +/// Generalized 32/64 bit Segment Command +pub struct Segment<'a> { + pub cmd: u32, + pub cmdsize: u32, + pub segname: [u8; 16], + pub vmaddr: u64, + pub vmsize: u64, + pub fileoff: u64, + pub filesize: u64, + pub maxprot: u32, + pub initprot: u32, + pub nsects: u32, + pub flags: u32, + pub data: &'a [u8], + offset: usize, + raw_data: &'a [u8], + ctx: container::Ctx, +} + +impl<'a> From<Segment<'a>> for SegmentCommand64 { + fn from(segment: Segment<'a>) -> Self { + SegmentCommand64 { + cmd: segment.cmd, + cmdsize: segment.cmdsize, + segname: segment.segname, + vmaddr: segment.vmaddr as u64, + vmsize: segment.vmsize as u64, + fileoff: segment.fileoff as u64, + filesize: segment.filesize as u64, + maxprot: segment.maxprot, + initprot: segment.initprot, + nsects: segment.nsects, + flags: segment.flags, + } + } +} + +impl<'a> From<Segment<'a>> for SegmentCommand32 { + fn from(segment: Segment<'a>) -> Self { + SegmentCommand32 { + cmd: segment.cmd, + cmdsize: segment.cmdsize, + segname: segment.segname, + vmaddr: segment.vmaddr as u32, + vmsize: segment.vmsize as u32, + fileoff: segment.fileoff as u32, + filesize: segment.filesize as u32, + maxprot: segment.maxprot, + initprot: segment.initprot, + nsects: segment.nsects, + flags: segment.flags, + } + } +} + +impl<'a> fmt::Debug for Segment<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Segment") + .field("cmd", &self.cmd) + .field("cmdsize", &self.cmdsize) + .field("segname", &self.segname.pread::<&str>(0).unwrap()) + .field("vmaddr", &self.vmaddr) + .field("vmsize", &self.vmsize) + .field("fileoff", &self.fileoff) + .field("filesize", &self.filesize) + .field("maxprot", &self.maxprot) + .field("initprot", &self.initprot) + .field("nsects", &self.nsects) + .field("flags", &self.flags) + .field("data", &self.data.len()) + .field( + "sections()", + &self.sections().map(|sections| { + sections + .into_iter() + .map(|(section, _)| section) + .collect::<Vec<_>>() + }), + ) + .finish() + } +} + +impl<'a> ctx::SizeWith<container::Ctx> for Segment<'a> { + fn size_with(ctx: &container::Ctx) -> usize { + match ctx.container { + container::Container::Little => SIZEOF_SEGMENT_COMMAND_32, + container::Container::Big => SIZEOF_SEGMENT_COMMAND_64, + } + } +} + +impl<'a> ctx::TryIntoCtx<container::Ctx> for Segment<'a> { + type Error = crate::error::Error; + fn try_into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) -> Result<usize, Self::Error> { + let segment_size = Self::size_with(&ctx); + // should be able to write the section data inline after this, but not working at the moment + //let section_size = bytes.pwrite(data, segment_size)?; + //debug!("Segment size: {} raw section data size: {}", segment_size, data.len()); + if ctx.is_big() { + bytes.pwrite_with::<SegmentCommand64>(self.into(), 0, ctx.le)?; + } else { + bytes.pwrite_with::<SegmentCommand32>(self.into(), 0, ctx.le)?; + } + //debug!("Section size: {}", section_size); + Ok(segment_size) + } +} + +impl<'a> ctx::IntoCtx<container::Ctx> for Segment<'a> { + fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) { + bytes.pwrite_with(self, 0, ctx).unwrap(); + } +} + +/// Read data that belongs to a segment if the offset is within the boundaries of bytes. +fn segment_data(bytes: &[u8], fileoff: u64, filesize: u64) -> Result<&[u8], error::Error> { + let data: &[u8] = if filesize != 0 { + bytes.pread_with(fileoff as usize, filesize as usize)? + } else { + &[] + }; + Ok(data) +} + +impl<'a> Segment<'a> { + /// Create a new, blank segment, with cmd either `LC_SEGMENT_64`, or `LC_SEGMENT`, depending on `ctx`. + /// **NB** You are responsible for providing a correctly marshalled byte array as the sections. You should not use this for anything other than writing. + pub fn new(ctx: container::Ctx, sections: &'a [u8]) -> Self { + Segment { + cmd: if ctx.is_big() { + LC_SEGMENT_64 + } else { + LC_SEGMENT + }, + cmdsize: (Self::size_with(&ctx) + sections.len()) as u32, + segname: [0; 16], + vmaddr: 0, + vmsize: 0, + fileoff: 0, + filesize: 0, + maxprot: 0, + initprot: 0, + nsects: 0, + flags: 0, + data: sections, + offset: 0, + raw_data: &[], + ctx, + } + } + /// Get the name of this segment + pub fn name(&self) -> error::Result<&str> { + Ok(self.segname.pread::<&str>(0)?) + } + /// Get the sections from this segment, erroring if any section couldn't be retrieved + pub fn sections(&self) -> error::Result<Vec<(Section, SectionData<'a>)>> { + let mut sections = Vec::new(); + for section in self.into_iter() { + sections.push(section?); + } + Ok(sections) + } + /// Convert the raw C 32-bit segment command to a generalized version + pub fn from_32( + bytes: &'a [u8], + segment: &SegmentCommand32, + offset: usize, + ctx: container::Ctx, + ) -> Result<Self, error::Error> { + Ok(Segment { + cmd: segment.cmd, + cmdsize: segment.cmdsize, + segname: segment.segname, + vmaddr: u64::from(segment.vmaddr), + vmsize: u64::from(segment.vmsize), + fileoff: u64::from(segment.fileoff), + filesize: u64::from(segment.filesize), + maxprot: segment.maxprot, + initprot: segment.initprot, + nsects: segment.nsects, + flags: segment.flags, + data: segment_data( + bytes, + u64::from(segment.fileoff), + u64::from(segment.filesize), + )?, + offset, + raw_data: bytes, + ctx, + }) + } + /// Convert the raw C 64-bit segment command to a generalized version + pub fn from_64( + bytes: &'a [u8], + segment: &SegmentCommand64, + offset: usize, + ctx: container::Ctx, + ) -> Result<Self, error::Error> { + Ok(Segment { + cmd: segment.cmd, + cmdsize: segment.cmdsize, + segname: segment.segname, + vmaddr: segment.vmaddr, + vmsize: segment.vmsize, + fileoff: segment.fileoff, + filesize: segment.filesize, + maxprot: segment.maxprot, + initprot: segment.initprot, + nsects: segment.nsects, + flags: segment.flags, + data: segment_data(bytes, segment.fileoff, segment.filesize)?, + offset, + raw_data: bytes, + ctx, + }) + } +} + +#[derive(Debug, Default)] +/// An opaque 32/64-bit container for Mach-o segments +pub struct Segments<'a> { + segments: Vec<Segment<'a>>, +} + +impl<'a> Deref for Segments<'a> { + type Target = Vec<Segment<'a>>; + fn deref(&self) -> &Self::Target { + &self.segments + } +} + +impl<'a> DerefMut for Segments<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.segments + } +} + +impl<'a, 'b> IntoIterator for &'b Segments<'a> { + type Item = &'b Segment<'a>; + type IntoIter = ::core::slice::Iter<'b, Segment<'a>>; + fn into_iter(self) -> Self::IntoIter { + self.segments.iter() + } +} + +impl<'a> Segments<'a> { + /// Construct a new generalized segment container from this `ctx` + pub fn new(_ctx: container::Ctx) -> Self { + Segments { + segments: Vec::new(), + } + } + /// Get every section from every segment + // thanks to SpaceManic for figuring out the 'b lifetimes here :) + pub fn sections<'b>(&'b self) -> Box<dyn Iterator<Item = SectionIterator<'a>> + 'b> { + Box::new(self.segments.iter().map(|segment| segment.into_iter())) + } +} |