This commit is contained in:
@@ -0,0 +1,386 @@
|
||||
# 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
|
||||
# The {PackInfo} class.
|
||||
class PackInfo
|
||||
def initialize(format:, sizeof:, struct:)
|
||||
@format_le = format.freeze
|
||||
@format_be = format.tr('<', '>').freeze
|
||||
@sizeof = sizeof.freeze
|
||||
@struct = struct.freeze
|
||||
end
|
||||
|
||||
attr_reader :sizeof
|
||||
|
||||
def pack(io, data, little_endian)
|
||||
raise ArgumentError if io.write(data.values_at(*@struct).pack(format(little_endian))) != @sizeof
|
||||
end
|
||||
|
||||
def unpack(io, little_endian)
|
||||
@struct.zip(io.read(@sizeof).unpack(format(little_endian))).to_h
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def format(little_endian)
|
||||
little_endian ? @format_le : @format_be
|
||||
end
|
||||
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
|
||||
|
||||
PN_XNUM = 0xffff
|
||||
|
||||
# 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
|
||||
|
||||
Elf32_Ehdr = PackInfo.new(
|
||||
format: "a#{EI_NIDENT}S<2L<5S<6",
|
||||
sizeof: 52,
|
||||
struct: %i[
|
||||
e_ident
|
||||
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 = PackInfo.new(
|
||||
format: "a#{EI_NIDENT}S<2L<Q<3L<S<6",
|
||||
sizeof: 64,
|
||||
struct: %i[
|
||||
e_ident
|
||||
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
|
||||
|
||||
# These constants define the permissions on sections in the program header, p_flags.
|
||||
PF_R = 0x4
|
||||
PF_W = 0x2
|
||||
PF_X = 0x1
|
||||
|
||||
Elf32_Phdr = PackInfo.new(
|
||||
format: 'L<8',
|
||||
sizeof: 32,
|
||||
struct: %i[
|
||||
p_type
|
||||
p_offset
|
||||
p_vaddr
|
||||
p_paddr
|
||||
p_filesz
|
||||
p_memsz
|
||||
p_flags
|
||||
p_align
|
||||
]
|
||||
).freeze
|
||||
|
||||
Elf64_Phdr = PackInfo.new(
|
||||
format: 'L<2Q<6',
|
||||
sizeof: 56,
|
||||
struct: %i[
|
||||
p_type
|
||||
p_flags
|
||||
p_offset
|
||||
p_vaddr
|
||||
p_paddr
|
||||
p_filesz
|
||||
p_memsz
|
||||
p_align
|
||||
]
|
||||
).freeze
|
||||
|
||||
# sh_type
|
||||
SHT_NULL = 0
|
||||
SHT_PROGBITS = 1
|
||||
SHT_SYMTAB = 2
|
||||
SHT_STRTAB = 3
|
||||
SHT_RELA = 4
|
||||
SHT_HASH = 5
|
||||
SHT_DYNAMIC = 6
|
||||
SHT_NOTE = 7
|
||||
SHT_NOBITS = 8
|
||||
SHT_REL = 9
|
||||
SHT_SHLIB = 10
|
||||
SHT_DYNSYM = 11
|
||||
SHT_NUM = 12
|
||||
SHT_LOPROC = 0x70000000
|
||||
SHT_HIPROC = 0x7fffffff
|
||||
SHT_LOUSER = 0x80000000
|
||||
SHT_HIUSER = 0xffffffff
|
||||
|
||||
# sh_flags
|
||||
SHF_WRITE = 0x1
|
||||
SHF_ALLOC = 0x2
|
||||
SHF_EXECINSTR = 0x4
|
||||
SHF_RELA_LIVEPATCH = 0x00100000
|
||||
SHF_RO_AFTER_INIT = 0x00200000
|
||||
SHF_MASKPROC = 0xf0000000
|
||||
|
||||
# special section indexes
|
||||
SHN_UNDEF = 0
|
||||
SHN_LORESERVE = 0xff00
|
||||
SHN_LOPROC = 0xff00
|
||||
SHN_HIPROC = 0xff1f
|
||||
SHN_LIVEPATCH = 0xff20
|
||||
SHN_ABS = 0xfff1
|
||||
SHN_COMMON = 0xfff2
|
||||
SHN_HIRESERVE = 0xffff
|
||||
|
||||
Elf32_Shdr = PackInfo.new(
|
||||
format: 'L<10',
|
||||
sizeof: 40,
|
||||
struct: %i[
|
||||
sh_name
|
||||
sh_type
|
||||
sh_flags
|
||||
sh_addr
|
||||
sh_offset
|
||||
sh_size
|
||||
sh_link
|
||||
sh_info
|
||||
sh_addralign
|
||||
sh_entsize
|
||||
]
|
||||
).freeze
|
||||
|
||||
Elf64_Shdr = PackInfo.new(
|
||||
format: 'L<2Q<4L<2Q<2',
|
||||
sizeof: 64,
|
||||
struct: %i[
|
||||
sh_name
|
||||
sh_type
|
||||
sh_flags
|
||||
sh_addr
|
||||
sh_offset
|
||||
sh_size
|
||||
sh_link
|
||||
sh_info
|
||||
sh_addralign
|
||||
sh_entsize
|
||||
]
|
||||
).freeze
|
||||
|
||||
# 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(io, program_headers: true, section_headers: false)
|
||||
io.rewind
|
||||
e_ident = io.read(EI_NIDENT).unpack('C*')
|
||||
raise ArgumentError unless e_ident.slice(EI_MAG0, SELFMAG).pack('C*') == ELFMAG
|
||||
|
||||
case e_ident[EI_CLASS]
|
||||
when ELFCLASS32
|
||||
elf_ehdr = Elf32_Ehdr
|
||||
elf_phdr = Elf32_Phdr
|
||||
elf_shdr = Elf32_Shdr
|
||||
when ELFCLASS64
|
||||
elf_ehdr = Elf64_Ehdr
|
||||
elf_phdr = Elf64_Phdr
|
||||
elf_shdr = Elf64_Shdr
|
||||
else
|
||||
raise EncodingError
|
||||
end
|
||||
|
||||
case e_ident[EI_DATA]
|
||||
when ELFDATA2LSB
|
||||
little_endian = true
|
||||
when ELFDATA2MSB
|
||||
little_endian = false
|
||||
else
|
||||
raise EncodingError
|
||||
end
|
||||
|
||||
io.rewind
|
||||
ehdr = elf_ehdr.unpack(io, little_endian)
|
||||
ehdr[:e_ident] = e_ident
|
||||
|
||||
phdrs = if program_headers && ehdr[:e_phnum].positive?
|
||||
io.seek(ehdr[:e_phoff], IO::SEEK_SET)
|
||||
Array.new(ehdr[:e_phnum]) do
|
||||
elf_phdr.unpack(io, little_endian)
|
||||
end
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
shdrs = if section_headers && ehdr[:e_shnum].positive?
|
||||
io.seek(ehdr[:e_shoff], IO::SEEK_SET)
|
||||
Array.new(ehdr[:e_shnum]) do
|
||||
elf_shdr.unpack(io, little_endian)
|
||||
end
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
@io = io
|
||||
@ehdr = ehdr
|
||||
@phdrs = phdrs
|
||||
@shdrs = shdrs
|
||||
end
|
||||
|
||||
def dump(io)
|
||||
e_ident = @ehdr[:e_ident]
|
||||
raise ArgumentError unless e_ident.slice(EI_MAG0, SELFMAG).pack('C*') == ELFMAG
|
||||
|
||||
ehdr = @ehdr.dup
|
||||
ehdr[:e_ident] = e_ident.pack('C*')
|
||||
phdrs = @phdrs
|
||||
shdrs = @shdrs
|
||||
|
||||
case e_ident[EI_CLASS]
|
||||
when ELFCLASS32
|
||||
elf_ehdr = Elf32_Ehdr
|
||||
elf_phdr = Elf32_Phdr
|
||||
elf_shdr = Elf32_Shdr
|
||||
when ELFCLASS64
|
||||
elf_ehdr = Elf64_Ehdr
|
||||
elf_phdr = Elf64_Phdr
|
||||
elf_shdr = Elf64_Shdr
|
||||
else
|
||||
raise EncodingError
|
||||
end
|
||||
|
||||
case e_ident[EI_DATA]
|
||||
when ELFDATA2LSB
|
||||
little_endian = true
|
||||
when ELFDATA2MSB
|
||||
little_endian = false
|
||||
else
|
||||
raise EncodingError
|
||||
end
|
||||
|
||||
io.rewind
|
||||
elf_ehdr.pack(io, ehdr, little_endian)
|
||||
|
||||
io.seek(ehdr[:e_phoff], IO::SEEK_SET) if ehdr[:e_phnum].positive?
|
||||
phdrs.each do |phdr|
|
||||
elf_phdr.pack(io, phdr, little_endian)
|
||||
end
|
||||
|
||||
io.seek(ehdr[:e_shoff], IO::SEEK_SET) if ehdr[:e_shnum].positive?
|
||||
shdrs.each do |shdr|
|
||||
elf_shdr.pack(io, shdr, little_endian)
|
||||
end
|
||||
|
||||
io.flush
|
||||
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 = @phdrs.find { |p| p[:p_type] == PT_INTERP }
|
||||
return if phdr.nil?
|
||||
|
||||
@io.seek(phdr[:p_offset], IO::SEEK_SET)
|
||||
@io.read(phdr[:p_filesz]).unpack1('Z*')
|
||||
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
|
||||
Reference in New Issue
Block a user