diff options
| author | Shaun Jackman | 2017-12-01 16:29:50 -0800 | 
|---|---|---|
| committer | Shaun Jackman | 2017-12-03 16:22:51 -0800 | 
| commit | e5847b652f93744d41f45b5636e67bb2748d67c7 (patch) | |
| tree | a68502820b15e73ebc589c1c10d4fef91aae65de /Library/Homebrew/os | |
| parent | 0ad42ebbcb735422612a7c6d4edc5ad88044dd4b (diff) | |
| download | brew-e5847b652f93744d41f45b5636e67bb2748d67c7.tar.bz2 | |
extend/pathname: Add os/linux/elf.rb
Diffstat (limited to 'Library/Homebrew/os')
| -rw-r--r-- | Library/Homebrew/os/linux/elf.rb | 160 | 
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  | 
