class Elf::File
Attributes
abi[R]
abi_version[R]
data_encoding[R]
ehsize[R]
elf_class[R]
entry_address[R]
flags[R]
machine[R]
phentsize[R]
phnum[R]
phoff[R]
shentsize[R]
shnum[R]
shoff[R]
raw data access
shstrndx[R]
string_table[R]
type[R]
version[R]
Public Class Methods
new(path)
click to toggle source
Calls superclass method
# File lib/elf/file.rb, line 151 def initialize(path) _checkvalidpath(path) super(path, "rb") begin begin raise NotAnELF unless readexactly(4) == MagicString rescue EOFError raise NotAnELF end begin @elf_class = Class[read_u8] rescue Value::OutOfBound => e raise InvalidElfClass.new(e.val) end begin @data_encoding = DataEncoding[read_u8] rescue Value::OutOfBound => e raise InvalidDataEncoding.new(e.val) end @version = read_u8 raise UnsupportedElfVersion.new(@version) if @version > 1 begin @abi = OsAbi[read_u8] rescue Value::OutOfBound => e raise InvalidOsAbi.new(e.val) end @abi_version = read_u8 seek(16, IO::SEEK_SET) set_endian(DataEncoding::BytestreamMapping[@data_encoding]) begin @type = Type[read_half] rescue Value::OutOfBound => e raise InvalidElfType.new(e.val) end begin @machine = Machine[read_half] rescue Value::OutOfBound => e raise InvalidMachine.new(e.val) end @version = read_word @entry_address = read_addr @phoff = read_off @shoff = read_off @flags = read_word @ehsize = read_half @phentsize = read_half @phnum = read_half @shentsize = read_half @shnum = read_half @shstrndx = read_half elf32 = elf_class == Class::Elf32 @sections = {} @sections_data = [] seek(@shoff) for i in 1..@shnum sectdata = {} sectdata[:idx] = i-1 sectdata[:name_idx] = read_word sectdata[:type_id] = read_word sectdata[:flags_val] = elf32 ? read_word : read_xword sectdata[:addr] = read_addr sectdata[:offset] = read_off sectdata[:size] = elf32 ? read_word : read_xword sectdata[:link] = read_word sectdata[:info] = read_word sectdata[:addralign] = elf32 ? read_word : read_xword sectdata[:entsize] = elf32 ? read_word : read_xword @sections_data << sectdata end # When the section header string table index is set to zero, # there is not going to be a string table in the file, this # happens usually when the file is a static ELF file built # directly with an assembler, or when it was passed through # the elfkickers' sstrip utility. # # To handle this specific case, set the @string_table attribute # to false, that is distinct from nil, and raise # MissingStringTable on request. If the string table is not yet # loaded raise instead StringTableNotLoaded. if @shstrndx == 0 or not self[@shstrndx].is_a? StringTable @string_table = false else @string_table = self[@shstrndx] @sections_names = {} @sections_data.each do |sectdata| @sections_names[@string_table[sectdata[:name_idx]]] = sectdata[:idx] end end rescue ::Exception => e close raise e end end
Public Instance Methods
[](sect_idx_or_name)
click to toggle source
# File lib/elf/file.rb, line 298 def [](sect_idx_or_name) if sect_idx_or_name.is_a? String and not @string_table.is_a? Elf::Section raise MissingStringTable.new(sect_idx_or_name) if @string_table == false raise StringTableNotLoaded.new(sect_idx_or_name) if @string_table.nil? end load_section(sect_idx_or_name) unless @sections.has_key? sect_idx_or_name return @sections[sect_idx_or_name] end
address_print_size()
click to toggle source
Returns the hex address size for the file.
Since each ELF file uses either 32- or 64-bit addresses, it is important to know how many characters a file’s address would require when printed as an hexadecimal string.
# File lib/elf/file.rb, line 346 def address_print_size (@elf_class == Elf::Class::Elf32 ? 8 : 16) end
arm_be8?()
click to toggle source
# File lib/elf/file.rb, line 398 def arm_be8? return nil if machine != Elf::Machine::ARM return (@flags & ARM::EFlags_BE8) == ARM::EFlags_BE8 end
arm_eabi_version()
click to toggle source
# File lib/elf/file.rb, line 392 def arm_eabi_version return nil if machine != Elf::Machine::ARM return (@flags & ARM::EFlags_EABI_Mask) >> 24 end
each_section() { |sections[sectdata| ... }
click to toggle source
# File lib/elf/file.rb, line 310 def each_section @sections_data.each do |sectdata| load_section(sectdata[:idx]) yield @sections[sectdata[:idx]] end end
find_section_by_addr(addr)
click to toggle source
# File lib/elf/file.rb, line 317 def find_section_by_addr(addr) @sections_data.each do |sectdata| next unless sectdata[:addr] == addr load_section(sectdata[:idx]) return @sections[sectdata[:idx]] end end
has_section?(sect_idx_or_name)
click to toggle source
# File lib/elf/file.rb, line 325 def has_section?(sect_idx_or_name) if sect_idx_or_name.is_a? String and not @string_table.is_a? Elf::Section raise MissingStringTable.new(sect_idx_or_name) if @string_table == false raise StringTableNotLoaded.new(sect_idx_or_name) if @string_table.nil? end if sect_idx_or_name.is_a? Integer return @sections_data[sect_idx_or_name] != nil elsif sect_idx_or_name.is_a? String return @sections_names.has_key?(sect_idx_or_name) else raise TypeError.new("wrong argument type #{sect_idx_or_name.class} (expected String or Integer)") end end
is_compatible(other)
click to toggle source
Checks whether two ELF files are compatible one with the other for linking
This function has to check whether two ELF files can be linked together (either at build time or at load time), and thus checks for class, encoding, versioning, ABI and machine type.
Note that it explicitly does not check for ELF file type since you can link different type of files together, like an Executable with a Dynamic
library.
# File lib/elf/file.rb, line 372 def is_compatible(other) raise TypeError.new("wrong argument type #{other.class} (expected Elf::File)") unless other.is_a? Elf::File @elf_class == other.elf_class and @data_encoding == other.data_encoding and @version == other.version and @abi == other.abi and @abi_version == other.abi_version and @machine == other.machine end
load_section(sect_idx_or_name)
click to toggle source
# File lib/elf/file.rb, line 266 def load_section(sect_idx_or_name) if sect_idx_or_name.is_a? Integer raise MissingSection.new(sect_idx_or_name) unless @sections_data[sect_idx_or_name] @sections[sect_idx_or_name] = Section.read(self, sect_idx_or_name, @sections_data[sect_idx_or_name]) else raise MissingSection.new(sect_idx_or_name) unless @sections_names[sect_idx_or_name] load_section @sections_names[sect_idx_or_name] @sections[sect_idx_or_name] = @sections[@sections_names[sect_idx_or_name]] end end
read_addr()
click to toggle source
# File lib/elf/file.rb, line 94 def read_addr case @elf_class when Class::Elf32 then read_u32 when Class::Elf64 then read_u64 end end
read_off()
click to toggle source
# File lib/elf/file.rb, line 101 def read_off case @elf_class when Class::Elf32 then read_u32 when Class::Elf64 then read_u64 end end
sections()
click to toggle source
# File lib/elf/file.rb, line 294 def sections return @sections_data.size end
summary()
click to toggle source
# File lib/elf/file.rb, line 350 def summary $stdout.puts "ELF file #{path}" $stdout.puts "ELF class: #{@elf_class} #{@data_encoding} ver. #{@version}" $stdout.puts "ELF ABI: #{@abi} ver. #{@abi_version}" $stdout.puts "ELF type: #{@type} machine: #{@machine}" $stdout.puts "Sections:" @sections.values.uniq.each do |sh| sh.summary end return nil end
Private Instance Methods
_checkvalidpath(path)
click to toggle source
# File lib/elf/file.rb, line 119 def _checkvalidpath(path) # We're going to check the path we're given for a few reasons, # the first of which is that we do not want to open a pipe or # something like that. If we were just to leave it to File.open, # we would end up stuck waiting for data on a named pipe, for # instance. # # We cannot just use File.file? either because it'll be ignoring # ENOENT by default (which would be bad for us). # # If we were just to use File.ftype we would have to handle # manually the links... since Pathname will properly report # ENOENT for broken links, we're going to keep it this way. path = Pathname.new(path) unless path.is_a? Pathname case path.ftype when "directory" then raise Errno::EISDIR when "file" then # do nothing when "link" # we use path.realpath here; the reason is that if # we're to use readlink we're going to have a lot of # trouble to find the correct path. We cannot just # always use realpath as that will run too many stat # calls and have a huge hit on performance. _checkvalidpath(path.realpath) else raise Errno::EINVAL end end