Skip to content

Commit

Permalink
read/macho: fix data for segments in dyld caches (#673)
Browse files Browse the repository at this point in the history
Use the correct subcache for operations on the segment itself.
Individual section data was already using the correct subcache.

This also does the change for section relocations, but they shouldn't
be present in the dyld cache.
  • Loading branch information
philipc committed Apr 30, 2024
1 parent 9650d64 commit 74c757c
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 32 deletions.
20 changes: 5 additions & 15 deletions src/read/macho/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ where
pub(super) header_offset: u64,
pub(super) header: &'data Mach,
pub(super) segments: Vec<MachOSegmentInternal<'data, Mach, R>>,
pub(super) sections: Vec<MachOSectionInternal<'data, Mach>>,
pub(super) sections: Vec<MachOSectionInternal<'data, Mach, R>>,
pub(super) symbols: SymbolTable<'data, Mach, R>,
}

Expand All @@ -65,11 +65,10 @@ where
if let Ok(mut commands) = header.load_commands(endian, data, 0) {
while let Ok(Some(command)) = commands.next() {
if let Some((segment, section_data)) = Mach::Segment::from_command(command)? {
let segment_index = segments.len();
segments.push(MachOSegmentInternal { segment, data });
for section in segment.sections(endian, section_data)? {
let index = SectionIndex(sections.len() + 1);
sections.push(MachOSectionInternal::parse(index, segment_index, section));
sections.push(MachOSectionInternal::parse(index, section, data));
}
} else if let Some(symtab) = command.symtab()? {
symbols = symtab.symbols(endian, data)?;
Expand Down Expand Up @@ -110,6 +109,7 @@ where
if let Some((segment, section_data)) = Mach::Segment::from_command(command)? {
// Each segment can be stored in a different subcache. Get the segment's
// address and look it up in the cache mappings, to find the correct cache data.
// This was observed for the arm64e __LINKEDIT segment in macOS 12.0.1.
let addr = segment.vmaddr(endian).into();
let (data, _offset) = image
.cache
Expand All @@ -118,12 +118,11 @@ where
if segment.name() == macho::SEG_LINKEDIT.as_bytes() {
linkedit_data = Some(data);
}
let segment_index = segments.len();
segments.push(MachOSegmentInternal { segment, data });

for section in segment.sections(endian, section_data)? {
let index = SectionIndex(sections.len() + 1);
sections.push(MachOSectionInternal::parse(index, segment_index, section));
sections.push(MachOSectionInternal::parse(index, section, data));
}
} else if let Some(st) = command.symtab()? {
symtab = Some(st);
Expand Down Expand Up @@ -154,23 +153,14 @@ where
pub(super) fn section_internal(
&self,
index: SectionIndex,
) -> Result<&MachOSectionInternal<'data, Mach>> {
) -> Result<&MachOSectionInternal<'data, Mach, R>> {
index
.0
.checked_sub(1)
.and_then(|index| self.sections.get(index))
.read_error("Invalid Mach-O section index")
}

pub(super) fn segment_internal(
&self,
index: usize,
) -> Result<&MachOSegmentInternal<'data, Mach, R>> {
self.segments
.get(index)
.read_error("Invalid Mach-O segment index")
}

/// Returns the endianness.
pub fn endian(&self) -> Mach::Endian {
self.endian
Expand Down
28 changes: 13 additions & 15 deletions src/read/macho/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ where
R: ReadRef<'data>,
{
pub(super) file: &'file MachOFile<'data, Mach, R>,
pub(super) iter: slice::Iter<'file, MachOSectionInternal<'data, Mach>>,
pub(super) iter: slice::Iter<'file, MachOSectionInternal<'data, Mach, R>>,
}

impl<'data, 'file, Mach, R> fmt::Debug for MachOSectionIterator<'data, 'file, Mach, R>
Expand Down Expand Up @@ -71,7 +71,7 @@ where
R: ReadRef<'data>,
{
pub(super) file: &'file MachOFile<'data, Mach, R>,
pub(super) internal: MachOSectionInternal<'data, Mach>,
pub(super) internal: MachOSectionInternal<'data, Mach, R>,
}

impl<'data, 'file, Mach, R> MachOSection<'data, 'file, Mach, R>
Expand All @@ -80,11 +80,9 @@ where
R: ReadRef<'data>,
{
fn bytes(&self) -> Result<&'data [u8]> {
let segment_index = self.internal.segment_index;
let segment = self.file.segment_internal(segment_index)?;
self.internal
.section
.data(self.file.endian, segment.data)
.data(self.file.endian, self.internal.data)
.read_error("Invalid Mach-O section size or offset")
}
}
Expand Down Expand Up @@ -193,7 +191,7 @@ where
relocations: self
.internal
.section
.relocations(self.file.endian, self.file.data)
.relocations(self.file.endian, self.internal.data)
.unwrap_or(&[])
.iter(),
}
Expand All @@ -211,19 +209,19 @@ where
}

#[derive(Debug, Clone, Copy)]
pub(super) struct MachOSectionInternal<'data, Mach: MachHeader> {
pub(super) struct MachOSectionInternal<'data, Mach: MachHeader, R: ReadRef<'data>> {
pub index: SectionIndex,
pub segment_index: usize,
pub kind: SectionKind,
pub section: &'data Mach::Section,
/// The data for the file that contains the section data.
///
/// This is required for dyld caches, where this may be a different subcache
/// from the file containing the Mach-O load commands.
pub data: R,
}

impl<'data, Mach: MachHeader> MachOSectionInternal<'data, Mach> {
pub(super) fn parse(
index: SectionIndex,
segment_index: usize,
section: &'data Mach::Section,
) -> Self {
impl<'data, Mach: MachHeader, R: ReadRef<'data>> MachOSectionInternal<'data, Mach, R> {
pub(super) fn parse(index: SectionIndex, section: &'data Mach::Section, data: R) -> Self {
// TODO: we don't validate flags, should we?
let kind = match (section.segment_name(), section.name()) {
(b"__TEXT", b"__text") => SectionKind::Text,
Expand All @@ -246,9 +244,9 @@ impl<'data, Mach: MachHeader> MachOSectionInternal<'data, Mach> {
};
MachOSectionInternal {
index,
segment_index,
kind,
section,
data,
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/read/macho/segment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ where
fn bytes(&self) -> Result<&'data [u8]> {
self.internal
.segment
.data(self.file.endian, self.file.data)
.data(self.file.endian, self.internal.data)
.read_error("Invalid Mach-O segment size or offset")
}
}
Expand Down Expand Up @@ -149,8 +149,12 @@ where

#[derive(Debug, Clone, Copy)]
pub(super) struct MachOSegmentInternal<'data, Mach: MachHeader, R: ReadRef<'data>> {
pub data: R,
pub segment: &'data Mach::Segment,
/// The data for the file that contains the segment data.
///
/// This is required for dyld caches, where this may be a different subcache
/// from the file containing the Mach-O load commands.
pub data: R,
}

/// A trait for generic access to [`macho::SegmentCommand32`] and [`macho::SegmentCommand64`].
Expand Down

0 comments on commit 74c757c

Please sign in to comment.