Skip to content

Commit

Permalink
Merge pull request #304 from mstange/macho-header-offset
Browse files Browse the repository at this point in the history
Add MachOFile::parse_at_offset
  • Loading branch information
philipc authored May 18, 2021
2 parents 0ec88f3 + 5be7f0e commit ba60aa4
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 16 deletions.
6 changes: 3 additions & 3 deletions examples/readobj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3222,14 +3222,14 @@ mod macho {
}

pub(super) fn print_macho32(p: &mut Printer<impl Write>, data: &[u8]) {
if let Ok(header) = MachHeader32::parse(data) {
if let Ok(header) = MachHeader32::parse(data, 0) {
println!("Format: Mach-O 32-bit");
print_macho(p, header, data);
}
}

pub(super) fn print_macho64(p: &mut Printer<impl Write>, data: &[u8]) {
if let Ok(header) = MachHeader64::parse(data) {
if let Ok(header) = MachHeader64::parse(data, 0) {
println!("Format: Mach-O 64-bit");
print_macho(p, header, data);
}
Expand All @@ -3248,7 +3248,7 @@ mod macho {
if let Ok(endian) = header.endian() {
let mut state = MachState::default();
print_mach_header(p, endian, header);
if let Ok(mut commands) = header.load_commands(endian, data) {
if let Ok(mut commands) = header.load_commands(endian, data, 0) {
while let Ok(Some(command)) = commands.next() {
print_load_command(p, endian, data, header, command, &mut state);
}
Expand Down
41 changes: 30 additions & 11 deletions src/read/macho/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ where
{
pub(super) endian: Mach::Endian,
pub(super) data: R,
pub(super) header_offset: u64,
pub(super) header: &'data Mach,
pub(super) sections: Vec<MachOSectionInternal<'data, Mach>>,
pub(super) symbols: SymbolTable<'data, Mach>,
Expand All @@ -45,13 +46,21 @@ where
{
/// Parse the raw Mach-O file data.
pub fn parse(data: R) -> Result<Self> {
let header = Mach::parse(data)?;
Self::parse_at_offset(data, 0)
}

/// Parse the raw Mach-O file data at an arbitrary offset inside the input data.
/// This can be used for parsing Mach-O images inside the dyld shared cache,
/// where multiple images, located at different offsets, share the same address
/// space.
pub fn parse_at_offset(data: R, header_offset: u64) -> Result<Self> {
let header = Mach::parse(data, header_offset)?;
let endian = header.endian()?;

let mut symbols = SymbolTable::default();
// Build a list of sections to make some operations more efficient.
let mut sections = Vec::new();
if let Ok(mut commands) = header.load_commands(endian, data) {
if let Ok(mut commands) = header.load_commands(endian, data, header_offset) {
while let Ok(Some(command)) = commands.next() {
if let Some((segment, section_data)) = Mach::Segment::from_command(command)? {
for section in segment.sections(endian, section_data)? {
Expand All @@ -67,6 +76,7 @@ where
Ok(MachOFile {
endian,
header,
header_offset,
sections,
symbols,
data,
Expand Down Expand Up @@ -137,7 +147,7 @@ where
file: self,
commands: self
.header
.load_commands(self.endian, self.data)
.load_commands(self.endian, self.data, self.header_offset)
.ok()
.unwrap_or_else(Default::default),
}
Expand Down Expand Up @@ -240,7 +250,9 @@ where
if twolevel {
libraries.push(&[][..]);
}
let mut commands = self.header.load_commands(self.endian, self.data)?;
let mut commands = self
.header
.load_commands(self.endian, self.data, self.header_offset)?;
while let Some(command) = commands.next()? {
if let Some(command) = command.dysymtab()? {
dysymtab = Some(command);
Expand Down Expand Up @@ -278,7 +290,9 @@ where

fn exports(&self) -> Result<Vec<Export<'data>>> {
let mut dysymtab = None;
let mut commands = self.header.load_commands(self.endian, self.data)?;
let mut commands = self
.header
.load_commands(self.endian, self.data, self.header_offset)?;
while let Some(command) = commands.next()? {
if let Some(command) = command.dysymtab()? {
dysymtab = Some(command);
Expand Down Expand Up @@ -313,11 +327,14 @@ where
}

fn mach_uuid(&self) -> Result<Option<[u8; 16]>> {
self.header.uuid(self.endian, self.data)
self.header.uuid(self.endian, self.data, self.header_offset)
}

fn entry(&self) -> u64 {
if let Ok(mut commands) = self.header.load_commands(self.endian, self.data) {
if let Ok(mut commands) =
self.header
.load_commands(self.endian, self.data, self.header_offset)
{
while let Ok(Some(command)) = commands.next() {
if let Ok(Some(command)) = command.entry_point() {
return command.entryoff.get(self.endian);
Expand Down Expand Up @@ -480,9 +497,9 @@ pub trait MachHeader: Debug + Pod {
/// Read the file header.
///
/// Also checks that the magic field in the file header is a supported format.
fn parse<'data, R: ReadRef<'data>>(data: R) -> read::Result<&'data Self> {
fn parse<'data, R: ReadRef<'data>>(data: R, offset: u64) -> read::Result<&'data Self> {
let header = data
.read_at::<Self>(0)
.read_at::<Self>(offset)
.read_error("Invalid Mach-O header size or alignment")?;
if !header.is_supported() {
return Err(Error("Unsupported Mach-O header"));
Expand All @@ -502,10 +519,11 @@ pub trait MachHeader: Debug + Pod {
&self,
endian: Self::Endian,
data: R,
header_offset: u64,
) -> Result<LoadCommandIterator<'data, Self::Endian>> {
let data = data
.read_bytes_at(
mem::size_of::<Self>() as u64,
header_offset + mem::size_of::<Self>() as u64,
self.sizeofcmds(endian).into(),
)
.read_error("Invalid Mach-O load command table size")?;
Expand All @@ -517,8 +535,9 @@ pub trait MachHeader: Debug + Pod {
&self,
endian: Self::Endian,
data: R,
header_offset: u64,
) -> Result<Option<[u8; 16]>> {
let mut commands = self.load_commands(endian, data)?;
let mut commands = self.load_commands(endian, data, header_offset)?;
while let Some(command) = commands.next()? {
if let Ok(Some(uuid)) = command.uuid() {
return Ok(Some(uuid.uuid));
Expand Down
4 changes: 2 additions & 2 deletions tests/round_trip/macho.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ fn issue_286_segment_file_size() {
object.append_section_data(text, &[1; 30], 0x1000);

let bytes = &*object.write().unwrap();
let header = macho::MachHeader64::parse(bytes).unwrap();
let header = macho::MachHeader64::parse(bytes, 0).unwrap();
let endian: Endianness = header.endian().unwrap();
let mut commands = header.load_commands(endian, bytes).unwrap();
let mut commands = header.load_commands(endian, bytes, 0).unwrap();
let command = commands.next().unwrap().unwrap();
let (segment, _) = command.segment_64().unwrap().unwrap();
assert_eq!(segment.vmsize.get(endian), 30);
Expand Down

0 comments on commit ba60aa4

Please sign in to comment.