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

Conversation

woodruffw
Copy link
Contributor

First of all, thanks for this library! It's a pleasure to use.

I'm the maintainer of ruby-macho, and I noticed that these load commands were missing from your Mach-O implementation. I've taken the liberty of adding them; please let me know if there's anything else I can do.


Commit message:

Adds support for LC_VERSION_MIN_{TV,WATCH}OS, as well as
LC_VERSION_EXPORTS_TRIE and LC_VERSION_CHAINED_FIXUPS.
Each of the above maps to a pre-existing load command structure,
so no additional structure definitions were necessary.

Also adds constant support for LC_NOTE and LC_BUILD_VERSION.
These require new load command structures (and substructures, in the
case of LC_BUILD_VERSION), so I left them out of this PR in
the interest of brevity. I can do them in a follow-up PR,
if desired.

Adds support for `LC_VERSION_MIN_{TV,WATCH}OS`, as well as
`LC_VERSION_EXPORTS_TRIE` and `LC_VERSION_CHAINED_FIXUPS`.
Each of the above maps to a pre-existing load command structure,
so no additional structure definitions were necessary.

Also adds constant support for `LC_NOTE` and `LC_BUILD_VERSION`.
These require new load command structures (and substructures, in the
case of `LC_BUILD_VERSION`), so I left them out of this PR in
the interest of brevity. I can do them in a follow-up PR,
if desired.
Copy link
Collaborator

@willglynn willglynn left a comment

Choose a reason for hiding this comment

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

LGTM, thanks! I would welcome followup PRs that bring goblin back to parity with current #include <mach-o/*.h>.

@@ -1014,6 +1015,7 @@ pub struct VersionMinCommand {
}

impl VersionMinCommand {
// TODO: Needs a discriminant for TvOS, WatchOS.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, we ought to break this interface. Maybe add a new #[non_exhaustive] enum Platform and add a fn platform(&self) -> Platform while we're at it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

SGTM! I can make that part of this PR.

Uses the Platform enum to supply the appropriate load command number
to `VersionMinCommand`, and implements the appropriate lossless/lossy
traits for round-tripping with a `u32`.
@woodruffw
Copy link
Contributor Author

Okay, I've added a Platform enum and the appropriate conversion traits. One slightly messy API detail: platform() currently returns a Result<Platform> instead of a Platform directly, since u32 -> Platform is implemented with the lossy TryFrom.

It's technically safe to remove that Result with an unwrap (since the only ways to create a VersionMinCommand ensure that self.cmd is always valid), but it's difficult to prove that at the type level with the current API. Thoughts? I'm happy to go either way 🙂

@woodruffw
Copy link
Contributor Author

woodruffw commented Oct 11, 2020

Also, TIL that non_exhaustive was only stabilized in 1.40. That means bumping the MSRV from the current (1.36), or just dropping that attribute. Again, either's fine by me, I figure this is a question for the maintainers 🙂

Edit: I'm going to push up a commit that bumps the MSRV, just to push the changes through the CI. Happy to drop it and make the other change, if desired.

@m4b
Copy link
Owner

m4b commented Oct 11, 2020

Going to review in a bit, thanks for this! So this is a breaking change right (which is fine, might as well do this now)?

@woodruffw
Copy link
Contributor Author

So this is a breaking change right (which is fine, might as well do this now)?

Yep, correct -- this changes the public API for VersionMinCommand::new.

Copy link
Owner

@m4b m4b left a comment

Choose a reason for hiding this comment

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

Only real question I have is whether the Platform should be repr(u32) (doesn’t have to be maybe there’s reasons to keep it away from the backing u32 consts?)

} 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.

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

src/mach/load_command.rs Show resolved Hide resolved
/// An enumeration of platforms currently identifiable within a version_min_command.
#[non_exhaustive]
#[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).

@@ -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.

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.

@m4b
Copy link
Owner

m4b commented Oct 12, 2020

I’m ok with bumping msrv. Re The unwrapping issue, id day if it really is safe to unwrap then do it (but use expect); but if it’s the possible, e.g. with a malformed binary, etc, to have that unwrap fail, then leave it as is :)

@woodruffw
Copy link
Contributor Author

Re The unwrapping issue, id day if it really is safe to unwrap then do it (but use expect); but if it’s the possible, e.g. with a malformed binary, etc, to have that unwrap fail, then leave it as is :)

Sounds good! It should always be safe since the only ways to construct a VersionMinCommand constrain cmd to the variants we expect, so I'll use expect.

Copy link
Owner

@m4b m4b left a comment

Choose a reason for hiding this comment

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

Awesome, thank you for this PR! ❤️

@m4b m4b merged commit b4cf6f5 into m4b:master Oct 13, 2020
@m4b
Copy link
Owner

m4b commented Oct 13, 2020

@woodruffw I hope you don't mind, but I squashed and merged; i find this makes PR's cleaner generally, easier to bisect, known passing commit, etc. It's not a hard and fast rule but I prefer it more and more. People do get less "commit credit" so i do give them a heads up so they don't get sad :D

@woodruffw
Copy link
Contributor Author

woodruffw commented Oct 13, 2020 via email

@woodruffw woodruffw deleted the ww/more-lcs branch October 13, 2020 14:40
@m4b
Copy link
Owner

m4b commented Nov 26, 2020

ok this is wrapped up in upcoming 0.3 release

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants