ruby-macho 1.3.0.pre.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/macho.rb +11 -11
- data/lib/macho/exceptions.rb +10 -0
- data/lib/macho/fat_file.rb +52 -16
- data/lib/macho/headers.rb +73 -6
- data/lib/macho/load_commands.rb +25 -0
- data/lib/macho/macho_file.rb +29 -2
- data/lib/macho/sections.rb +6 -0
- data/lib/macho/tools.rb +3 -2
- data/lib/macho/utils.rb +30 -6
- metadata +5 -5
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 644d3e65c34cd64ddcba05a186fa28caa4032adb7ca7c4022e485f5d2a5bb399
         | 
| 4 | 
            +
              data.tar.gz: 6835b176571ab230e171080c418a4f2873be9af58b47ebd1a316e8e219119222
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: d4e29aaa92deb1128369c0f5368b6832d9c1068a89c5d1e35951ca17dc068806fc7674064dcf603ba3b6ba66a1e7b57189cb9589bbd53f74b7df4602d7726926
         | 
| 7 | 
            +
              data.tar.gz: '0838f24f7b496660ce71eb06b1167c65f8c8b551ae6b6f223ec1518664d43bb8c1bdc0803e605ff93319c07ca2c8088a80aad1d2f397c9019c7fc067bae6c3d6'
         | 
    
        data/lib/macho.rb
    CHANGED
    
    | @@ -1,18 +1,18 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 1 | 
            +
            require_relative "macho/structure"
         | 
| 2 | 
            +
            require_relative "macho/view"
         | 
| 3 | 
            +
            require_relative "macho/headers"
         | 
| 4 | 
            +
            require_relative "macho/load_commands"
         | 
| 5 | 
            +
            require_relative "macho/sections"
         | 
| 6 | 
            +
            require_relative "macho/macho_file"
         | 
| 7 | 
            +
            require_relative "macho/fat_file"
         | 
| 8 | 
            +
            require_relative "macho/exceptions"
         | 
| 9 | 
            +
            require_relative "macho/utils"
         | 
| 10 | 
            +
            require_relative "macho/tools"
         | 
| 11 11 |  | 
| 12 12 | 
             
            # The primary namespace for ruby-macho.
         | 
| 13 13 | 
             
            module MachO
         | 
| 14 14 | 
             
              # release version
         | 
| 15 | 
            -
              VERSION = "1.3.0 | 
| 15 | 
            +
              VERSION = "1.3.0".freeze
         | 
| 16 16 |  | 
| 17 17 | 
             
              # Opens the given filename as a MachOFile or FatFile, depending on its magic.
         | 
| 18 18 | 
             
              # @param filename [String] the file being opened
         | 
    
        data/lib/macho/exceptions.rb
    CHANGED
    
    | @@ -194,4 +194,14 @@ module MachO | |
| 194 194 | 
             
                  super "Unimplemented: #{thing}"
         | 
| 195 195 | 
             
                end
         | 
| 196 196 | 
             
              end
         | 
| 197 | 
            +
             | 
| 198 | 
            +
              # Raised when attempting to create a {FatFile} from one or more {MachOFile}s
         | 
| 199 | 
            +
              #  whose offsets will not fit within the resulting 32-bit {Headers::FatArch#offset} fields.
         | 
| 200 | 
            +
              class FatArchOffsetOverflowError < MachOError
         | 
| 201 | 
            +
                # @param offset [Integer] the offending offset
         | 
| 202 | 
            +
                def initialize(offset)
         | 
| 203 | 
            +
                  super "Offset #{offset} exceeds the 32-bit width of a fat_arch offset." \
         | 
| 204 | 
            +
                        " Consider merging with `fat64: true`"
         | 
| 205 | 
            +
                end
         | 
| 206 | 
            +
              end
         | 
| 197 207 | 
             
            end
         | 
    
        data/lib/macho/fat_file.rb
    CHANGED
    
    | @@ -14,7 +14,7 @@ module MachO | |
| 14 14 | 
             
                # @return [Headers::FatHeader] the file's header
         | 
| 15 15 | 
             
                attr_reader :header
         | 
| 16 16 |  | 
| 17 | 
            -
                # @return [Array<Headers::FatArch | 
| 17 | 
            +
                # @return [Array<Headers::FatArch>, Array<Headers::FatArch64] an array of fat architectures
         | 
| 18 18 | 
             
                attr_reader :fat_archs
         | 
| 19 19 |  | 
| 20 20 | 
             
                # @return [Array<MachOFile>] an array of Mach-O binaries
         | 
| @@ -22,22 +22,54 @@ module MachO | |
| 22 22 |  | 
| 23 23 | 
             
                # Creates a new FatFile from the given (single-arch) Mach-Os
         | 
| 24 24 | 
             
                # @param machos [Array<MachOFile>] the machos to combine
         | 
| 25 | 
            +
                # @param fat64 [Boolean] whether to use {Headers::FatArch64}s to represent each slice
         | 
| 25 26 | 
             
                # @return [FatFile] a new FatFile containing the give machos
         | 
| 26 | 
            -
                 | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 27 | 
            +
                # @raise [ArgumentError] if less than one Mach-O is given
         | 
| 28 | 
            +
                # @raise [FatArchOffsetOverflowError] if the Mach-Os are too big to be represented
         | 
| 29 | 
            +
                #  in a 32-bit {Headers::FatArch} and `fat64` is `false`.
         | 
| 30 | 
            +
                def self.new_from_machos(*machos, fat64: false)
         | 
| 31 | 
            +
                  raise ArgumentError, "expected at least one Mach-O" if machos.empty?
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  fa_klass, magic = if fat64
         | 
| 34 | 
            +
                    [Headers::FatArch64, Headers::FAT_MAGIC_64]
         | 
| 35 | 
            +
                  else
         | 
| 36 | 
            +
                    [Headers::FatArch, Headers::FAT_MAGIC]
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  # put the smaller alignments further forwards in fat macho, so that we do less padding
         | 
| 40 | 
            +
                  machos = machos.sort_by(&:segment_alignment)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  bin = if String.instance_methods.include? :+@
         | 
| 43 | 
            +
                    +""
         | 
| 44 | 
            +
                  else
         | 
| 45 | 
            +
                    ""
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  bin << Headers::FatHeader.new(magic, machos.size).serialize
         | 
| 49 | 
            +
                  offset = Headers::FatHeader.bytesize + (machos.size * fa_klass.bytesize)
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  macho_pads = {}
         | 
| 52 | 
            +
             | 
| 30 53 | 
             
                  machos.each do |macho|
         | 
| 31 | 
            -
                     | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
                     | 
| 54 | 
            +
                    macho_offset = Utils.round(offset, 2**macho.segment_alignment)
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    if !fat64 && macho_offset > (2**32 - 1)
         | 
| 57 | 
            +
                      raise FatArchOffsetOverflowError, macho_offset
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    macho_pads[macho] = Utils.padding_for(offset, 2**macho.segment_alignment)
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    bin << fa_klass.new(macho.header.cputype, macho.header.cpusubtype,
         | 
| 63 | 
            +
                                        macho_offset, macho.serialize.bytesize,
         | 
| 64 | 
            +
                                        macho.segment_alignment).serialize
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    offset += (macho.serialize.bytesize + macho_pads[macho])
         | 
| 36 67 | 
             
                  end
         | 
| 37 68 |  | 
| 38 | 
            -
                   | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 69 | 
            +
                  machos.each do |macho|
         | 
| 70 | 
            +
                    bin << Utils.nullpad(macho_pads[macho])
         | 
| 71 | 
            +
                    bin << macho.serialize
         | 
| 72 | 
            +
                  end
         | 
| 41 73 |  | 
| 42 74 | 
             
                  new_from_bin(bin)
         | 
| 43 75 | 
             
                end
         | 
| @@ -262,6 +294,7 @@ module MachO | |
| 262 294 | 
             
                # @note Overwrites all data in the file!
         | 
| 263 295 | 
             
                def write!
         | 
| 264 296 | 
             
                  raise MachOError, "no initial file to write to" if filename.nil?
         | 
| 297 | 
            +
             | 
| 265 298 | 
             
                  File.open(@filename, "wb") { |f| f.write(@raw_data) }
         | 
| 266 299 | 
             
                end
         | 
| 267 300 |  | 
| @@ -311,10 +344,12 @@ module MachO | |
| 311 344 | 
             
                def populate_fat_archs
         | 
| 312 345 | 
             
                  archs = []
         | 
| 313 346 |  | 
| 314 | 
            -
                   | 
| 315 | 
            -
                   | 
| 347 | 
            +
                  fa_klass = Utils.fat_magic32?(header.magic) ? Headers::FatArch : Headers::FatArch64
         | 
| 348 | 
            +
                  fa_off   = Headers::FatHeader.bytesize
         | 
| 349 | 
            +
                  fa_len   = fa_klass.bytesize
         | 
| 350 | 
            +
             | 
| 316 351 | 
             
                  header.nfat_arch.times do |i|
         | 
| 317 | 
            -
                    archs <<  | 
| 352 | 
            +
                    archs << fa_klass.new_from_bin(:big, @raw_data[fa_off + (fa_len * i), fa_len])
         | 
| 318 353 | 
             
                  end
         | 
| 319 354 |  | 
| 320 355 | 
             
                  archs
         | 
| @@ -364,6 +399,7 @@ module MachO | |
| 364 399 |  | 
| 365 400 | 
             
                      # Strict mode: Immediately re-raise. Otherwise: Retain, check later.
         | 
| 366 401 | 
             
                      raise error if strict
         | 
| 402 | 
            +
             | 
| 367 403 | 
             
                      errors << error
         | 
| 368 404 | 
             
                    end
         | 
| 369 405 | 
             
                  end
         | 
    
        data/lib/macho/headers.rb
    CHANGED
    
    | @@ -6,11 +6,19 @@ module MachO | |
| 6 6 | 
             
                FAT_MAGIC = 0xcafebabe
         | 
| 7 7 |  | 
| 8 8 | 
             
                # little-endian fat magic
         | 
| 9 | 
            -
                #  | 
| 10 | 
            -
                # fat headers are always big-endian | 
| 9 | 
            +
                # @note This is defined for completeness, but should never appear in ruby-macho code,
         | 
| 10 | 
            +
                #  since fat headers are always big-endian.
         | 
| 11 11 | 
             
                # @api private
         | 
| 12 12 | 
             
                FAT_CIGAM = 0xbebafeca
         | 
| 13 13 |  | 
| 14 | 
            +
                # 64-bit big-endian fat magic
         | 
| 15 | 
            +
                FAT_MAGIC_64 = 0xcafebabf
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                # 64-bit little-endian fat magic
         | 
| 18 | 
            +
                # @note This is defined for completeness, but should never appear in ruby-macho code,
         | 
| 19 | 
            +
                #   since fat headers are always big-endian.
         | 
| 20 | 
            +
                FAT_CIGAM_64 = 0xbfbafeca
         | 
| 21 | 
            +
             | 
| 14 22 | 
             
                # 32-bit big-endian magic
         | 
| 15 23 | 
             
                # @api private
         | 
| 16 24 | 
             
                MH_MAGIC = 0xfeedface
         | 
| @@ -31,6 +39,7 @@ module MachO | |
| 31 39 | 
             
                # @api private
         | 
| 32 40 | 
             
                MH_MAGICS = {
         | 
| 33 41 | 
             
                  FAT_MAGIC => "FAT_MAGIC",
         | 
| 42 | 
            +
                  FAT_MAGIC_64 => "FAT_MAGIC_64",
         | 
| 34 43 | 
             
                  MH_MAGIC => "MH_MAGIC",
         | 
| 35 44 | 
             
                  MH_CIGAM => "MH_CIGAM",
         | 
| 36 45 | 
             
                  MH_MAGIC_64 => "MH_MAGIC_64",
         | 
| @@ -41,6 +50,11 @@ module MachO | |
| 41 50 | 
             
                # @api private
         | 
| 42 51 | 
             
                CPU_ARCH_ABI64 = 0x01000000
         | 
| 43 52 |  | 
| 53 | 
            +
                # mask for CPUs with 64-bit architectures (when running a 32-bit ABI?)
         | 
| 54 | 
            +
                # @see https://github.com/Homebrew/ruby-macho/issues/113
         | 
| 55 | 
            +
                # @api private
         | 
| 56 | 
            +
                CPU_ARCH_ABI32 = 0x02000000
         | 
| 57 | 
            +
             | 
| 44 58 | 
             
                # any CPU (unused?)
         | 
| 45 59 | 
             
                # @api private
         | 
| 46 60 | 
             
                CPU_TYPE_ANY = -1
         | 
| @@ -69,6 +83,10 @@ module MachO | |
| 69 83 | 
             
                # @api private
         | 
| 70 84 | 
             
                CPU_TYPE_ARM64 = (CPU_TYPE_ARM | CPU_ARCH_ABI64)
         | 
| 71 85 |  | 
| 86 | 
            +
                # 64-bit ARM compatible CPUs (running in 32-bit mode?)
         | 
| 87 | 
            +
                # @see https://github.com/Homebrew/ruby-macho/issues/113
         | 
| 88 | 
            +
                CPU_TYPE_ARM64_32 = (CPU_TYPE_ARM | CPU_ARCH_ABI32)
         | 
| 89 | 
            +
             | 
| 72 90 | 
             
                # PowerPC compatible CPUs
         | 
| 73 91 | 
             
                # @api private
         | 
| 74 92 | 
             
                CPU_TYPE_POWERPC = 0x12
         | 
| @@ -85,6 +103,7 @@ module MachO | |
| 85 103 | 
             
                  CPU_TYPE_X86_64 => :x86_64,
         | 
| 86 104 | 
             
                  CPU_TYPE_ARM => :arm,
         | 
| 87 105 | 
             
                  CPU_TYPE_ARM64 => :arm64,
         | 
| 106 | 
            +
                  CPU_TYPE_ARM64_32 => :arm64_32,
         | 
| 88 107 | 
             
                  CPU_TYPE_POWERPC => :ppc,
         | 
| 89 108 | 
             
                  CPU_TYPE_POWERPC64 => :ppc64,
         | 
| 90 109 | 
             
                }.freeze
         | 
| @@ -218,6 +237,10 @@ module MachO | |
| 218 237 | 
             
                # @api private
         | 
| 219 238 | 
             
                CPU_SUBTYPE_ARM64_V8 = 1
         | 
| 220 239 |  | 
| 240 | 
            +
                # the v8 sub-type for `CPU_TYPE_ARM64_32`
         | 
| 241 | 
            +
                # @api private
         | 
| 242 | 
            +
                CPU_SUBTYPE_ARM64_32_V8 = 1
         | 
| 243 | 
            +
             | 
| 221 244 | 
             
                # the lowest common sub-type for `CPU_TYPE_MC88000`
         | 
| 222 245 | 
             
                # @api private
         | 
| 223 246 | 
             
                CPU_SUBTYPE_MC88000_ALL = 0
         | 
| @@ -328,6 +351,9 @@ module MachO | |
| 328 351 | 
             
                    CPU_SUBTYPE_ARM64_ALL => :arm64,
         | 
| 329 352 | 
             
                    CPU_SUBTYPE_ARM64_V8 => :arm64v8,
         | 
| 330 353 | 
             
                  }.freeze,
         | 
| 354 | 
            +
                  CPU_TYPE_ARM64_32 => {
         | 
| 355 | 
            +
                    CPU_SUBTYPE_ARM64_32_V8 => :arm64_32v8,
         | 
| 356 | 
            +
                  }.freeze,
         | 
| 331 357 | 
             
                  CPU_TYPE_POWERPC => {
         | 
| 332 358 | 
             
                    CPU_SUBTYPE_POWERPC_ALL => :ppc,
         | 
| 333 359 | 
             
                    CPU_SUBTYPE_POWERPC_601 => :ppc601,
         | 
| @@ -486,8 +512,10 @@ module MachO | |
| 486 512 | 
             
                  end
         | 
| 487 513 | 
             
                end
         | 
| 488 514 |  | 
| 489 | 
            -
                #  | 
| 490 | 
            -
                # | 
| 515 | 
            +
                # 32-bit fat binary header architecture structure. A 32-bit fat Mach-O has one or more of
         | 
| 516 | 
            +
                #  these, indicating one or more internal Mach-O blobs.
         | 
| 517 | 
            +
                # @note "32-bit" indicates the fact that this structure stores 32-bit offsets, not that the
         | 
| 518 | 
            +
                #  Mach-Os that it points to necessarily *are* 32-bit.
         | 
| 491 519 | 
             
                # @see MachO::Headers::FatHeader
         | 
| 492 520 | 
             
                class FatArch < MachOStructure
         | 
| 493 521 | 
             
                  # @return [Integer] the CPU type of the Mach-O
         | 
| @@ -505,10 +533,10 @@ module MachO | |
| 505 533 | 
             
                  # @return [Integer] the alignment, as a power of 2
         | 
| 506 534 | 
             
                  attr_reader :align
         | 
| 507 535 |  | 
| 508 | 
            -
                  #  | 
| 536 | 
            +
                  # @note Always big endian.
         | 
| 509 537 | 
             
                  # @see MachOStructure::FORMAT
         | 
| 510 538 | 
             
                  # @api private
         | 
| 511 | 
            -
                  FORMAT = " | 
| 539 | 
            +
                  FORMAT = "L>5".freeze
         | 
| 512 540 |  | 
| 513 541 | 
             
                  # @see MachOStructure::SIZEOF
         | 
| 514 542 | 
             
                  # @api private
         | 
| @@ -542,6 +570,43 @@ module MachO | |
| 542 570 | 
             
                  end
         | 
| 543 571 | 
             
                end
         | 
| 544 572 |  | 
| 573 | 
            +
                # 64-bit fat binary header architecture structure. A 64-bit fat Mach-O has one or more of
         | 
| 574 | 
            +
                #  these, indicating one or more internal Mach-O blobs.
         | 
| 575 | 
            +
                # @note "64-bit" indicates the fact that this structure stores 64-bit offsets, not that the
         | 
| 576 | 
            +
                #  Mach-Os that it points to necessarily *are* 64-bit.
         | 
| 577 | 
            +
                # @see MachO::Headers::FatHeader
         | 
| 578 | 
            +
                class FatArch64 < FatArch
         | 
| 579 | 
            +
                  # @return [void]
         | 
| 580 | 
            +
                  attr_reader :reserved
         | 
| 581 | 
            +
             | 
| 582 | 
            +
                  # @note Always big endian.
         | 
| 583 | 
            +
                  # @see MachOStructure::FORMAT
         | 
| 584 | 
            +
                  # @api private
         | 
| 585 | 
            +
                  FORMAT = "L>2Q>2L>2".freeze
         | 
| 586 | 
            +
             | 
| 587 | 
            +
                  # @see MachOStructure::SIZEOF
         | 
| 588 | 
            +
                  # @api private
         | 
| 589 | 
            +
                  SIZEOF = 32
         | 
| 590 | 
            +
             | 
| 591 | 
            +
                  # @api private
         | 
| 592 | 
            +
                  def initialize(cputype, cpusubtype, offset, size, align, reserved = 0)
         | 
| 593 | 
            +
                    super(cputype, cpusubtype, offset, size, align)
         | 
| 594 | 
            +
                    @reserved = reserved
         | 
| 595 | 
            +
                  end
         | 
| 596 | 
            +
             | 
| 597 | 
            +
                  # @return [String] the serialized fields of the fat arch
         | 
| 598 | 
            +
                  def serialize
         | 
| 599 | 
            +
                    [cputype, cpusubtype, offset, size, align, reserved].pack(FORMAT)
         | 
| 600 | 
            +
                  end
         | 
| 601 | 
            +
             | 
| 602 | 
            +
                  # @return [Hash] a hash representation of this {FatArch64}
         | 
| 603 | 
            +
                  def to_h
         | 
| 604 | 
            +
                    {
         | 
| 605 | 
            +
                      "reserved" => reserved,
         | 
| 606 | 
            +
                    }.merge super
         | 
| 607 | 
            +
                  end
         | 
| 608 | 
            +
                end
         | 
| 609 | 
            +
             | 
| 545 610 | 
             
                # 32-bit Mach-O file header structure
         | 
| 546 611 | 
             
                class MachHeader < MachOStructure
         | 
| 547 612 | 
             
                  # @return [Integer] the magic number
         | 
| @@ -593,7 +658,9 @@ module MachO | |
| 593 658 | 
             
                  # @return [Boolean] true if `flag` is present in the header's flag section
         | 
| 594 659 | 
             
                  def flag?(flag)
         | 
| 595 660 | 
             
                    flag = MH_FLAGS[flag]
         | 
| 661 | 
            +
             | 
| 596 662 | 
             
                    return false if flag.nil?
         | 
| 663 | 
            +
             | 
| 597 664 | 
             
                    flags & flag == flag
         | 
| 598 665 | 
             
                  end
         | 
| 599 666 |  | 
    
        data/lib/macho/load_commands.rb
    CHANGED
    
    | @@ -242,6 +242,7 @@ module MachO | |
| 242 242 | 
             
                  # @api private
         | 
| 243 243 | 
             
                  def serialize(context)
         | 
| 244 244 | 
             
                    raise LoadCommandNotSerializableError, LOAD_COMMANDS[cmd] unless serializable?
         | 
| 245 | 
            +
             | 
| 245 246 | 
             
                    format = Utils.specialize_format(FORMAT, context.endianness)
         | 
| 246 247 | 
             
                    [cmd, SIZEOF].pack(format)
         | 
| 247 248 | 
             
                  end
         | 
| @@ -298,7 +299,9 @@ module MachO | |
| 298 299 | 
             
                        lc_end = view.offset + lc.cmdsize - 1
         | 
| 299 300 | 
             
                        raw_string = view.raw_data.slice(lc_str_abs..lc_end)
         | 
| 300 301 | 
             
                        @string, null_byte, _padding = raw_string.partition("\x00")
         | 
| 302 | 
            +
             | 
| 301 303 | 
             
                        raise LCStrMalformedError, lc if null_byte.empty?
         | 
| 304 | 
            +
             | 
| 302 305 | 
             
                        @string_offset = lc_str
         | 
| 303 306 | 
             
                      else
         | 
| 304 307 | 
             
                        @string = lc_str
         | 
| @@ -473,10 +476,32 @@ module MachO | |
| 473 476 | 
             
                  # @return [Boolean] true if `flag` is present in the segment's flag field
         | 
| 474 477 | 
             
                  def flag?(flag)
         | 
| 475 478 | 
             
                    flag = SEGMENT_FLAGS[flag]
         | 
| 479 | 
            +
             | 
| 476 480 | 
             
                    return false if flag.nil?
         | 
| 481 | 
            +
             | 
| 477 482 | 
             
                    flags & flag == flag
         | 
| 478 483 | 
             
                  end
         | 
| 479 484 |  | 
| 485 | 
            +
                  # Guesses the alignment of the segment.
         | 
| 486 | 
            +
                  # @return [Integer] the guessed alignment, as a power of 2
         | 
| 487 | 
            +
                  # @note See `guess_align` in `cctools/misc/lipo.c`
         | 
| 488 | 
            +
                  def guess_align
         | 
| 489 | 
            +
                    return Sections::MAX_SECT_ALIGN if vmaddr.zero?
         | 
| 490 | 
            +
             | 
| 491 | 
            +
                    align = 0
         | 
| 492 | 
            +
                    segalign = 1
         | 
| 493 | 
            +
             | 
| 494 | 
            +
                    while (segalign & vmaddr).zero?
         | 
| 495 | 
            +
                      segalign <<= 1
         | 
| 496 | 
            +
                      align += 1
         | 
| 497 | 
            +
                    end
         | 
| 498 | 
            +
             | 
| 499 | 
            +
                    return 2 if align < 2
         | 
| 500 | 
            +
                    return Sections::MAX_SECT_ALIGN if align > Sections::MAX_SECT_ALIGN
         | 
| 501 | 
            +
             | 
| 502 | 
            +
                    align
         | 
| 503 | 
            +
                  end
         | 
| 504 | 
            +
             | 
| 480 505 | 
             
                  # @return [Hash] a hash representation of this {SegmentCommand}
         | 
| 481 506 | 
             
                  def to_h
         | 
| 482 507 | 
             
                    {
         | 
    
        data/lib/macho/macho_file.rb
    CHANGED
    
    | @@ -219,8 +219,7 @@ module MachO | |
| 219 219 | 
             
                  update_sizeofcmds(sizeofcmds - lc.cmdsize)
         | 
| 220 220 |  | 
| 221 221 | 
             
                  # pad the space after the load commands to preserve offsets
         | 
| 222 | 
            -
                   | 
| 223 | 
            -
                  @raw_data.insert(header.class.bytesize + sizeofcmds - lc.cmdsize, null_pad)
         | 
| 222 | 
            +
                  @raw_data.insert(header.class.bytesize + sizeofcmds - lc.cmdsize, Utils.nullpad(lc.cmdsize))
         | 
| 224 223 |  | 
| 225 224 | 
             
                  populate_fields if options.fetch(:repopulate, true)
         | 
| 226 225 | 
             
                end
         | 
| @@ -252,6 +251,33 @@ module MachO | |
| 252 251 | 
             
                  end
         | 
| 253 252 | 
             
                end
         | 
| 254 253 |  | 
| 254 | 
            +
                # The segment alignment for the Mach-O. Guesses conservatively.
         | 
| 255 | 
            +
                # @return [Integer] the alignment, as a power of 2
         | 
| 256 | 
            +
                # @note This is **not** the same as {#alignment}!
         | 
| 257 | 
            +
                # @note See `get_align` and `get_align_64` in `cctools/misc/lipo.c`
         | 
| 258 | 
            +
                def segment_alignment
         | 
| 259 | 
            +
                  # special cases: 12 for x86/64/PPC/PP64, 14 for ARM/ARM64
         | 
| 260 | 
            +
                  return 12 if %i[i386 x86_64 ppc ppc64].include?(cputype)
         | 
| 261 | 
            +
                  return 14 if %i[arm arm64].include?(cputype)
         | 
| 262 | 
            +
             | 
| 263 | 
            +
                  cur_align = Sections::MAX_SECT_ALIGN
         | 
| 264 | 
            +
             | 
| 265 | 
            +
                  segments.each do |segment|
         | 
| 266 | 
            +
                    if filetype == :object
         | 
| 267 | 
            +
                      # start with the smallest alignment, and work our way up
         | 
| 268 | 
            +
                      align = magic32? ? 2 : 3
         | 
| 269 | 
            +
                      segment.sections.each do |section|
         | 
| 270 | 
            +
                        align = section.align unless section.align <= align
         | 
| 271 | 
            +
                      end
         | 
| 272 | 
            +
                    else
         | 
| 273 | 
            +
                      align = segment.guess_align
         | 
| 274 | 
            +
                    end
         | 
| 275 | 
            +
                    cur_align = align if align < cur_align
         | 
| 276 | 
            +
                  end
         | 
| 277 | 
            +
             | 
| 278 | 
            +
                  cur_align
         | 
| 279 | 
            +
                end
         | 
| 280 | 
            +
             | 
| 255 281 | 
             
                # The Mach-O's dylib ID, or `nil` if not a dylib.
         | 
| 256 282 | 
             
                # @example
         | 
| 257 283 | 
             
                #  file.dylib_id # => 'libBar.dylib'
         | 
| @@ -405,6 +431,7 @@ module MachO | |
| 405 431 | 
             
                # @note Overwrites all data in the file!
         | 
| 406 432 | 
             
                def write!
         | 
| 407 433 | 
             
                  raise MachOError, "no initial file to write to" if @filename.nil?
         | 
| 434 | 
            +
             | 
| 408 435 | 
             
                  File.open(@filename, "wb") { |f| f.write(@raw_data) }
         | 
| 409 436 | 
             
                end
         | 
| 410 437 |  | 
    
        data/lib/macho/sections.rb
    CHANGED
    
    | @@ -13,6 +13,10 @@ module MachO | |
| 13 13 | 
             
                # system settable attributes mask
         | 
| 14 14 | 
             
                SECTION_ATTRIBUTES_SYS = 0x00ffff00
         | 
| 15 15 |  | 
| 16 | 
            +
                # maximum specifiable section alignment, as a power of 2
         | 
| 17 | 
            +
                # @note see `MAXSECTALIGN` macro in `cctools/misc/lipo.c`
         | 
| 18 | 
            +
                MAX_SECT_ALIGN = 15
         | 
| 19 | 
            +
             | 
| 16 20 | 
             
                # association of section flag symbols to values
         | 
| 17 21 | 
             
                # @api private
         | 
| 18 22 | 
             
                SECTION_FLAGS = {
         | 
| @@ -146,7 +150,9 @@ module MachO | |
| 146 150 | 
             
                  # @return [Boolean] whether the flag is present in the section's {flags}
         | 
| 147 151 | 
             
                  def flag?(flag)
         | 
| 148 152 | 
             
                    flag = SECTION_FLAGS[flag]
         | 
| 153 | 
            +
             | 
| 149 154 | 
             
                    return false if flag.nil?
         | 
| 155 | 
            +
             | 
| 150 156 | 
             
                    flags & flag == flag
         | 
| 151 157 | 
             
                  end
         | 
| 152 158 |  | 
    
        data/lib/macho/tools.rb
    CHANGED
    
    | @@ -89,8 +89,9 @@ module MachO | |
| 89 89 | 
             
                # Merge multiple Mach-Os into one universal (Fat) binary.
         | 
| 90 90 | 
             
                # @param filename [String] the fat binary to create
         | 
| 91 91 | 
             
                # @param files [Array<String>] the files to merge
         | 
| 92 | 
            +
                # @param fat64 [Boolean] whether to use {Headers::FatArch64}s to represent each slice
         | 
| 92 93 | 
             
                # @return [void]
         | 
| 93 | 
            -
                def self.merge_machos(filename, *files)
         | 
| 94 | 
            +
                def self.merge_machos(filename, *files, fat64: false)
         | 
| 94 95 | 
             
                  machos = files.map do |file|
         | 
| 95 96 | 
             
                    macho = MachO.open(file)
         | 
| 96 97 | 
             
                    case macho
         | 
| @@ -101,7 +102,7 @@ module MachO | |
| 101 102 | 
             
                    end
         | 
| 102 103 | 
             
                  end.flatten
         | 
| 103 104 |  | 
| 104 | 
            -
                  fat_macho = MachO::FatFile.new_from_machos(*machos)
         | 
| 105 | 
            +
                  fat_macho = MachO::FatFile.new_from_machos(*machos, :fat64 => fat64)
         | 
| 105 106 | 
             
                  fat_macho.write(filename)
         | 
| 106 107 | 
             
                end
         | 
| 107 108 | 
             
              end
         | 
    
        data/lib/macho/utils.rb
    CHANGED
    
    | @@ -22,6 +22,16 @@ module MachO | |
| 22 22 | 
             
                  round(size, alignment) - size
         | 
| 23 23 | 
             
                end
         | 
| 24 24 |  | 
| 25 | 
            +
                # Returns a string of null bytes of the requested (non-negative) size
         | 
| 26 | 
            +
                # @param size [Integer] the size of the nullpad
         | 
| 27 | 
            +
                # @return [String] the null string (or empty string, for `size = 0`)
         | 
| 28 | 
            +
                # @raise [ArgumentError] if a non-positive nullpad is requested
         | 
| 29 | 
            +
                def self.nullpad(size)
         | 
| 30 | 
            +
                  raise ArgumentError, "size < 0: #{size}" if size < 0
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  "\x00" * size
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 25 35 | 
             
                # Converts an abstract (native-endian) String#unpack format to big or
         | 
| 26 36 | 
             
                #  little.
         | 
| 27 37 | 
             
                # @param format [String] the format string being converted
         | 
| @@ -46,11 +56,11 @@ module MachO | |
| 46 56 | 
             
                  strings.each do |key, string|
         | 
| 47 57 | 
             
                    offsets[key] = next_offset
         | 
| 48 58 | 
             
                    payload << string
         | 
| 49 | 
            -
                    payload <<  | 
| 59 | 
            +
                    payload << Utils.nullpad(1)
         | 
| 50 60 | 
             
                    next_offset += string.bytesize + 1
         | 
| 51 61 | 
             
                  end
         | 
| 52 62 |  | 
| 53 | 
            -
                  payload <<  | 
| 63 | 
            +
                  payload << Utils.nullpad(padding_for(fixed_offset + payload.bytesize, alignment))
         | 
| 54 64 | 
             
                  [payload, offsets]
         | 
| 55 65 | 
             
                end
         | 
| 56 66 |  | 
| @@ -65,35 +75,49 @@ module MachO | |
| 65 75 | 
             
                # @param num [Integer] the number being checked
         | 
| 66 76 | 
             
                # @return [Boolean] whether `num` is a valid Fat magic number
         | 
| 67 77 | 
             
                def self.fat_magic?(num)
         | 
| 78 | 
            +
                  [Headers::FAT_MAGIC, Headers::FAT_MAGIC_64].include? num
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                # Compares the given number to valid 32-bit Fat magic numbers.
         | 
| 82 | 
            +
                # @param num [Integer] the number being checked
         | 
| 83 | 
            +
                # @return [Boolean] whether `num` is a valid 32-bit fat magic number
         | 
| 84 | 
            +
                def self.fat_magic32?(num)
         | 
| 68 85 | 
             
                  num == Headers::FAT_MAGIC
         | 
| 69 86 | 
             
                end
         | 
| 70 87 |  | 
| 88 | 
            +
                # Compares the given number to valid 64-bit Fat magic numbers.
         | 
| 89 | 
            +
                # @param num [Integer] the number being checked
         | 
| 90 | 
            +
                # @return [Boolean] whether `num` is a valid 64-bit fat magic number
         | 
| 91 | 
            +
                def self.fat_magic64?(num)
         | 
| 92 | 
            +
                  num == Headers::FAT_MAGIC_64
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 71 95 | 
             
                # Compares the given number to valid 32-bit Mach-O magic numbers.
         | 
| 72 96 | 
             
                # @param num [Integer] the number being checked
         | 
| 73 97 | 
             
                # @return [Boolean] whether `num` is a valid 32-bit magic number
         | 
| 74 98 | 
             
                def self.magic32?(num)
         | 
| 75 | 
            -
                   | 
| 99 | 
            +
                  [Headers::MH_MAGIC, Headers::MH_CIGAM].include? num
         | 
| 76 100 | 
             
                end
         | 
| 77 101 |  | 
| 78 102 | 
             
                # Compares the given number to valid 64-bit Mach-O magic numbers.
         | 
| 79 103 | 
             
                # @param num [Integer] the number being checked
         | 
| 80 104 | 
             
                # @return [Boolean] whether `num` is a valid 64-bit magic number
         | 
| 81 105 | 
             
                def self.magic64?(num)
         | 
| 82 | 
            -
                   | 
| 106 | 
            +
                  [Headers::MH_MAGIC_64, Headers::MH_CIGAM_64].include? num
         | 
| 83 107 | 
             
                end
         | 
| 84 108 |  | 
| 85 109 | 
             
                # Compares the given number to valid little-endian magic numbers.
         | 
| 86 110 | 
             
                # @param num [Integer] the number being checked
         | 
| 87 111 | 
             
                # @return [Boolean] whether `num` is a valid little-endian magic number
         | 
| 88 112 | 
             
                def self.little_magic?(num)
         | 
| 89 | 
            -
                   | 
| 113 | 
            +
                  [Headers::MH_CIGAM, Headers::MH_CIGAM_64].include? num
         | 
| 90 114 | 
             
                end
         | 
| 91 115 |  | 
| 92 116 | 
             
                # Compares the given number to valid big-endian magic numbers.
         | 
| 93 117 | 
             
                # @param num [Integer] the number being checked
         | 
| 94 118 | 
             
                # @return [Boolean] whether `num` is a valid big-endian magic number
         | 
| 95 119 | 
             
                def self.big_magic?(num)
         | 
| 96 | 
            -
                   | 
| 120 | 
            +
                  [Headers::MH_MAGIC, Headers::MH_MAGIC_64].include? num
         | 
| 97 121 | 
             
                end
         | 
| 98 122 | 
             
              end
         | 
| 99 123 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: ruby-macho
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.3.0 | 
| 4 | 
            +
              version: 1.3.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - William Woodruff
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2018- | 
| 11 | 
            +
            date: 2018-09-30 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies: []
         | 
| 13 13 | 
             
            description: A library for viewing and manipulating Mach-O files in Ruby.
         | 
| 14 14 | 
             
            email: william@yossarian.net
         | 
| @@ -42,12 +42,12 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 42 42 | 
             
              requirements:
         | 
| 43 43 | 
             
              - - ">="
         | 
| 44 44 | 
             
                - !ruby/object:Gem::Version
         | 
| 45 | 
            -
                  version: '2. | 
| 45 | 
            +
                  version: '2.3'
         | 
| 46 46 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 47 47 | 
             
              requirements:
         | 
| 48 | 
            -
              - - " | 
| 48 | 
            +
              - - ">="
         | 
| 49 49 | 
             
                - !ruby/object:Gem::Version
         | 
| 50 | 
            -
                  version:  | 
| 50 | 
            +
                  version: '0'
         | 
| 51 51 | 
             
            requirements: []
         | 
| 52 52 | 
             
            rubyforge_project: 
         | 
| 53 53 | 
             
            rubygems_version: 2.7.6
         |