Class: ElfUtils::ElfFile

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/elf_utils/elf_file.rb

Overview

Read ELF and DWARF data from any class that provides a #pread method.

Examples:

dump the symbols found in an ELF file

ElfUtils::ElfFile.open("spec/data/complex_64be-dwarf64-v5") do |elf_file|
  pp elf_file.symbols
end

read complex structure from running process

# open an ELF file, in this case an executable
elf_file = ElfUtils.open("a.out")

# get the Symbol instance for a global variable
symbol = elf_file.symbol(:some_global_var)      # => #<ElfUtils::Symbol ...>

# get the address of the symbol
addr = symbol.addr                              # => 0x40001000

# relocate the load segments to account for ASLR.  Addresses for load segments
# gleaned manually from /proc/pid/map.
elf_file.load_segments[0].relocate(text_addr)
elf_file.load_segments[1].relocate(data_addr)

# get the address of the symbol after relocation
addr = symbol.addr                              # => 0x90301000

# get the data type for the symbol; requires DWARF debug information
type = symbol.ctype                             # => #<CTypes::Struct ...>

# open the memory for a process running this executable, and read the value of
# the global variable.
value = File.open(File.join("/proc", pid, "mem")) do |mem|
    # read the raw bytes for the variable
    bytes = mem.pread(type.size, addr)          # => "\xef\x99\xde... "

    # unpack the bytes
    type.unpack_one(bytes)                      # => { field: val, ... }
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io) ⇒ ElfFile

create an instance of ElfFile

Parameters:

  • io (IO, String, #pread)

    Anything that provides the #pread(max_len, offset) method



79
80
81
82
83
84
# File 'lib/elf_utils/elf_file.rb', line 79

def initialize(io)
  io.force_encoding("ASCII-8BIT") if io.is_a?(String)
  @io = io
  @header = load_header(io)
  @type_prefix = (elf_class == :elf32) ? "Elf32" : "Elf64"
end

Instance Attribute Details

#headerObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



87
88
89
# File 'lib/elf_utils/elf_file.rb', line 87

def header
  @header
end

Class Method Details

.open(path) ⇒ ElfFile .open(path) {|ElfFile| ... } ⇒ Object

open a file path, and return an ElfFile instance

Examples:

elf_file = ElfFile.open("spec/data/complex_64be-dwarf64-v5")
pp(elf_file.symbols)
elf_file.close
ElfFile.open("spec/data/complex_64be-dwarf64-v5") do |elf_file|
  pp(elf_file.symbols)
end

Overloads:

  • .open(path) ⇒ ElfFile

    Parameters:

    • path (String)

      file path

    Returns:

  • .open(path) {|ElfFile| ... } ⇒ Object

    Parameters:

    • path (String)

      file path

    Yields:

    • (ElfFile)

      invokes block with opened file, will close when block returns



66
67
68
69
70
71
72
73
74
# File 'lib/elf_utils/elf_file.rb', line 66

def self.open(path)
  if block_given?
    File.open(path) do |f|
      yield new(f)
    end
  else
    new(File.open(path))
  end
end

Instance Method Details

#addr_typeObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

get the ctype that represents an address for this ELF file



247
248
249
# File 'lib/elf_utils/elf_file.rb', line 247

def addr_type
  elf_type(:Addr)
end

#debug_infoSection::DebugInfo

return the .symtab section

Returns:



168
169
170
# File 'lib/elf_utils/elf_file.rb', line 168

def debug_info
  section(".debug_info")
end

#elf_classSymbol

Return the ELF class according to the ident in the header

Returns:

  • (Symbol)

    :elf32 or :elf64



97
98
99
100
101
102
103
104
105
106
# File 'lib/elf_utils/elf_file.rb', line 97

def elf_class
  case @header.e_ident.ei_class
  when Types::ELFCLASS32
    :elf32
  when Types::ELFCLASS64
    :elf64
  else
    raise InvalidFormat, "Unsupported ELF ident.ei_class: %p", @header
  end
end

#elf_type(name) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

lookup the ctypes datatype for this file’s bitsize (ELF32 vs ELF64)

Parameters:

  • name (Symbol, String)

    name of type

See Also:



241
242
243
# File 'lib/elf_utils/elf_file.rb', line 241

def elf_type(name)
  Types.const_get("#{@type_prefix}_#{name}").with_endian(endian)
end

#endianSymbol

get the endian according to the ELF header

Returns:

  • (Symbol)

    :big or :little



110
111
112
113
114
115
116
117
118
119
# File 'lib/elf_utils/elf_file.rb', line 110

def endian
  case @header.e_ident.ei_data
  when :lsb
    :little
  when :msb
    :big
  else
    raise "Invalid EI_DATA value in ELF header ident: %p", @header
  end
end

#load_segmentsArray<Segment::Base>

Return the list of load segments

Returns:



137
138
139
# File 'lib/elf_utils/elf_file.rb', line 137

def load_segments
  segments.select { |s| s.type == :load }
end

#pathString?

return the path of the ElfFile

Returns:

  • (String, nil)

    path of the ElfFile



91
92
93
# File 'lib/elf_utils/elf_file.rb', line 91

def path
  @io.path if @io.respond_to?(:path)
end

#pread(type_or_size, offset) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

pread wrapper providing support for type lookup by name



230
231
232
233
234
235
# File 'lib/elf_utils/elf_file.rb', line 230

def pread(type_or_size, offset)
  type, size = parse_type(type_or_size)
  buf = @io.pread(size, offset)
  return buf unless type
  type.unpack(buf)
end

#relocatable?Boolean

check if this is a relocatable ELF file

Returns:

  • (Boolean)

    true if the file is relocatable



123
124
125
# File 'lib/elf_utils/elf_file.rb', line 123

def relocatable?
  @header.e_type == :rel
end

#section(name) ⇒ Section::Base?

lookup a section by name

Returns:

  • (Section::Base, nil)

    section instance if found, nil otherwise



183
184
185
186
187
188
# File 'lib/elf_utils/elf_file.rb', line 183

def section(name)
  @sections_by_name ||= sections.each_with_object({}) do |section, cache|
    cache[section.name] = section
  end
  @sections_by_name[name]
end

#sectionsArray<Section::Base>

get the sections present in the ELF file

Returns:



174
175
176
177
178
179
# File 'lib/elf_utils/elf_file.rb', line 174

def sections
  @sections ||= pread([:Shdr, @header.e_shnum], @header.e_shoff)
    .map do |hdr|
      Section.from_header(self, hdr)
    end
end

#segmentsArray<Segment::Base>

Get the segments

Returns:



129
130
131
132
133
# File 'lib/elf_utils/elf_file.rb', line 129

def segments
  @segments ||= pread([:Phdr, @header.e_phnum], @header.e_phoff).map do |hdr|
    Segment.from_header(self, hdr)
  end
end

#shstrtabSection

Return the .shstrtab section

Returns:



143
144
145
146
147
148
149
150
151
152
# File 'lib/elf_utils/elf_file.rb', line 143

def shstrtab
  # XXX we end up with a duplicate copy of this section, but we need to
  # get the shstrtab while building the full sections list
  @shstrtab ||= begin
    hdr_class = elf_type(:Shdr)
    offset = hdr_class.size * @header.e_shstrndx
    hdr = pread(hdr_class, @header.e_shoff + offset)
    Section.from_header(self, hdr)
  end
end

#strtabSection::Strtab

Return the .strtab section

Returns:



156
157
158
# File 'lib/elf_utils/elf_file.rb', line 156

def strtab
  section(".strtab")
end

#symbol(name) ⇒ Symbol, name

lookup a symbol by name

Parameters:

  • symbol (String, Symbol)

    name

Returns:



199
200
201
202
# File 'lib/elf_utils/elf_file.rb', line 199

def symbol(name)
  name = name.to_s
  symbols.find { |s| s.name == name }
end

#symbol_at_addr(addr) ⇒ Symbol, name

lookup a symbol by memory address

Parameters:

  • in-memory (Integer)

    address

Returns:



207
208
209
210
211
212
# File 'lib/elf_utils/elf_file.rb', line 207

def symbol_at_addr(addr)
  symbols.find do |sym|
    next unless sym.section&.alloc?
    sym.to_range.include?(addr)
  end
end

#symbolsArray<Symbol>

return the list of symbols found in the ELF file

Returns:



192
193
194
# File 'lib/elf_utils/elf_file.rb', line 192

def symbols
  symtab&.symbols || []
end

#symtabSection::Symtab

return the .symtab section

Returns:



162
163
164
# File 'lib/elf_utils/elf_file.rb', line 162

def symtab
  @symtab ||= section(".symtab") || section(".dynsym")
end

#type(name) ⇒ Object Also known as: ctype

Get the ctypes datatype for a type defined in the .debug_info section

Examples:

lookup a structure type by name from .debug_info section

ElfFile.open("spec/data/complex_64be-dwarf64-v5") do |elf_file|
  elf_file.type("struct tlv")
end   # => #<CTypes::Struct ... >

Parameters:

  • name (String)

    name of type

Raises:



221
222
223
224
225
# File 'lib/elf_utils/elf_file.rb', line 221

def type(name)
  raise Error, "File does not contain .debug_info section: #{path}" unless
    (debug_info = section(".debug_info"))
  debug_info.type(name)
end