Aug 13, 2016 Tags: gsoc, programming, ruby, ruby-macho
For a quick background, see the first post in this series.
As of the last two weeks…
Modification of “inconsistent” fat Mach-Os now works!
First, some background: an “inconsistent” Mach-O is a universal (“fat”)
binary whose individual architecture slices do not reference the same
external components. For example, the imaginary inconsistent
might have an
i386 that references
x86_64 slice may not reference
/usr/lib/libimportant.dylib at all
(or at a different path). Such inconsistencies are not necessarily
program-breaking or incorrect (different architectures can require different
dependency linkages, after all), and usually arise from a manual
step in the build process instead of the (preferred)
-arch linker flags.
Prior to ruby-macho/55,
ruby-macho assumed that all fat binaries given to it were internally
consistent, meaning that each internal slice contained the same list of
linked dylibs, rpaths, and so forth. As a result, attempting to modify a
rpath that isn’t referenced in all slices of the fat Mach-O
would result in the modification’s corresponding exception.
This was amended via the addition of an
:strict flag (
true by default),
whose behavior is as follows:
true, fail immediately upon the first failed modification.
To avoid confusing users with this new behavior, the exception hierarchy
was also modified to accommodate the
RecoverableModificationError classes, which are subclassed as necessary
by errors like
OffsetInsertionError (unrecoverable) and
DylibUnknownError (recoverable). This generalization also allows us to
provide additional error context to the user in the form of
macho_slice attribute, which gives the
index of the first slice to fail during modification.
ruby-macho’s style is now RuboCop enforced!
As of ruby-macho/56, the
main ruby-macho development branch is now consistently formatted (and
enforced by RuboCop). A full list of
ruby-macho’s custom style rules can be found in the project’s
Some relevant changes:
Ugly housekeeping methods like
get_load_commands are now prefixed with
Exceptions are raised without explicit instantiation, where possible:
1 2 raise LCStrMalformedError.new(lc) # old raise LCStrMalformedError, lc # new
All freezable constants are now frozen, including dozens of
Long method definitions and invocations are aligned to the parameter list.
FatFile#dylib_id= are now defined as
change_dylib_id for uniformity with other modification methods. The old
method names are retained as aliases for compatibility.
Section enumeration is cleaner!
Prior to ruby-macho/57,
the standard way to enumerate a
MachOFile’s sections was a nested loop
1 2 3 4 5 file.segments.each do |segment| file.sections(segment).each do |section| # do something with each section end end
Like the old (and thankfully removed)
brings control flow back into the
MachOFile instance for no particularly
good reason. By subclassing
MachO::SegmentCommand64 as a
MachO::SegmentCommand, we were able to make segment instantiation
significantly DRYer and add a generic
Segment#sections to the following
1 2 3 4 5 file.segments.each do |segment| segment.sections.each do |section| # do something with each section end end
This preserves the object/flow hierarchy and is much more intuitive.
MachOFile#sections has been marked as deprecated and will
probably be removed shortly.
In line with the subclassing of
SegmentCommand64 as a
RoutinesCommand64 is now a
RoutinesCommand as of
As of ruby-macho/59,
unimplemented unit tests now use
skip instead of
pass. This was a
originally a personal mistake, as ruby-macho’s tests were my first
Minitest::Test and I wasn’t aware that a distinction was
drawn between a (no-op)
pass and a
And for the final week:
Cap the 1.0 release off. I may not have time to complete all of the items marked as “important”, but adding broader serialization support and beginning work on constant/namespace delineation are high-priority.
Get 1.0 merged into
brew and activated for all users!