167 lines
4.1 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
# @see https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
2017-12-01 16:29:50 -08:00
module ELFShim
MAGIC_NUMBER_OFFSET = 0
MAGIC_NUMBER_ASCII = "\x7fELF"
2017-12-01 16:29:50 -08:00
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
2019-07-17 16:57:01 +08:00
@dynamic_elf = if which "readelf"
2017-12-01 16:29:50 -08:00
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
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)
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)
return [nil, []] unless path.dynamic_elf?
2017-12-01 16:29:50 -08:00
patchelf = DevelopmentTools.locate "patchelf"
if path.dylib?
command = [patchelf, "--print-soname", path.expand_path.to_s]
2018-07-16 23:17:16 +02:00
soname = Utils.safe_popen_read(*command).chomp
2017-12-01 16:29:50 -08:00
end
command = [patchelf, "--print-needed", path.expand_path.to_s]
2018-07-16 23:17:16 +02:00
needed = Utils.safe_popen_read(*command).split("\n")
2017-12-01 16:29:50 -08:00
[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, err: :out).split("\n")
2017-12-01 16:29:50 -08:00
lines.each do |s|
next if s.start_with?("readelf: Warning: possibly corrupt ELF header")
2017-12-01 16:29:50 -08:00
filename = s[/\[(.*)\]/, 1]
next if filename.nil?
2018-09-17 02:45:00 +02:00
2017-12-01 16:29:50 -08:00
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