223 lines
4.8 KiB
Ruby
223 lines
4.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Sass
|
|
# The {ELF} class.
|
|
#
|
|
# It parses ELF header to extract interpreter.
|
|
# @see https://github.com/torvalds/linux/blob/HEAD/include/uapi/linux/elf.h
|
|
# @see https://github.com/torvalds/linux/blob/HEAD/kernel/kexec_elf.c
|
|
class ELF
|
|
module PackInfo
|
|
PACK_MAP = {
|
|
Elf32_Ehdr: 'S<2L<5S<6',
|
|
Elf64_Ehdr: 'S<2L<Q<3L<S<6',
|
|
Elf32_Phdr: 'L<8',
|
|
Elf64_Phdr: 'L<2Q<6'
|
|
}.freeze
|
|
|
|
SIZE_MAP = {
|
|
Elf32_Ehdr: 36,
|
|
Elf64_Ehdr: 48,
|
|
Elf32_Phdr: 32,
|
|
Elf64_Phdr: 56
|
|
}.freeze
|
|
|
|
STRUCT_MAP = {
|
|
Elf32_Ehdr: %i[
|
|
e_type
|
|
e_machine
|
|
e_version
|
|
e_entry
|
|
e_phoff
|
|
e_shoff
|
|
e_flags
|
|
e_ehsize
|
|
e_phentsize
|
|
e_phnum
|
|
e_shentsize
|
|
e_shnum
|
|
e_shstrndx
|
|
].freeze,
|
|
Elf64_Ehdr: %i[
|
|
e_type
|
|
e_machine
|
|
e_version
|
|
e_entry
|
|
e_phoff
|
|
e_shoff
|
|
e_flags
|
|
e_ehsize
|
|
e_phentsize
|
|
e_phnum
|
|
e_shentsize
|
|
e_shnum
|
|
e_shstrndx
|
|
].freeze,
|
|
Elf32_Phdr: %i[
|
|
p_type
|
|
p_offset
|
|
p_vaddr
|
|
p_paddr
|
|
p_filesz
|
|
p_memsz
|
|
p_flags
|
|
p_align
|
|
].freeze,
|
|
Elf64_Phdr: %i[
|
|
p_type
|
|
p_flags
|
|
p_offset
|
|
p_vaddr
|
|
p_paddr
|
|
p_filesz
|
|
p_memsz
|
|
p_align
|
|
].freeze
|
|
}.freeze
|
|
end
|
|
|
|
private_constant :PackInfo
|
|
|
|
# These constants are for the segment types stored in the image headers
|
|
PT_NULL = 0
|
|
PT_LOAD = 1
|
|
PT_DYNAMIC = 2
|
|
PT_INTERP = 3
|
|
PT_NOTE = 4
|
|
PT_SHLIB = 5
|
|
PT_PHDR = 6
|
|
PT_TLS = 7
|
|
PT_LOOS = 0x60000000
|
|
PT_HIOS = 0x6fffffff
|
|
PT_LOPROC = 0x70000000
|
|
PT_HIPROC = 0x7fffffff
|
|
|
|
# These constants define the different elf file types
|
|
ET_NONE = 0
|
|
ET_REL = 1
|
|
ET_EXEC = 2
|
|
ET_DYN = 3
|
|
ET_CORE = 4
|
|
ET_LOPROC = 0xff00
|
|
ET_HIPROC = 0xffff
|
|
|
|
EI_NIDENT = 16
|
|
|
|
# e_ident[] indexes
|
|
EI_MAG0 = 0
|
|
EI_MAG1 = 1
|
|
EI_MAG2 = 2
|
|
EI_MAG3 = 3
|
|
EI_CLASS = 4
|
|
EI_DATA = 5
|
|
EI_VERSION = 6
|
|
EI_OSABI = 7
|
|
EI_PAD = 8
|
|
|
|
# EI_MAG
|
|
ELFMAG0 = 0x7f
|
|
ELFMAG1 = 0x45
|
|
ELFMAG2 = 0x4c
|
|
ELFMAG3 = 0x46
|
|
ELFMAG = [ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3].pack('C*')
|
|
SELFMAG = 4
|
|
|
|
# e_ident[EI_CLASS]
|
|
ELFCLASSNONE = 0
|
|
ELFCLASS32 = 1
|
|
ELFCLASS64 = 2
|
|
ELFCLASSNUM = 3
|
|
|
|
# e_ident[EI_DATA]
|
|
ELFDATANONE = 0
|
|
ELFDATA2LSB = 1
|
|
ELFDATA2MSB = 2
|
|
|
|
def initialize(buffer)
|
|
@buffer = buffer
|
|
|
|
@ehdr = { e_ident: @buffer.read(EI_NIDENT).unpack('C*') }
|
|
raise ArgumentError unless @ehdr[:e_ident].slice(EI_MAG0, SELFMAG).pack('C*') == ELFMAG
|
|
|
|
case @ehdr[:e_ident][EI_CLASS]
|
|
when ELFCLASS32
|
|
elf_ehdr = :Elf32_Ehdr
|
|
elf_phdr = :Elf32_Phdr
|
|
when ELFCLASS64
|
|
elf_ehdr = :Elf64_Ehdr
|
|
elf_phdr = :Elf64_Phdr
|
|
else
|
|
raise EncodingError
|
|
end
|
|
|
|
case @ehdr[:e_ident][EI_DATA]
|
|
when ELFDATA2LSB
|
|
little_endian = true
|
|
when ELFDATA2MSB
|
|
little_endian = false
|
|
else
|
|
raise EncodingError
|
|
end
|
|
|
|
@ehdr.merge!(read(elf_ehdr, little_endian))
|
|
|
|
@buffer.seek(@ehdr[:e_phoff], IO::SEEK_SET)
|
|
@proghdrs = Array.new(@ehdr[:e_phnum]) do
|
|
read(elf_phdr, little_endian)
|
|
end
|
|
end
|
|
|
|
def relocatable?
|
|
@ehdr[:e_type] == ET_REL
|
|
end
|
|
|
|
def executable?
|
|
@ehdr[:e_type] == ET_EXEC
|
|
end
|
|
|
|
def shared_object?
|
|
@ehdr[:e_type] == ET_DYN
|
|
end
|
|
|
|
def core?
|
|
@ehdr[:e_type] == ET_CORE
|
|
end
|
|
|
|
def interpreter
|
|
phdr = @proghdrs.find { |p| p[:p_type] == PT_INTERP }
|
|
return if phdr.nil?
|
|
|
|
@buffer.seek(phdr[:p_offset], IO::SEEK_SET)
|
|
interpreter = @buffer.read(phdr[:p_filesz])
|
|
raise EncodingError unless interpreter.end_with?("\0")
|
|
|
|
interpreter.chomp!("\0")
|
|
end
|
|
|
|
private
|
|
|
|
def read(type, little_endian)
|
|
size = PackInfo::SIZE_MAP[type]
|
|
format = little_endian ? PackInfo::PACK_MAP[type] : PackInfo::PACK_MAP[type].tr('<', '>')
|
|
[PackInfo::STRUCT_MAP[type], @buffer.read(size).unpack(format)].transpose.to_h
|
|
end
|
|
|
|
INTERPRETER = begin
|
|
proc_self_exe = '/proc/self/exe'
|
|
if File.exist?(proc_self_exe)
|
|
File.open(proc_self_exe, 'rb') do |file|
|
|
elf = ELF.new(file)
|
|
interpreter = elf.interpreter
|
|
if interpreter.nil? && elf.shared_object?
|
|
File.readlink(proc_self_exe)
|
|
else
|
|
interpreter
|
|
end
|
|
end
|
|
end
|
|
end.freeze
|
|
end
|
|
|
|
private_constant :ELF
|
|
end
|