Class: Elf::File

Inherits:
File
  • Object
show all
Includes:
BytestreamReader
Defined in:
lib/elf/file.rb

Defined Under Namespace

Modules: ARM Classes: InvalidDataEncoding, InvalidElfClass, InvalidElfType, InvalidMachine, InvalidOsAbi, MissingSection, MissingStringTable, NotAnELF, StringTableNotLoaded, Type, UnsupportedElfVersion

Constant Summary

Constants included from BytestreamReader

BytestreamReader::BigEndian, BytestreamReader::LittleEndian

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from BytestreamReader

#read_array_s16_be, #read_array_s16_le, #read_array_s32_be, #read_array_s32_le, #read_array_s64_be, #read_array_s64_le, #read_array_s8, #read_array_u16_be, #read_array_u16_le, #read_array_u32_be, #read_array_u32_le, #read_array_u64_be, #read_array_u64_le, #read_array_u8, #read_s16, #read_s16_be, #read_s16_le, #read_s32, #read_s32_be, #read_s32_le, #read_s64, #read_s64_be, #read_s64_le, #read_s8, #read_u16, #read_u16_be, #read_u16_le, #read_u32, #read_u32_be, #read_u32_le, #read_u64, #read_u64_be, #read_u64_le, #read_u8, #readexactly, #set_endian

Constructor Details

#initialize(path) ⇒ File

Returns a new instance of File.



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/elf/file.rb', line 152

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

Instance Attribute Details

#abiObject (readonly)

Returns the value of attribute abi.



86
87
88
# File 'lib/elf/file.rb', line 86

def abi
  @abi
end

#abi_versionObject (readonly)

Returns the value of attribute abi_version.



86
87
88
# File 'lib/elf/file.rb', line 86

def abi_version
  @abi_version
end

#data_encodingObject (readonly)

Returns the value of attribute data_encoding.



86
87
88
# File 'lib/elf/file.rb', line 86

def data_encoding
  @data_encoding
end

#ehsizeObject (readonly)

Returns the value of attribute ehsize.



86
87
88
# File 'lib/elf/file.rb', line 86

def ehsize
  @ehsize
end

#elf_classObject (readonly)

Returns the value of attribute elf_class.



86
87
88
# File 'lib/elf/file.rb', line 86

def elf_class
  @elf_class
end

#entry_addressObject (readonly)

Returns the value of attribute entry_address.



86
87
88
# File 'lib/elf/file.rb', line 86

def entry_address
  @entry_address
end

#flagsObject (readonly)

Returns the value of attribute flags.



86
87
88
# File 'lib/elf/file.rb', line 86

def flags
  @flags
end

#machineObject (readonly)

Returns the value of attribute machine.



86
87
88
# File 'lib/elf/file.rb', line 86

def machine
  @machine
end

#phentsizeObject (readonly)

Returns the value of attribute phentsize.



86
87
88
# File 'lib/elf/file.rb', line 86

def phentsize
  @phentsize
end

#phnumObject (readonly)

Returns the value of attribute phnum.



86
87
88
# File 'lib/elf/file.rb', line 86

def phnum
  @phnum
end

#phoffObject (readonly)

Returns the value of attribute phoff.



86
87
88
# File 'lib/elf/file.rb', line 86

def phoff
  @phoff
end

#shentsizeObject (readonly)

Returns the value of attribute shentsize.



86
87
88
# File 'lib/elf/file.rb', line 86

def shentsize
  @shentsize
end

#shnumObject (readonly)

Returns the value of attribute shnum.



86
87
88
# File 'lib/elf/file.rb', line 86

def shnum
  @shnum
end

#shoffObject (readonly)

raw data access



93
94
95
# File 'lib/elf/file.rb', line 93

def shoff
  @shoff
end

#shstrndxObject (readonly)

Returns the value of attribute shstrndx.



86
87
88
# File 'lib/elf/file.rb', line 86

def shstrndx
  @shstrndx
end

#string_tableObject (readonly)

Returns the value of attribute string_table.



90
91
92
# File 'lib/elf/file.rb', line 90

def string_table
  @string_table
end

#typeObject (readonly)

Returns the value of attribute type.



86
87
88
# File 'lib/elf/file.rb', line 86

def type
  @type
end

#versionObject (readonly)

Returns the value of attribute version.



86
87
88
# File 'lib/elf/file.rb', line 86

def version
  @version
end

Instance Method Details

#[](sect_idx_or_name) ⇒ Object



299
300
301
302
303
304
305
306
307
308
309
# File 'lib/elf/file.rb', line 299

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_sizeObject

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.



347
348
349
# File 'lib/elf/file.rb', line 347

def address_print_size
  (@elf_class == Elf::Class::Elf32 ? 8 : 16)
end

#arm_be8?Boolean

Returns:

  • (Boolean)


401
402
403
404
405
# File 'lib/elf/file.rb', line 401

def arm_be8?
  return nil if machine != Elf::Machine::ARM

  return (@flags & ARM::EFlags_BE8) == ARM::EFlags_BE8
end

#arm_eabi_versionObject



395
396
397
398
399
# File 'lib/elf/file.rb', line 395

def arm_eabi_version
  return nil if machine != Elf::Machine::ARM

  return (@flags & ARM::EFlags_EABI_Mask) >> 24
end

#each_sectionObject



311
312
313
314
315
316
# File 'lib/elf/file.rb', line 311

def each_section
  @sections_data.each do |sectdata|
    load_section(sectdata[:idx])
    yield @sections[sectdata[:idx]]
  end
end

#find_section_by_addr(addr) ⇒ Object



318
319
320
321
322
323
324
# File 'lib/elf/file.rb', line 318

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) ⇒ Boolean

Returns:

  • (Boolean)


326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/elf/file.rb', line 326

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) ⇒ Object

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.

Raises:

  • (TypeError)


373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/elf/file.rb', line 373

def is_compatible(other)
  raise TypeError.new("wrong argument type #{other.class} (expected Elf::File)") unless
    other.is_a? Elf::File

  compatible_abi = (@abi.linux_compatible? && other.abi.linux_compatible?) \
    || ([@abi, @abi_version] == [other.abi, other.abi_version])

  @elf_class == other.elf_class and
    @data_encoding == other.data_encoding and
    @version == other.version and
    @machine == other.machine and
    compatible_abi
end

#load_section(sect_idx_or_name) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/elf/file.rb', line 267

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_addrObject



95
96
97
98
99
100
# File 'lib/elf/file.rb', line 95

def read_addr
  case @elf_class
  when Class::Elf32 then read_u32
  when Class::Elf64 then read_u64
  end
end

#read_offObject



102
103
104
105
106
107
# File 'lib/elf/file.rb', line 102

def read_off
  case @elf_class
  when Class::Elf32 then read_u32
  when Class::Elf64 then read_u64
  end
end

#sectionsObject



295
296
297
# File 'lib/elf/file.rb', line 295

def sections
  return @sections_data.size
end

#summaryObject



351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/elf/file.rb', line 351

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