From bace3ac5269dd692076aaee6f868aea0a1f606f9 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 3 Jul 2018 12:11:19 -0400 Subject: [PATCH] vendor: ruby-macho 2.0 --- Library/Homebrew/vendor/macho/macho.rb | 22 +- .../Homebrew/vendor/macho/macho/fat_file.rb | 45 +- .../Homebrew/vendor/macho/macho/headers.rb | 49 +- .../vendor/macho/macho/load_commands.rb | 444 ++++++++++++++++-- .../Homebrew/vendor/macho/macho/macho_file.rb | 42 +- .../Homebrew/vendor/macho/macho/sections.rb | 42 +- .../Homebrew/vendor/macho/macho/structure.rb | 10 + Library/Homebrew/vendor/macho/macho/utils.rb | 14 +- Library/Homebrew/vendor/macho/macho/view.rb | 8 + 9 files changed, 588 insertions(+), 88 deletions(-) diff --git a/Library/Homebrew/vendor/macho/macho.rb b/Library/Homebrew/vendor/macho/macho.rb index 3ba3073304..06dd39b09f 100644 --- a/Library/Homebrew/vendor/macho/macho.rb +++ b/Library/Homebrew/vendor/macho/macho.rb @@ -1,18 +1,18 @@ -require "#{File.dirname(__FILE__)}/macho/structure" -require "#{File.dirname(__FILE__)}/macho/view" -require "#{File.dirname(__FILE__)}/macho/headers" -require "#{File.dirname(__FILE__)}/macho/load_commands" -require "#{File.dirname(__FILE__)}/macho/sections" -require "#{File.dirname(__FILE__)}/macho/macho_file" -require "#{File.dirname(__FILE__)}/macho/fat_file" -require "#{File.dirname(__FILE__)}/macho/exceptions" -require "#{File.dirname(__FILE__)}/macho/utils" -require "#{File.dirname(__FILE__)}/macho/tools" +require_relative "macho/structure" +require_relative "macho/view" +require_relative "macho/headers" +require_relative "macho/load_commands" +require_relative "macho/sections" +require_relative "macho/macho_file" +require_relative "macho/fat_file" +require_relative "macho/exceptions" +require_relative "macho/utils" +require_relative "macho/tools" # The primary namespace for ruby-macho. module MachO # release version - VERSION = "1.2.0".freeze + VERSION = "2.0.0".freeze # Opens the given filename as a MachOFile or FatFile, depending on its magic. # @param filename [String] the file being opened diff --git a/Library/Homebrew/vendor/macho/macho/fat_file.rb b/Library/Homebrew/vendor/macho/macho/fat_file.rb index e46948de41..0080a5593c 100644 --- a/Library/Homebrew/vendor/macho/macho/fat_file.rb +++ b/Library/Homebrew/vendor/macho/macho/fat_file.rb @@ -23,21 +23,37 @@ module MachO # Creates a new FatFile from the given (single-arch) Mach-Os # @param machos [Array] the machos to combine # @return [FatFile] a new FatFile containing the give machos + # @raise [ArgumentError] if less than one Mach-O is given def self.new_from_machos(*machos) - header = Headers::FatHeader.new(Headers::FAT_MAGIC, machos.size) + raise ArgumentError, "expected at least one Mach-O" if machos.empty? + + # put the smaller alignments further forwards in fat macho, so that we do less padding + machos = machos.sort_by(&:segment_alignment) + + bin = +"" + + bin << Headers::FatHeader.new(Headers::FAT_MAGIC, machos.size).serialize offset = Headers::FatHeader.bytesize + (machos.size * Headers::FatArch.bytesize) - fat_archs = [] + + macho_pads = {} + macho_bins = {} + machos.each do |macho| - fat_archs << Headers::FatArch.new(macho.header.cputype, - macho.header.cpusubtype, - offset, macho.serialize.bytesize, - macho.alignment) - offset += macho.serialize.bytesize + macho_offset = Utils.round(offset, 2**macho.segment_alignment) + macho_pads[macho] = Utils.padding_for(offset, 2**macho.segment_alignment) + macho_bins[macho] = macho.serialize + + bin << Headers::FatArch.new(macho.header.cputype, macho.header.cpusubtype, + macho_offset, macho_bins[macho].bytesize, + macho.segment_alignment).serialize + + offset += (macho_bins[macho].bytesize + macho_pads[macho]) end - bin = header.serialize - bin << fat_archs.map(&:serialize).join - bin << machos.map(&:serialize).join + machos.each do |macho| + bin << Utils.nullpad(macho_pads[macho]) + bin << macho_bins[macho] + end new_from_bin(bin) end @@ -265,6 +281,15 @@ module MachO File.open(@filename, "wb") { |f| f.write(@raw_data) } end + # @return [Hash] a hash representation of this {FatFile} + def to_h + { + "header" => header.to_h, + "fat_archs" => fat_archs.map(&:to_h), + "machos" => machos.map(&:to_h), + } + end + private # Obtain the fat header from raw file data. diff --git a/Library/Homebrew/vendor/macho/macho/headers.rb b/Library/Homebrew/vendor/macho/macho/headers.rb index 0d4463a6cb..ee8c99e84b 100644 --- a/Library/Homebrew/vendor/macho/macho/headers.rb +++ b/Library/Homebrew/vendor/macho/macho/headers.rb @@ -475,6 +475,15 @@ module MachO def serialize [magic, nfat_arch].pack(FORMAT) end + + # @return [Hash] a hash representation of this {FatHeader} + def to_h + { + "magic" => magic, + "magic_sym" => MH_MAGICS[magic], + "nfat_arch" => nfat_arch, + }.merge super + end end # Fat binary header architecture structure. A Fat binary has one or more of @@ -508,7 +517,7 @@ module MachO # @api private def initialize(cputype, cpusubtype, offset, size, align) @cputype = cputype - @cpusubtype = cpusubtype + @cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK @offset = offset @size = size @align = align @@ -518,6 +527,19 @@ module MachO def serialize [cputype, cpusubtype, offset, size, align].pack(FORMAT) end + + # @return [Hash] a hash representation of this {FatArch} + def to_h + { + "cputype" => cputype, + "cputype_sym" => CPU_TYPES[cputype], + "cpusubtype" => cpusubtype, + "cpusubtype_sym" => CPU_SUBTYPES[cputype][cpusubtype], + "offset" => offset, + "size" => size, + "align" => align, + }.merge super + end end # 32-bit Mach-O file header structure @@ -639,6 +661,24 @@ module MachO def alignment magic32? ? 4 : 8 end + + # @return [Hash] a hash representation of this {MachHeader} + def to_h + { + "magic" => magic, + "magic_sym" => MH_MAGICS[magic], + "cputype" => cputype, + "cputype_sym" => CPU_TYPES[cputype], + "cpusubtype" => cpusubtype, + "cpusubtype_sym" => CPU_SUBTYPES[cputype][cpusubtype], + "filetype" => filetype, + "filetype_sym" => MH_FILETYPES[filetype], + "ncmds" => ncmds, + "sizeofcmds" => sizeofcmds, + "flags" => flags, + "alignment" => alignment, + }.merge super + end end # 64-bit Mach-O file header structure @@ -660,6 +700,13 @@ module MachO super(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags) @reserved = reserved end + + # @return [Hash] a hash representation of this {MachHeader64} + def to_h + { + "reserved" => reserved, + }.merge super + end end end end diff --git a/Library/Homebrew/vendor/macho/macho/load_commands.rb b/Library/Homebrew/vendor/macho/macho/load_commands.rb index 8316fccddd..08bda5a9e4 100644 --- a/Library/Homebrew/vendor/macho/macho/load_commands.rb +++ b/Library/Homebrew/vendor/macho/macho/load_commands.rb @@ -143,7 +143,7 @@ module MachO :LC_LINKER_OPTIMIZATION_HINT => "LinkeditDataCommand", :LC_VERSION_MIN_TVOS => "VersionMinCommand", :LC_VERSION_MIN_WATCHOS => "VersionMinCommand", - :LC_NOTE => "LoadCommand", + :LC_NOTE => "NoteCommand", :LC_BUILD_VERSION => "BuildVersionCommand", }.freeze @@ -169,15 +169,16 @@ module MachO :SG_PROTECTED_VERSION_1 => 0x8, }.freeze - # Mach-O load command structure - # This is the most generic load command - only cmd ID and size are - # represented, and no actual data. Used when a more specific class - # isn't available/implemented. + # The top-level Mach-O load command structure. + # + # This is the most generic load command -- only the type ID and size are + # represented. Used when a more specific class isn't available or isn't implemented. class LoadCommand < MachOStructure - # @return [MachO::MachOView] the raw view associated with the load command + # @return [MachO::MachOView, nil] the raw view associated with the load command, + # or nil if the load command was created via {create}. attr_reader :view - # @return [Integer] the load command's identifying number + # @return [Integer] the load command's type ID attr_reader :cmd # @return [Integer] the size of the load command, in bytes @@ -251,8 +252,8 @@ module MachO view.offset end - # @return [Symbol] a symbol representation of the load command's - # identifying number + # @return [Symbol, nil] a symbol representation of the load command's + # type ID, or nil if the ID doesn't correspond to a known load command class def type LOAD_COMMANDS[cmd] end @@ -265,6 +266,17 @@ module MachO type.to_s end + # @return [Hash] a hash representation of this load command + # @note Children should override this to include additional information. + def to_h + { + "view" => view.to_h, + "cmd" => cmd, + "cmdsize" => cmdsize, + "type" => type, + }.merge super + end + # Represents a Load Command string. A rough analogue to the lc_str # struct used internally by OS X. This class allows ruby-macho to # pretend that strings stored in LCs are immediately available without @@ -304,6 +316,14 @@ module MachO def to_i @string_offset end + + # @return [Hash] a hash representation of this {LCStr}. + def to_h + { + "string" => to_s, + "offset" => to_i, + } + end end # Represents the contextual information needed by a load command to @@ -364,6 +384,14 @@ module MachO segs.join("-") end + + # @return [Hash] returns a hash representation of this {UUIDCommand} + def to_h + { + "uuid" => uuid, + "uuid_string" => uuid_string, + }.merge super + end end # A load command indicating that part of this file is to be mapped into @@ -398,7 +426,7 @@ module MachO # @see MachOStructure::FORMAT # @api private - FORMAT = "L=2a16L=4l=2L=2".freeze + FORMAT = "L=2Z16L=4l=2L=2".freeze # @see MachOStructure::SIZEOF # @api private @@ -408,7 +436,7 @@ module MachO def initialize(view, cmd, cmdsize, segname, vmaddr, vmsize, fileoff, filesize, maxprot, initprot, nsects, flags) super(view, cmd, cmdsize) - @segname = segname.delete("\x00") + @segname = segname @vmaddr = vmaddr @vmsize = vmsize @fileoff = fileoff @@ -448,6 +476,42 @@ module MachO return false if flag.nil? flags & flag == flag end + + # Guesses the alignment of the segment. + # @return [Integer] the guessed alignment, as a power of 2 + # @note See `guess_align` in `cctools/misc/lipo.c` + def guess_align + return Sections::MAX_SECT_ALIGN if vmaddr.zero? + + align = 0 + segalign = 1 + + while (segalign & vmaddr).zero? + segalign <<= 1 + align += 1 + end + + return 2 if align < 2 + return Sections::MAX_SECT_ALIGN if align > Sections::MAX_SECT_ALIGN + + align + end + + # @return [Hash] a hash representation of this {SegmentCommand} + def to_h + { + "segname" => segname, + "vmaddr" => vmaddr, + "vmsize" => vmsize, + "fileoff" => fileoff, + "filesize" => filesize, + "maxprot" => maxprot, + "initprot" => initprot, + "nsects" => nsects, + "flags" => flags, + "sections" => sections.map(&:to_h), + }.merge super + end end # A load command indicating that part of this file is to be mapped into @@ -455,7 +519,7 @@ module MachO class SegmentCommand64 < SegmentCommand # @see MachOStructure::FORMAT # @api private - FORMAT = "L=2a16Q=4l=2L=2".freeze + FORMAT = "L=2Z16Q=4l=2L=2".freeze # @see MachOStructure::SIZEOF # @api private @@ -510,6 +574,16 @@ module MachO [cmd, cmdsize, string_offsets[:name], timestamp, current_version, compatibility_version].pack(format) + string_payload end + + # @return [Hash] a hash representation of this {DylibCommand} + def to_h + { + "name" => name.to_h, + "timestamp" => timestamp, + "current_version" => current_version, + "compatibility_version" => compatibility_version, + }.merge super + end end # A load command representing some aspect of the dynamic linker, depending @@ -546,6 +620,13 @@ module MachO cmdsize = SIZEOF + string_payload.bytesize [cmd, cmdsize, string_offsets[:name]].pack(format) + string_payload end + + # @return [Hash] a hash representation of this {DylinkerCommand} + def to_h + { + "name" => name.to_h, + }.merge super + end end # A load command used to indicate dynamic libraries used in prebinding. @@ -576,6 +657,15 @@ module MachO @nmodules = nmodules @linked_modules = linked_modules end + + # @return [Hash] a hash representation of this {PreboundDylibCommand} + def to_h + { + "name" => name.to_h, + "nmodules" => nmodules, + "linked_modules" => linked_modules, + }.merge super + end end # A load command used to represent threads. @@ -641,6 +731,20 @@ module MachO @reserved5 = reserved5 @reserved6 = reserved6 end + + # @return [Hash] a hash representation of this {RoutinesCommand} + def to_h + { + "init_address" => init_address, + "init_module" => init_module, + "reserved1" => reserved1, + "reserved2" => reserved2, + "reserved3" => reserved3, + "reserved4" => reserved4, + "reserved5" => reserved5, + "reserved6" => reserved6, + }.merge super + end end # A load command containing the address of the dynamic shared library @@ -675,6 +779,13 @@ module MachO super(view, cmd, cmdsize) @umbrella = LCStr.new(self, umbrella) end + + # @return [Hash] a hash representation of this {SubFrameworkCommand} + def to_h + { + "umbrella" => umbrella.to_h, + }.merge super + end end # A load command signifying membership of a subumbrella containing the name @@ -696,6 +807,13 @@ module MachO super(view, cmd, cmdsize) @sub_umbrella = LCStr.new(self, sub_umbrella) end + + # @return [Hash] a hash representation of this {SubUmbrellaCommand} + def to_h + { + "sub_umbrella" => sub_umbrella.to_h, + }.merge super + end end # A load command signifying a sublibrary of a shared library. Corresponds @@ -717,6 +835,13 @@ module MachO super(view, cmd, cmdsize) @sub_library = LCStr.new(self, sub_library) end + + # @return [Hash] a hash representation of this {SubLibraryCommand} + def to_h + { + "sub_library" => sub_library.to_h, + }.merge super + end end # A load command signifying a shared library that is a subframework of @@ -738,6 +863,13 @@ module MachO super(view, cmd, cmdsize) @sub_client = LCStr.new(self, sub_client) end + + # @return [Hash] a hash representation of this {SubClientCommand} + def to_h + { + "sub_client" => sub_client.to_h, + }.merge super + end end # A load command containing the offsets and sizes of the link-edit 4.3BSD @@ -749,10 +881,10 @@ module MachO # @return [Integer] the number of symbol table entries attr_reader :nsyms - # @return the string table's offset + # @return [Integer] the string table's offset attr_reader :stroff - # @return the string table size in bytes + # @return [Integer] the string table size in bytes attr_reader :strsize # @see MachOStructure::FORMAT @@ -771,6 +903,16 @@ module MachO @stroff = stroff @strsize = strsize end + + # @return [Hash] a hash representation of this {SymtabCommand} + def to_h + { + "symoff" => symoff, + "nsyms" => nsyms, + "stroff" => stroff, + "strsize" => strsize, + }.merge super + end end # A load command containing symbolic information needed to support data @@ -864,6 +1006,30 @@ module MachO @locreloff = locreloff @nlocrel = nlocrel end + + # @return [Hash] a hash representation of this {DysymtabCommand} + def to_h + { + "ilocalsym" => ilocalsym, + "nlocalsym" => nlocalsym, + "iextdefsym" => iextdefsym, + "nextdefsym" => nextdefsym, + "iundefsym" => iundefsym, + "nundefsym" => nundefsym, + "tocoff" => tocoff, + "ntoc" => ntoc, + "modtaboff" => modtaboff, + "nmodtab" => nmodtab, + "extrefsymoff" => extrefsymoff, + "nextrefsyms" => nextrefsyms, + "indirectsymoff" => indirectsymoff, + "nindirectsyms" => nindirectsyms, + "extreloff" => extreloff, + "nextrel" => nextrel, + "locreloff" => locreloff, + "nlocrel" => nlocrel, + }.merge super + end end # A load command containing the offset and number of hints in the two-level @@ -895,6 +1061,15 @@ module MachO @table = TwolevelHintsTable.new(view, htoffset, nhints) end + # @return [Hash] a hash representation of this {TwolevelHintsCommand} + def to_h + { + "htoffset" => htoffset, + "nhints" => nhints, + "table" => table.hints.map(&:to_h), + }.merge super + end + # A representation of the two-level namespace lookup hints table exposed # by a {TwolevelHintsCommand} (`LC_TWOLEVEL_HINTS`). class TwolevelHintsTable @@ -927,6 +1102,14 @@ module MachO @isub_image = blob >> 24 @itoc = blob & 0x00FFFFFF end + + # @return [Hash] a hash representation of this {TwolevelHint} + def to_h + { + "isub_image" => isub_image, + "itoc" => itoc, + } + end end end end @@ -950,6 +1133,13 @@ module MachO super(view, cmd, cmdsize) @cksum = cksum end + + # @return [Hash] a hash representation of this {PrebindCksumCommand} + def to_h + { + "cksum" => cksum, + }.merge super + end end # A load command representing an rpath, which specifies a path that should @@ -984,6 +1174,13 @@ module MachO cmdsize = SIZEOF + string_payload.bytesize [cmd, cmdsize, string_offsets[:path]].pack(format) + string_payload end + + # @return [Hash] a hash representation of this {RpathCommand} + def to_h + { + "path" => path.to_h, + }.merge super + end end # A load command representing the offsets and sizes of a blob of data in @@ -1011,6 +1208,14 @@ module MachO @dataoff = dataoff @datasize = datasize end + + # @return [Hash] a hash representation of this {LinkeditDataCommand} + def to_h + { + "dataoff" => dataoff, + "datasize" => datasize, + }.merge super + end end # A load command representing the offset to and size of an encrypted @@ -1040,20 +1245,20 @@ module MachO @cryptsize = cryptsize @cryptid = cryptid end + + # @return [Hash] a hash representation of this {EncryptionInfoCommand} + def to_h + { + "cryptoff" => cryptoff, + "cryptsize" => cryptsize, + "cryptid" => cryptid, + }.merge super + end end # A load command representing the offset to and size of an encrypted # segment. Corresponds to LC_ENCRYPTION_INFO_64. - class EncryptionInfoCommand64 < LoadCommand - # @return [Integer] the offset to the encrypted segment - attr_reader :cryptoff - - # @return [Integer] the size of the encrypted segment - attr_reader :cryptsize - - # @return [Integer] the encryption system, or 0 if not encrypted yet - attr_reader :cryptid - + class EncryptionInfoCommand64 < EncryptionInfoCommand # @return [Integer] 64-bit padding value attr_reader :pad @@ -1067,12 +1272,16 @@ module MachO # @api private def initialize(view, cmd, cmdsize, cryptoff, cryptsize, cryptid, pad) - super(view, cmd, cmdsize) - @cryptoff = cryptoff - @cryptsize = cryptsize - @cryptid = cryptid + super(view, cmd, cmdsize, cryptoff, cryptsize, cryptid) @pad = pad end + + # @return [Hash] a hash representation of this {EncryptionInfoCommand64} + def to_h + { + "pad" => pad, + }.merge super + end end # A load command containing the minimum OS version on which the binary @@ -1121,6 +1330,16 @@ module MachO segs.join(".") end + + # @return [Hash] a hash representation of this {VersionMinCommand} + def to_h + { + "version" => version, + "version_string" => version_string, + "sdk" => sdk, + "sdk_string" => sdk_string, + }.merge super + end end # A load command containing the minimum OS version on which @@ -1156,6 +1375,40 @@ module MachO @tool_entries = ToolEntries.new(view, ntools) end + # A string representation of the binary's minimum OS version. + # @return [String] a string representing the minimum OS version. + def minos_string + binary = "%032b" % minos + segs = [ + binary[0..15], binary[16..23], binary[24..31] + ].map { |s| s.to_i(2) } + + segs.join(".") + end + + # A string representation of the binary's SDK version. + # @return [String] a string representing the SDK version. + def sdk_string + binary = "%032b" % sdk + segs = [ + binary[0..15], binary[16..23], binary[24..31] + ].map { |s| s.to_i(2) } + + segs.join(".") + end + + # @return [Hash] a hash representation of this {BuildVersionCommand} + def to_h + { + "platform" => platform, + "minos" => minos, + "minos_string" => minos_string, + "sdk" => sdk, + "sdk_string" => sdk_string, + "tool_entries" => tool_entries.tools.map(&:to_h), + }.merge super + end + # A representation of the tool versions exposed # by a {BuildVersionCommand} (`LC_BUILD_VERSION`). class ToolEntries @@ -1181,37 +1434,23 @@ module MachO # @return [Integer] the tool's version number attr_reader :version - # @param tool 32-bit integer - # # @param version 32-bit integer + # @param tool [Integer] 32-bit integer + # @param version [Integer] 32-bit integer # @api private def initialize(tool, version) @tool = tool @version = version end + + # @return [Hash] a hash representation of this {Tool} + def to_h + { + "tool" => tool, + "version" => version, + } + end end end - - # A string representation of the binary's minimum OS version. - # @return [String] a string representing the minimum OS version. - def minos_string - binary = "%032b" % minos - segs = [ - binary[0..15], binary[16..23], binary[24..31] - ].map { |s| s.to_i(2) } - - segs.join(".") - end - - # A string representation of the binary's SDK version. - # @return [String] a string representing the SDK version. - def sdk_string - binary = "%032b" % sdk - segs = [ - binary[0..15], binary[16..23], binary[24..31] - ].map { |s| s.to_i(2) } - - segs.join(".") - end end # A load command containing the file offsets and sizes of the new @@ -1272,6 +1511,22 @@ module MachO @export_off = export_off @export_size = export_size end + + # @return [Hash] a hash representation of this {DyldInfoCommand} + def to_h + { + "rebase_off" => rebase_off, + "rebase_size" => rebase_size, + "bind_off" => bind_off, + "bind_size" => bind_size, + "weak_bind_off" => weak_bind_off, + "weak_bind_size" => weak_bind_size, + "lazy_bind_off" => lazy_bind_off, + "lazy_bind_size" => lazy_bind_size, + "export_off" => export_off, + "export_size" => export_size, + }.merge super + end end # A load command containing linker options embedded in object files. @@ -1293,6 +1548,13 @@ module MachO super(view, cmd, cmdsize) @count = count end + + # @return [Hash] a hash representation of this {LinkerOptionCommand} + def to_h + { + "count" => count, + }.merge super + end end # A load command specifying the offset of main(). Corresponds to LC_MAIN. @@ -1317,6 +1579,14 @@ module MachO @entryoff = entryoff @stacksize = stacksize end + + # @return [Hash] a hash representation of this {EntryPointCommand} + def to_h + { + "entryoff" => entryoff, + "stacksize" => stacksize, + }.merge super + end end # A load command specifying the version of the sources used to build the @@ -1350,6 +1620,14 @@ module MachO segs.join(".") end + + # @return [Hash] a hash representation of this {SourceVersionCommand} + def to_h + { + "version" => version, + "version_string" => version_string, + }.merge super + end end # An obsolete load command containing the offset and size of the (GNU style) @@ -1375,6 +1653,14 @@ module MachO @offset = offset @size = size end + + # @return [Hash] a hash representation of this {SymsegCommand} + def to_h + { + "offset" => offset, + "size" => size, + }.merge super + end end # An obsolete load command containing a free format string table. Each @@ -1412,6 +1698,14 @@ module MachO @name = LCStr.new(self, name) @header_addr = header_addr end + + # @return [Hash] a hash representation of this {FvmfileCommand} + def to_h + { + "name" => name.to_h, + "header_addr" => header_addr, + }.merge super + end end # An obsolete load command containing the path to a library to be loaded @@ -1440,6 +1734,52 @@ module MachO @minor_version = minor_version @header_addr = header_addr end + + # @return [Hash] a hash representation of this {FvmlibCommand} + def to_h + { + "name" => name.to_h, + "minor_version" => minor_version, + "header_addr" => header_addr, + }.merge super + end + end + + # A load command containing an owner name and offset/size for an arbitrary data region. + # Corresponds to LC_NOTE. + class NoteCommand < LoadCommand + # @return [String] the name of the owner for this note + attr_reader :data_owner + + # @return [Integer] the offset, within the file, of the note + attr_reader :offset + + # @return [Integer] the size, in bytes, of the note + attr_reader :size + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2Z16Q=2".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 48 + + def initialize(view, cmd, cmdsize, data_owner, offset, size) + super(view, cmd, cmdsize) + @data_owner = data_owner + @offset = offset + @size = size + end + + # @return [Hash] a hash representation of this {NoteCommand} + def to_h + { + "data_owner" => data_owner, + "offset" => offset, + "size" => size, + }.merge super + end end end end diff --git a/Library/Homebrew/vendor/macho/macho/macho_file.rb b/Library/Homebrew/vendor/macho/macho/macho_file.rb index f8c0bd1c57..3c61bf06b7 100644 --- a/Library/Homebrew/vendor/macho/macho/macho_file.rb +++ b/Library/Homebrew/vendor/macho/macho/macho_file.rb @@ -25,7 +25,7 @@ module MachO # @note load commands are provided in order of ascending offset. attr_reader :load_commands - # Creates a new MachOFile instance from a binary string. + # Creates a new instance from a binary string. # @param bin [String] a binary string containing raw Mach-O data # @return [MachOFile] a new MachOFile def self.new_from_bin(bin) @@ -35,7 +35,7 @@ module MachO instance end - # Creates a new FatFile from the given filename. + # Creates a new instance from data read from the given filename. # @param filename [String] the Mach-O file to load from # @raise [ArgumentError] if the given file does not exist def initialize(filename) @@ -219,8 +219,7 @@ module MachO update_sizeofcmds(sizeofcmds - lc.cmdsize) # pad the space after the load commands to preserve offsets - null_pad = "\x00" * lc.cmdsize - @raw_data.insert(header.class.bytesize + sizeofcmds - lc.cmdsize, null_pad) + @raw_data.insert(header.class.bytesize + sizeofcmds - lc.cmdsize, Utils.nullpad(lc.cmdsize)) populate_fields if options.fetch(:repopulate, true) end @@ -252,6 +251,33 @@ module MachO end end + # The segment alignment for the Mach-O. Guesses conservatively. + # @return [Integer] the alignment, as a power of 2 + # @note This is **not** the same as {#alignment}! + # @note See `get_align` and `get_align_64` in `cctools/misc/lipo.c` + def segment_alignment + # special cases: 12 for x86/64/PPC/PP64, 14 for ARM/ARM64 + return 12 if %i[i386 x86_64 ppc ppc64].include?(cputype) + return 14 if %i[arm arm64].include?(cputype) + + cur_align = Sections::MAX_SECT_ALIGN + + segments.each do |segment| + if filetype == :object + # start with the smallest alignment, and work our way up + align = magic32? ? 2 : 3 + segment.sections.each do |section| + align = section.align unless section.align <= align + end + else + align = segment.guess_align + end + cur_align = align if align < cur_align + end + + cur_align + end + # The Mach-O's dylib ID, or `nil` if not a dylib. # @example # file.dylib_id # => 'libBar.dylib' @@ -408,6 +434,14 @@ module MachO File.open(@filename, "wb") { |f| f.write(@raw_data) } end + # @return [Hash] a hash representation of this {MachOFile} + def to_h + { + "header" => header.to_h, + "load_commands" => load_commands.map(&:to_h), + } + end + private # The file's Mach-O header structure. diff --git a/Library/Homebrew/vendor/macho/macho/sections.rb b/Library/Homebrew/vendor/macho/macho/sections.rb index 7e96888dbb..61d646743b 100644 --- a/Library/Homebrew/vendor/macho/macho/sections.rb +++ b/Library/Homebrew/vendor/macho/macho/sections.rb @@ -13,6 +13,10 @@ module MachO # system settable attributes mask SECTION_ATTRIBUTES_SYS = 0x00ffff00 + # maximum specifiable section alignment, as a power of 2 + # @note see `MAXSECTALIGN` macro in `cctools/misc/lipo.c` + MAX_SECT_ALIGN = 15 + # association of section flag symbols to values # @api private SECTION_FLAGS = { @@ -104,7 +108,7 @@ module MachO attr_reader :reserved2 # @see MachOStructure::FORMAT - FORMAT = "a16a16L=9".freeze + FORMAT = "Z16Z16L=9".freeze # @see MachOStructure::SIZEOF SIZEOF = 68 @@ -125,16 +129,14 @@ module MachO @reserved2 = reserved2 end - # @return [String] the section's name, with any trailing NULL characters - # removed + # @return [String] the section's name def section_name - sectname.delete("\x00") + sectname end - # @return [String] the parent segment's name, with any trailing NULL - # characters removed + # @return [String] the parent segment's name def segment_name - segname.delete("\x00") + segname end # @return [Boolean] whether the section is empty (i.e, {size} is 0) @@ -151,6 +153,23 @@ module MachO return false if flag.nil? flags & flag == flag end + + # @return [Hash] a hash representation of this {Section} + def to_h + { + "sectname" => sectname, + "segname" => segname, + "addr" => addr, + "size" => size, + "offset" => offset, + "align" => align, + "reloff" => reloff, + "nreloc" => nreloc, + "flags" => flags, + "reserved1" => reserved1, + "reserved2" => reserved2, + }.merge super + end end # Represents a section of a segment for 64-bit architectures. @@ -159,7 +178,7 @@ module MachO attr_reader :reserved3 # @see MachOStructure::FORMAT - FORMAT = "a16a16Q=2L=8".freeze + FORMAT = "Z16Z16Q=2L=8".freeze # @see MachOStructure::SIZEOF SIZEOF = 80 @@ -171,6 +190,13 @@ module MachO nreloc, flags, reserved1, reserved2) @reserved3 = reserved3 end + + # @return [Hash] a hash representation of this {Section64} + def to_h + { + "reserved3" => reserved3, + }.merge super + end end end end diff --git a/Library/Homebrew/vendor/macho/macho/structure.rb b/Library/Homebrew/vendor/macho/macho/structure.rb index 792aeeeac1..7bece4d704 100644 --- a/Library/Homebrew/vendor/macho/macho/structure.rb +++ b/Library/Homebrew/vendor/macho/macho/structure.rb @@ -26,5 +26,15 @@ module MachO new(*bin.unpack(format)) end + + # @return [Hash] a hash representation of this {MachOStructure}. + def to_h + { + "structure" => { + "format" => self.class::FORMAT, + "bytesize" => self.class.bytesize, + }, + } + end end end diff --git a/Library/Homebrew/vendor/macho/macho/utils.rb b/Library/Homebrew/vendor/macho/macho/utils.rb index 3bc81df478..a766de23ce 100644 --- a/Library/Homebrew/vendor/macho/macho/utils.rb +++ b/Library/Homebrew/vendor/macho/macho/utils.rb @@ -22,6 +22,16 @@ module MachO round(size, alignment) - size end + # Returns a string of null bytes of the requested (non-negative) size + # @param size [Integer] the size of the nullpad + # @return [String] the null string (or empty string, for `size = 0`) + # @raise [ArgumentError] if a non-positive nullpad is requested + def self.nullpad(size) + raise ArgumentError, "size < 0: #{size}" if size.negative? + + "\x00" * size + end + # Converts an abstract (native-endian) String#unpack format to big or # little. # @param format [String] the format string being converted @@ -46,11 +56,11 @@ module MachO strings.each do |key, string| offsets[key] = next_offset payload << string - payload << "\x00" + payload << Utils.nullpad(1) next_offset += string.bytesize + 1 end - payload << "\x00" * padding_for(fixed_offset + payload.bytesize, alignment) + payload << Utils.nullpad(padding_for(fixed_offset + payload.bytesize, alignment)) [payload, offsets] end diff --git a/Library/Homebrew/vendor/macho/macho/view.rb b/Library/Homebrew/vendor/macho/macho/view.rb index c03c2580e8..5bd40e7f0e 100644 --- a/Library/Homebrew/vendor/macho/macho/view.rb +++ b/Library/Homebrew/vendor/macho/macho/view.rb @@ -19,5 +19,13 @@ module MachO @endianness = endianness @offset = offset end + + # @return [Hash] a hash representation of this {MachOView}. + def to_h + { + "endianness" => endianness, + "offset" => offset, + } + end end end