aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Homebrew/os/linux/elf.rb
diff options
context:
space:
mode:
Diffstat (limited to 'Library/Homebrew/os/linux/elf.rb')
-rw-r--r--Library/Homebrew/os/linux/elf.rb160
1 files changed, 160 insertions, 0 deletions
diff --git a/Library/Homebrew/os/linux/elf.rb b/Library/Homebrew/os/linux/elf.rb
new file mode 100644
index 000000000..1a53e50f3
--- /dev/null
+++ b/Library/Homebrew/os/linux/elf.rb
@@ -0,0 +1,160 @@
+module ELFShim
+ # See: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
+ MAGIC_NUMBER_OFFSET = 0
+ MAGIC_NUMBER_ASCII = "\x7fELF".freeze
+
+ OS_ABI_OFFSET = 0x07
+ OS_ABI_SYSTEM_V = 0
+ OS_ABI_LINUX = 3
+
+ TYPE_OFFSET = 0x10
+ TYPE_EXECUTABLE = 2
+ TYPE_SHARED = 3
+
+ ARCHITECTURE_OFFSET = 0x12
+ ARCHITECTURE_I386 = 0x3
+ ARCHITECTURE_POWERPC = 0x14
+ ARCHITECTURE_ARM = 0x28
+ ARCHITECTURE_X86_64 = 0x62
+ ARCHITECTURE_AARCH64 = 0xB7
+
+ def read_uint8(offset)
+ read(1, offset).unpack("C").first
+ end
+
+ def read_uint16(offset)
+ read(2, offset).unpack("v").first
+ 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
+
+ def dynamic_elf?
+ return @dynamic_elf if defined? @dynamic_elf
+
+ if which "readelf"
+ Utils.popen_read("readelf", "-l", to_path).include?(" DYNAMIC ")
+ elsif which "file"
+ !Utils.popen_read("file", "-L", "-b", to_path)[/dynamic|shared/].nil?
+ else
+ raise "Please install either readelf (from binutils) or file."
+ end
+ end
+
+ 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
+ match.captures.compact.first
+ end.compact
+ @dylibs = ldd_paths.select do |ldd_path|
+ next true unless ldd_path.start_with? "/"
+ needed.include? File.basename(ldd_path)
+ end
+ end
+
+ private
+
+ def needed_libraries(path)
+ if DevelopmentTools.locate "readelf"
+ needed_libraries_using_readelf path
+ elsif DevelopmentTools.locate "patchelf"
+ needed_libraries_using_patchelf path
+ else
+ raise "patchelf must be installed: brew install patchelf"
+ end
+ end
+
+ def needed_libraries_using_patchelf(path)
+ patchelf = DevelopmentTools.locate "patchelf"
+ if path.dylib?
+ command = [patchelf, "--print-soname", path.expand_path.to_s]
+ soname = Utils.popen_read(*command).chomp
+ raise ErrorDuringExecution, command unless $CHILD_STATUS.success?
+ end
+ command = [patchelf, "--print-needed", path.expand_path.to_s]
+ needed = Utils.popen_read(*command).split("\n")
+ raise ErrorDuringExecution, command unless $CHILD_STATUS.success?
+ [soname, needed]
+ end
+
+ def needed_libraries_using_readelf(path)
+ soname = nil
+ needed = []
+ command = ["readelf", "-d", path.expand_path.to_s]
+ lines = Utils.popen_read(*command).split("\n")
+ raise ErrorDuringExecution, command unless $CHILD_STATUS.success?
+ lines.each do |s|
+ filename = s[/\[(.*)\]/, 1]
+ next if filename.nil?
+ if s.include? "(SONAME)"
+ soname = filename
+ elsif s.include? "(NEEDED)"
+ needed << filename
+ end
+ end
+ [soname, needed]
+ end
+ end
+
+ def metadata
+ @metadata ||= Metadata.new(self)
+ end
+
+ def dylib_id
+ metadata.dylib_id
+ end
+
+ def dynamically_linked_libraries(*)
+ metadata.dylibs
+ end
+end