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

Add support for writing a mach-o file with an LC_BUILD_VERSION command #524

Merged
merged 1 commit into from
Mar 27, 2023
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
42 changes: 42 additions & 0 deletions src/write/macho.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,30 @@ struct SymbolOffsets {
str_id: Option<StringId>,
}

/// The customizable portion of a [`macho::BuildVersionCommand`].
#[derive(Debug, Default, Clone, Copy)]
#[non_exhaustive] // May want to add the tool list?
pub struct MachOBuildVersion {
/// One of the `PLATFORM_` constants (for example,
/// [`object::macho::PLATFORM_MACOS`](macho::PLATFORM_MACOS)).
pub platform: u32,
/// The minimum OS version, where `X.Y.Z` is encoded in nibbles as
/// `xxxx.yy.zz`.
pub minos: u32,
/// The SDK version as `X.Y.Z`, where `X.Y.Z` is encoded in nibbles as
/// `xxxx.yy.zz`.
pub sdk: u32,
}

impl MachOBuildVersion {
fn cmdsize(&self) -> u32 {
// Same size for both endianness, and we don't have `ntools`.
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think endianness ever changes sizes. Maybe you meant both address sizes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It doesn't, I was just being clear why we didn't need anything passed in from the caller (and just picked an arbitrary Endianess).

let sz = mem::size_of::<macho::BuildVersionCommand<Endianness>>();
debug_assert!(sz <= u32::MAX as usize);
sz as u32
}
}

impl<'a> Object<'a> {
pub(crate) fn macho_set_subsections_via_symbols(&mut self) {
let flags = match self.flags {
Expand Down Expand Up @@ -213,6 +237,12 @@ impl<'a> Object<'a> {
let mut ncmds = 0;
let command_offset = offset;

let build_version_offset = offset;
if let Some(version) = &self.macho_build_version {
offset += version.cmdsize() as usize;
ncmds += 1;
}

// Calculate size of segment command and section headers.
let segment_command_offset = offset;
let segment_command_len =
Expand Down Expand Up @@ -352,6 +382,18 @@ impl<'a> Object<'a> {
},
);

if let Some(version) = &self.macho_build_version {
debug_assert_eq!(build_version_offset, buffer.len());
buffer.write(&macho::BuildVersionCommand {
cmd: U32::new(endian, macho::LC_BUILD_VERSION),
cmdsize: U32::new(endian, version.cmdsize()),
platform: U32::new(endian, version.platform),
minos: U32::new(endian, version.minos),
sdk: U32::new(endian, version.sdk),
ntools: U32::new(endian, 0),
});
}

// Write segment command.
debug_assert_eq!(segment_command_offset, buffer.len());
macho.write_segment_command(
Expand Down
15 changes: 15 additions & 0 deletions src/write/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pub mod elf;

#[cfg(feature = "macho")]
mod macho;
#[cfg(feature = "macho")]
pub use macho::MachOBuildVersion;

#[cfg(feature = "pe")]
pub mod pe;
Expand Down Expand Up @@ -70,6 +72,8 @@ pub struct Object<'a> {
pub mangling: Mangling,
/// Mach-O "_tlv_bootstrap" symbol.
tlv_bootstrap: Option<SymbolId>,
#[cfg(feature = "macho")]
macho_build_version: Option<MachOBuildVersion>,
}

impl<'a> Object<'a> {
Expand All @@ -88,6 +92,8 @@ impl<'a> Object<'a> {
flags: FileFlags::None,
mangling: Mangling::default(format, architecture),
tlv_bootstrap: None,
#[cfg(feature = "macho")]
macho_build_version: None,
}
}

Expand Down Expand Up @@ -115,6 +121,15 @@ impl<'a> Object<'a> {
self.mangling = mangling;
}

/// Specify information for a Mach-O `LC_BUILD_VERSION` command.
///
/// Requires `feature = "macho"`.
#[inline]
#[cfg(feature = "macho")]
pub fn set_macho_build_version(&mut self, info: MachOBuildVersion) {
self.macho_build_version = Some(info);
}

/// Return the name for a standard segment.
///
/// This will vary based on the file format.
Expand Down