| 
									
										
										
										
											2020-10-10 14:16:11 +02:00
										 |  |  | # typed: false | 
					
						
							| 
									
										
										
										
											2019-04-19 15:38:03 +09:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  | # {Pathname} extension for dealing with ELF files. | 
					
						
							| 
									
										
										
										
											2018-10-18 21:42:43 -04:00
										 |  |  | # @see https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  | # | 
					
						
							|  |  |  | # @api private | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  | module ELFShim | 
					
						
							|  |  |  |   MAGIC_NUMBER_OFFSET = 0
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :MAGIC_NUMBER_OFFSET | 
					
						
							| 
									
										
										
										
											2019-04-19 15:38:03 +09:00
										 |  |  |   MAGIC_NUMBER_ASCII = "\x7fELF" | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :MAGIC_NUMBER_ASCII | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   OS_ABI_OFFSET = 0x07
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :OS_ABI_OFFSET | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   OS_ABI_SYSTEM_V = 0
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :OS_ABI_SYSTEM_V | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   OS_ABI_LINUX = 3
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :OS_ABI_LINUX | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   TYPE_OFFSET = 0x10
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :TYPE_OFFSET | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   TYPE_EXECUTABLE = 2
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :TYPE_EXECUTABLE | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   TYPE_SHARED = 3
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :TYPE_SHARED | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   ARCHITECTURE_OFFSET = 0x12
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :ARCHITECTURE_OFFSET | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   ARCHITECTURE_I386 = 0x3
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :ARCHITECTURE_I386 | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   ARCHITECTURE_POWERPC = 0x14
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :ARCHITECTURE_POWERPC | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   ARCHITECTURE_ARM = 0x28
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :ARCHITECTURE_ARM | 
					
						
							| 
									
										
										
										
											2021-07-18 16:55:57 +08:00
										 |  |  |   ARCHITECTURE_X86_64 = 0x3E
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :ARCHITECTURE_X86_64 | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   ARCHITECTURE_AARCH64 = 0xB7
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :ARCHITECTURE_AARCH64 | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   def read_uint8(offset) | 
					
						
							| 
									
										
										
										
											2019-10-13 10:13:42 +01:00
										 |  |  |     read(1, offset).unpack1("C") | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def read_uint16(offset) | 
					
						
							| 
									
										
										
										
											2019-10-13 10:13:42 +01:00
										 |  |  |     read(2, offset).unpack1("v") | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def elf? | 
					
						
							|  |  |  |     return @elf if defined? @elf | 
					
						
							|  |  |  |     return @elf = false unless read(MAGIC_NUMBER_ASCII.size, MAGIC_NUMBER_OFFSET) == MAGIC_NUMBER_ASCII | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Check that this ELF file is for Linux or System V. | 
					
						
							|  |  |  |     # OS_ABI is often set to 0 (System V), regardless of the target platform. | 
					
						
							|  |  |  |     @elf = [OS_ABI_LINUX, OS_ABI_SYSTEM_V].include? read_uint8(OS_ABI_OFFSET) | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def arch | 
					
						
							|  |  |  |     return :dunno unless elf? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @arch ||= case read_uint16(ARCHITECTURE_OFFSET) | 
					
						
							|  |  |  |     when ARCHITECTURE_I386 then :i386 | 
					
						
							|  |  |  |     when ARCHITECTURE_X86_64 then :x86_64 | 
					
						
							|  |  |  |     when ARCHITECTURE_POWERPC then :powerpc | 
					
						
							|  |  |  |     when ARCHITECTURE_ARM then :arm | 
					
						
							|  |  |  |     when ARCHITECTURE_AARCH64 then :arm64 | 
					
						
							|  |  |  |     else :dunno | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def elf_type | 
					
						
							|  |  |  |     return :dunno unless elf? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @elf_type ||= case read_uint16(TYPE_OFFSET) | 
					
						
							|  |  |  |     when TYPE_EXECUTABLE then :executable | 
					
						
							|  |  |  |     when TYPE_SHARED then :dylib | 
					
						
							|  |  |  |     else :dunno | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def dylib? | 
					
						
							|  |  |  |     elf_type == :dylib | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def binary_executable? | 
					
						
							|  |  |  |     elf_type == :executable | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-10 06:31:31 +05:30
										 |  |  |   def rpath | 
					
						
							|  |  |  |     return @rpath if defined? @rpath | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-08 06:41:51 +05:30
										 |  |  |     @rpath = rpath_using_patchelf_rb | 
					
						
							| 
									
										
										
										
											2020-07-10 06:31:31 +05:30
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-23 03:18:29 +05:30
										 |  |  |   def interpreter | 
					
						
							|  |  |  |     return @interpreter if defined? @interpreter | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-08 06:41:51 +05:30
										 |  |  |     @interpreter = patchelf_patcher.interpreter | 
					
						
							| 
									
										
										
										
											2019-07-18 15:22:09 +08:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-05 20:32:37 +05:30
										 |  |  |   def patch!(interpreter: nil, rpath: nil) | 
					
						
							|  |  |  |     return if interpreter.blank? && rpath.blank? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if HOMEBREW_PATCHELF_RB_WRITE | 
					
						
							|  |  |  |       save_using_patchelf_rb interpreter, rpath | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       save_using_patchelf interpreter, rpath | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   def dynamic_elf? | 
					
						
							|  |  |  |     return @dynamic_elf if defined? @dynamic_elf | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-08 06:41:51 +05:30
										 |  |  |     @dynamic_elf = patchelf_patcher.elf.segment_by_type(:DYNAMIC).present? | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   # Helper class for reading metadata from an ELF file. | 
					
						
							|  |  |  |   # | 
					
						
							|  |  |  |   # @api private | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   class Metadata | 
					
						
							|  |  |  |     attr_reader :path, :dylib_id, :dylibs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def initialize(path) | 
					
						
							|  |  |  |       @path = path | 
					
						
							|  |  |  |       @dylibs = [] | 
					
						
							|  |  |  |       @dylib_id, needed = needed_libraries path | 
					
						
							|  |  |  |       return if needed.empty? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ldd = DevelopmentTools.locate "ldd" | 
					
						
							|  |  |  |       ldd_output = Utils.popen_read(ldd, path.expand_path.to_s).split("\n") | 
					
						
							|  |  |  |       return unless $CHILD_STATUS.success? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ldd_paths = ldd_output.map do |line| | 
					
						
							|  |  |  |         match = line.match(/\t.+ => (.+) \(.+\)|\t(.+) => not found/) | 
					
						
							|  |  |  |         next unless match | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |         match.captures.compact.first | 
					
						
							|  |  |  |       end.compact | 
					
						
							|  |  |  |       @dylibs = ldd_paths.select do |ldd_path| | 
					
						
							|  |  |  |         next true unless ldd_path.start_with? "/" | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |         needed.include? File.basename(ldd_path) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def needed_libraries(path) | 
					
						
							| 
									
										
										
										
											2020-07-26 04:43:30 +05:30
										 |  |  |       return [nil, []] unless path.dynamic_elf? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-08 06:41:51 +05:30
										 |  |  |       needed_libraries_using_patchelf_rb path | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-02 18:36:13 +05:30
										 |  |  |     def needed_libraries_using_patchelf_rb(path) | 
					
						
							|  |  |  |       patcher = path.patchelf_patcher | 
					
						
							| 
									
										
										
										
											2020-07-10 06:31:31 +05:30
										 |  |  |       [patcher.soname, patcher.needed] | 
					
						
							| 
									
										
										
										
											2020-06-02 18:36:13 +05:30
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   end | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private_constant :Metadata | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-05 20:32:37 +05:30
										 |  |  |   def save_using_patchelf(new_interpreter, new_rpath) | 
					
						
							|  |  |  |     patchelf = DevelopmentTools.locate "patchelf" | 
					
						
							| 
									
										
										
										
											2021-01-24 21:40:41 -05:00
										 |  |  |     odie "Could not locate `patchelf`; please run `brew install patchelf`" if patchelf.blank? | 
					
						
							| 
									
										
										
										
											2020-08-05 20:32:37 +05:30
										 |  |  |     args = [] | 
					
						
							|  |  |  |     args << "--set-interpreter" << new_interpreter if new_interpreter.present? | 
					
						
							|  |  |  |     args << "--force-rpath" << "--set-rpath" << new_rpath if new_rpath.present? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Homebrew.safe_system(patchelf, *args, to_s) | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def save_using_patchelf_rb(new_interpreter, new_rpath) | 
					
						
							|  |  |  |     patcher = patchelf_patcher | 
					
						
							|  |  |  |     patcher.interpreter = new_interpreter if new_interpreter.present? | 
					
						
							|  |  |  |     patcher.rpath = new_rpath if new_rpath.present? | 
					
						
							|  |  |  |     patcher.save(patchelf_compatible: true) | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-10 06:31:31 +05:30
										 |  |  |   def rpath_using_patchelf_rb | 
					
						
							|  |  |  |     patchelf_patcher.runpath || patchelf_patcher.rpath | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-02 18:36:13 +05:30
										 |  |  |   def patchelf_patcher | 
					
						
							| 
									
										
										
										
											2020-07-28 23:09:13 +05:30
										 |  |  |     require "patchelf" | 
					
						
							| 
									
										
										
										
											2020-08-05 20:32:37 +05:30
										 |  |  |     @patchelf_patcher ||= ::PatchELF::Patcher.new to_s, on_error: :silent | 
					
						
							| 
									
										
										
										
											2020-06-02 18:36:13 +05:30
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  |   def metadata | 
					
						
							|  |  |  |     @metadata ||= Metadata.new(self) | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2020-08-25 00:11:59 +02:00
										 |  |  |   private :metadata | 
					
						
							| 
									
										
										
										
											2017-12-01 16:29:50 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   def dylib_id | 
					
						
							|  |  |  |     metadata.dylib_id | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def dynamically_linked_libraries(*) | 
					
						
							|  |  |  |     metadata.dylibs | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |