 9cf2710dc9
			
		
	
	
		9cf2710dc9
		
	
	
	
	
		
			
			Both the `CctoolsMachO` and `RubyMachO` module implement a common set of methods that simplify querying `mach_data`. Move these into a shared module, that gets included after either of these implementations is loaded and included in `Pathname`.
		
			
				
	
	
		
			100 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			100 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| module CctoolsMachO
 | |
|   # @private
 | |
|   OTOOL_RX = /\t(.*) \(compatibility version (?:\d+\.)*\d+, current version (?:\d+\.)*\d+\)/
 | |
| 
 | |
|   # Mach-O binary methods, see:
 | |
|   # /usr/include/mach-o/loader.h
 | |
|   # /usr/include/mach-o/fat.h
 | |
|   # @private
 | |
|   def mach_data
 | |
|     @mach_data ||= begin
 | |
|       offsets = []
 | |
|       mach_data = []
 | |
| 
 | |
|       header = read(8).unpack("N2")
 | |
|       case header[0]
 | |
|       when 0xcafebabe # universal
 | |
|         header[1].times do |i|
 | |
|           # header[1] is the number of struct fat_arch in the file.
 | |
|           # Each struct fat_arch is 20 bytes, and the 'offset' member
 | |
|           # begins 8 bytes into the struct, with an additional 8 byte
 | |
|           # offset due to the struct fat_header at the beginning of
 | |
|           # the file.
 | |
|           offsets << read(4, 20*i + 16).unpack("N")[0]
 | |
|         end
 | |
|       when 0xcefaedfe, 0xcffaedfe, 0xfeedface, 0xfeedfacf # Single arch
 | |
|         offsets << 0
 | |
|       else
 | |
|         raise "Not a Mach-O binary."
 | |
|       end
 | |
| 
 | |
|       offsets.each do |offset|
 | |
|         arch = case read(8, offset).unpack("N2")
 | |
|           when [0xcefaedfe, 0x07000000] then :i386
 | |
|           when [0xcffaedfe, 0x07000001] then :x86_64
 | |
|           when [0xfeedface, 0x00000012] then :ppc7400
 | |
|           when [0xfeedfacf, 0x01000012] then :ppc64
 | |
|           else :dunno
 | |
|           end
 | |
| 
 | |
|         type = case read(4, offset + 12).unpack("N")[0]
 | |
|           when 0x00000002, 0x02000000 then :executable
 | |
|           when 0x00000006, 0x06000000 then :dylib
 | |
|           when 0x00000008, 0x08000000 then :bundle
 | |
|           else :dunno
 | |
|           end
 | |
| 
 | |
|         mach_data << { :arch => arch, :type => type }
 | |
|       end
 | |
|       mach_data
 | |
|     rescue
 | |
|       []
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   # @private
 | |
|   class Metadata
 | |
|     attr_reader :path, :dylib_id, :dylibs
 | |
| 
 | |
|     def initialize(path)
 | |
|       @path = path
 | |
|       @dylib_id, @dylibs = parse_otool_L_output
 | |
|     end
 | |
| 
 | |
|     def parse_otool_L_output
 | |
|       args = ["-L", path.expand_path.to_s]
 | |
|       libs = Utils.popen_read(OS::Mac.otool, *args).split("\n")
 | |
|       unless $?.success?
 | |
|         raise ErrorDuringExecution.new(OS::Mac.otool, args)
 | |
|       end
 | |
| 
 | |
|       libs.shift # first line is the filename
 | |
| 
 | |
|       id = libs.shift[OTOOL_RX, 1] if path.dylib?
 | |
|       libs.map! { |lib| lib[OTOOL_RX, 1] }.compact!
 | |
| 
 | |
|       return id, libs
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   # @private
 | |
|   def mach_metadata
 | |
|     @mach_metadata ||= Metadata.new(self)
 | |
|   end
 | |
| 
 | |
|   # Returns an array containing all dynamically-linked libraries, based on the
 | |
|   # output of otool. This returns the install names, so these are not guaranteed
 | |
|   # to be absolute paths.
 | |
|   # Returns an empty array both for software that links against no libraries,
 | |
|   # and for non-mach objects.
 | |
|   # @private
 | |
|   def dynamically_linked_libraries
 | |
|     mach_metadata.dylibs
 | |
|   end
 | |
| 
 | |
|   # @private
 | |
|   def dylib_id
 | |
|     mach_metadata.dylib_id
 | |
|   end
 | |
| end
 |