Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 3 additions & 17 deletions Library/Homebrew/extend/os/linux/extend/pathname.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
class Pathname
# @private
def elf?
# See: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
read(4) == "\x7fELF"
end
require "os/linux/elf"

# @private
def dynamic_elf?
if which "readelf"
popen_read("readelf", "-l", to_path).include?(" DYNAMIC ")
elsif which "file"
!popen_read("file", "-L", "-b", to_path)[/dynamic|shared/].nil?
else
raise StandardError, "Neither `readelf` nor `file` is available "\
"to determine whether '#{self}' is dynamically or statically linked."
end
end
class Pathname
prepend ELFShim
end
2 changes: 1 addition & 1 deletion Library/Homebrew/extend/os/mac/extend/pathname.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require "os/mac/mach"

class Pathname
include MachOShim
prepend MachOShim
end
4 changes: 4 additions & 0 deletions Library/Homebrew/extend/pathname.rb
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,10 @@ def inspect
end
}
end

def mach_o_bundle?
false
end
end

require "extend/os/pathname"
Expand Down
160 changes: 160 additions & 0 deletions Library/Homebrew/os/linux/elf.rb
Original file line number Diff line number Diff line change
@@ -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?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stick a newline after each return; makes this easier to follow/scan (and consider doing this elsewhere in the file).


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
2 changes: 1 addition & 1 deletion Library/Homebrew/os/mac/linkage_checker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def initialize(keg, formula = nil)
def check_dylibs
@keg.find do |file|
next if file.symlink? || file.directory?
next unless file.dylib? || file.mach_o_executable? || file.mach_o_bundle?
next unless file.dylib? || file.binary_executable? || file.mach_o_bundle?

# weakly loaded dylibs may not actually exist on disk, so skip them
# when checking for broken linkage
Expand Down
2 changes: 2 additions & 0 deletions Library/Homebrew/os/mac/mach.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ def mach_o_executable?
mach_data.any? { |m| m.fetch(:type) == :executable }
end

alias binary_executable? mach_o_executable?

# @private
def mach_o_bundle?
mach_data.any? { |m| m.fetch(:type) == :bundle }
Expand Down