173 lines
4.0 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
2020-08-25 00:11:59 +02:00
# {Pathname} extension for dealing with ELF files.
#
# @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
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
2017-12-01 16:29:50 -08:00
ARCHITECTURE_X86_64 = 0x62
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
def rpath
return @rpath if defined? @rpath
@rpath = rpath_using_patchelf_rb
end
def interpreter
return @interpreter if defined? @interpreter
@interpreter = patchelf_patcher.interpreter
end
2017-12-01 16:29:50 -08:00
def dynamic_elf?
return @dynamic_elf if defined? @dynamic_elf
@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)
return [nil, []] unless path.dynamic_elf?
needed_libraries_using_patchelf_rb path
2017-12-01 16:29:50 -08:00
end
def needed_libraries_using_patchelf_rb(path)
patcher = path.patchelf_patcher
[patcher.soname, patcher.needed]
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
def rpath_using_patchelf_rb
patchelf_patcher.runpath || patchelf_patcher.rpath
end
def patchelf_patcher
2020-07-28 23:09:13 +05:30
require "patchelf"
@patchelf_patcher ||= PatchELF::Patcher.new to_s, on_error: :silent
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