Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mach/load_command: Add some missing load commands #240

Merged
merged 5 commits into from
Oct 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
include:
- build: MSRV # Minimum supported Rust version
os: ubuntu-latest
rust: 1.36.0
rust: 1.40.0
- build: stable
os: ubuntu-latest
rust: stable
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ https://docs.rs/goblin/

### Usage

Goblin requires `rustc` 1.36.0.
Goblin requires `rustc` 1.40.0.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t really like this but it’s fine I guess.


Add to your `Cargo.toml`

Expand Down
96 changes: 87 additions & 9 deletions src/mach/load_command.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Load commands tell the kernel and dynamic linker anything from how to load this binary into memory, what the entry point is, apple specific information, to which libraries it requires for dynamic linking

use crate::error;
use core::convert::TryFrom;
use core::fmt::{self, Display};
use scroll::{ctx, Endian};
use scroll::{IOread, IOwrite, Pread, Pwrite, SizeWith};
Expand Down Expand Up @@ -946,7 +947,8 @@ pub const SIZEOF_RPATH_COMMAND: usize = 12;
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct LinkeditDataCommand {
/// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT.
/// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE,
/// LC_DYLIB_CODE_SIGN_DRS, LC_LINKER_OPTIMIZATION_HINT, LC_DYLD_EXPORTS_TRIE, or LC_DYLD_CHAINED_FIXUPS.
pub cmd: u32,
/// sizeof(struct linkedit_data_command)
pub cmdsize: u32,
Expand Down Expand Up @@ -998,13 +1000,43 @@ pub struct EncryptionInfoCommand64 {

pub const SIZEOF_ENCRYPTION_INFO_COMMAND_64: usize = 24;

/// An enumeration of platforms currently identifiable within a version_min_command.
#[non_exhaustive]
#[repr(u32)]
#[derive(Debug)]
pub enum Platform {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these are tied to the LC values why not make it repr(u32) and set the values to the constants ? Maybe you can’t do that with constants in enums can’t remember

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought you couldn't either, but I just tried and it works 🤷. So I'll remove the trait usage in favour of repr(u32).

Macos = LC_VERSION_MIN_MACOSX,
Iphoneos = LC_VERSION_MIN_IPHONEOS,
Tvos = LC_VERSION_MIN_TVOS,
Watchos = LC_VERSION_MIN_WATCHOS,
}

impl TryFrom<u32> for Platform {
type Error = error::Error;

fn try_from(cmd: u32) -> Result<Self, Self::Error> {
Ok(match cmd {
LC_VERSION_MIN_MACOSX => Platform::Macos,
LC_VERSION_MIN_IPHONEOS => Platform::Iphoneos,
LC_VERSION_MIN_TVOS => Platform::Tvos,
LC_VERSION_MIN_WATCHOS => Platform::Watchos,
_ => {
return Err(error::Error::Malformed(format!(
"unknown platform for load command: {:x}",
cmd
)))
}
})
}
}

/// The version_min_command contains the min OS version on which this
/// binary was built to run.
///
/// LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS
#[repr(C)]
#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct VersionMinCommand {
/// LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_TVOS, or LC_VERSION_MIN_WATCHOS.
pub cmd: u32,
pub cmdsize: u32,
/// X.Y.Z is encoded in nibbles xxxx.yy.zz
Expand All @@ -1014,18 +1046,22 @@ pub struct VersionMinCommand {
}

impl VersionMinCommand {
pub fn new(is_ios: bool) -> Self {
pub fn new(platform: Platform) -> Self {
VersionMinCommand {
cmd: if is_ios {
LC_VERSION_MIN_IPHONEOS
} else {
LC_VERSION_MIN_MACOSX
},
cmd: platform as u32,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a new feature ? How is this cast working since Platform isn’t repr(u32).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this is part of the API breakage -- VersionMinCommand can be produced from one of four different LC_* constants, which are represented by that enum (since they might be useful elsewhere).

As for why it's working: I thought it was because I had implemented From<Platform> for u32, but I tried removing that trait impl and it still compiled 😟. So I'll change it to platform.into() to make sure it's using the trait explicitly.

m4b marked this conversation as resolved.
Show resolved Hide resolved
cmdsize: SIZEOF_VERSION_MIN_COMMAND as u32,
version: 0,
sdk: 0,
}
}

pub fn platform(&self) -> Platform {
// A panic here indicates an incomplete API change above: VersionMinCommand
// can only be constructed from one of the LC_VERSION_* commands or directly
// from a Platform, so an error indicates that a new one hasn't been correctly
// added to the Platform enum.
Platform::try_from(self.cmd).expect("impossible platform (implementation error)")
}
}

pub const SIZEOF_VERSION_MIN_COMMAND: usize = 16;
Expand Down Expand Up @@ -1184,6 +1220,8 @@ pub const LC_REEXPORT_DYLIB: u32 = 0x1f | LC_REQ_DYLD;
pub const LC_DYLD_INFO_ONLY: u32 = 0x22 | LC_REQ_DYLD;
pub const LC_LOAD_UPWARD_DYLIB: u32 = 0x23 | LC_REQ_DYLD;
pub const LC_MAIN: u32 = 0x28 | LC_REQ_DYLD;
pub const LC_DYLD_EXPORTS_TRIE: u32 = 0x33 | LC_REQ_DYLD;
pub const LC_DYLD_CHAINED_FIXUPS: u32 = 0x34 | LC_REQ_DYLD;
pub const LC_SEGMENT: u32 = 0x1;
pub const LC_SYMTAB: u32 = 0x2;
pub const LC_SYMSEG: u32 = 0x3;
Expand Down Expand Up @@ -1225,6 +1263,10 @@ pub const LC_DYLIB_CODE_SIGN_DRS: u32 = 0x2B;
pub const LC_ENCRYPTION_INFO_64: u32 = 0x2C;
pub const LC_LINKER_OPTION: u32 = 0x2D;
pub const LC_LINKER_OPTIMIZATION_HINT: u32 = 0x2E;
pub const LC_VERSION_MIN_TVOS: u32 = 0x2F;
pub const LC_VERSION_MIN_WATCHOS: u32 = 0x30;
pub const LC_NOTE: u32 = 0x31;
pub const LC_BUILD_VERSION: u32 = 0x32;

pub fn cmd_to_str(cmd: u32) -> &'static str {
match cmd {
Expand Down Expand Up @@ -1275,6 +1317,12 @@ pub fn cmd_to_str(cmd: u32) -> &'static str {
LC_ENCRYPTION_INFO_64 => "LC_ENCRYPTION_INFO_64",
LC_LINKER_OPTION => "LC_LINKER_OPTION",
LC_LINKER_OPTIMIZATION_HINT => "LC_LINKER_OPTIMIZATION_HINT",
LC_VERSION_MIN_TVOS => "LC_VERSION_MIN_TVOS",
LC_VERSION_MIN_WATCHOS => "LC_VERSION_MIN_WATCHOS",
LC_NOTE => "LC_NOTE",
LC_BUILD_VERSION => "LC_BUILD_VERSION",
LC_DYLD_EXPORTS_TRIE => "LC_DYLD_EXPORTS_TRIE",
LC_DYLD_CHAINED_FIXUPS => "LC_DYLD_CHAINED_FIXUPS",
_ => "LC_UNKNOWN",
}
}
Expand Down Expand Up @@ -1334,6 +1382,10 @@ pub enum CommandVariant {
DylibCodeSignDrs(LinkeditDataCommand),
LinkerOption(LinkeditDataCommand),
LinkerOptimizationHint(LinkeditDataCommand),
VersionMinTvos(VersionMinCommand),
VersionMinWatchos(VersionMinCommand),
DyldExportsTrie(LinkeditDataCommand),
DyldChainedFixups(LinkeditDataCommand),
Unimplemented(LoadCommandHeader),
}

Expand Down Expand Up @@ -1540,7 +1592,25 @@ impl<'a> ctx::TryFromCtx<'a, Endian> for CommandVariant {
let comm = bytes.pread_with::<LinkeditDataCommand>(0, le)?;
Ok((LinkerOptimizationHint(comm), size))
}
_ => Ok((Unimplemented(lc), size)),
LC_VERSION_MIN_TVOS => {
let comm = bytes.pread_with::<VersionMinCommand>(0, le)?;
Ok((VersionMinTvos(comm), size))
}
LC_VERSION_MIN_WATCHOS => {
let comm = bytes.pread_with::<VersionMinCommand>(0, le)?;
Ok((VersionMinWatchos(comm), size))
}
LC_DYLD_EXPORTS_TRIE => {
let comm = bytes.pread_with::<LinkeditDataCommand>(0, le)?;
Ok((DyldExportsTrie(comm), size))
}
LC_DYLD_CHAINED_FIXUPS => {
let comm = bytes.pread_with::<LinkeditDataCommand>(0, le)?;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and above are both LinkEditDataCommand? Interesting

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, they both reside in __linkedit, like a few others.

Ok((DyldChainedFixups(comm), size))
}
// TODO: LC_NOTE (NoteCommand) and LC_BUILD_VERSION (BuildVersionCommand)
// are unimplemented.
LC_NOTE | LC_BUILD_VERSION | _ => Ok((Unimplemented(lc), size)),
}
}
}
Expand Down Expand Up @@ -1596,6 +1666,10 @@ impl CommandVariant {
DylibCodeSignDrs(comm) => comm.cmdsize,
LinkerOption(comm) => comm.cmdsize,
LinkerOptimizationHint(comm) => comm.cmdsize,
VersionMinTvos(comm) => comm.cmdsize,
VersionMinWatchos(comm) => comm.cmdsize,
DyldExportsTrie(comm) => comm.cmdsize,
DyldChainedFixups(comm) => comm.cmdsize,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don’t need to do this now but might be a good idea to add non exhaustive annotations to these enums as much well

Unimplemented(comm) => comm.cmdsize,
};
cmdsize as usize
Expand Down Expand Up @@ -1650,6 +1724,10 @@ impl CommandVariant {
DylibCodeSignDrs(comm) => comm.cmd,
LinkerOption(comm) => comm.cmd,
LinkerOptimizationHint(comm) => comm.cmd,
VersionMinTvos(comm) => comm.cmd,
VersionMinWatchos(comm) => comm.cmd,
DyldExportsTrie(comm) => comm.cmd,
DyldChainedFixups(comm) => comm.cmd,
Unimplemented(comm) => comm.cmd,
}
}
Expand Down