Class: ELFTools::Structs::ELFStruct

Inherits:
BinData::Record
  • Object
show all
Defined in:
lib/elftools/structs.rb

Overview

The base structure to define common methods.

Constant Summary collapse

CHOICE_SIZE_T =

DRY. Many fields have different type in different arch.

proc do |t = 'uint'|
  { selection: :elf_class, choices: { 32 => :"#{t}32", 64 => :"#{t}64" }, copy_on_change: true }
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#elf_classInteger

Returns 32 or 64.

Returns:

  • (Integer)

    32 or 64.



18
19
20
# File 'lib/elftools/structs.rb', line 18

def elf_class
  @elf_class
end

#offsetInteger

Returns The file offset of this header.

Returns:

  • (Integer)

    The file offset of this header.



19
20
21
# File 'lib/elftools/structs.rb', line 19

def offset
  @offset
end

Class Method Details

.new(*args) ⇒ Object

Hooks the constructor.

BinData::Record doesn't allow us to override #initialize, so we hack new here.

Keyword arguments have to be taken as a trailing Hash instead of **kwargs: bindata defines new on each record class taking *args only, then re-dispatches it to the endian-specific subclass this method actually runs on, which collapses the caller's keywords into a positional Hash on the way. See override_new_in_class in https://github.com/dmendel/bindata/blob/master/lib/bindata/dsl.rb, which is the same in 2.5.1, 3.0.0, and master.



99
100
101
102
103
# File 'lib/elftools/structs.rb', line 99

def new(*args)
  kwargs = args.last.is_a?(Hash) ? args.last : {}
  offset = kwargs.delete(:offset)
  super.tap { |obj| obj.offset = offset }
end

.pack(val, bytes) ⇒ String

Deprecated.

Nothing here packs a patch by hand anymore, see #patches. This is kept for anyone who called it and goes in the next major.

Packs an integer to string.

Parameters:

  • val (Integer)
  • bytes (Integer)

Returns:

  • (String)

Raises:

  • (ArgumentError)


119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/elftools/structs.rb', line 119

def pack(val, bytes)
  raise ArgumentError, "Not supported assign type #{val.class}" unless val.is_a?(Integer)

  number = val & ((1 << (8 * bytes)) - 1)
  out = []
  bytes.times do
    out << (number & 0xff)
    number >>= 8
  end
  out = out.pack('C*')
  self_endian == :little ? out : out.reverse
end

.self_endian:little, :big

Gets the endianness of current class.

Returns:

  • (:little, :big)

    The endianness.



107
108
109
# File 'lib/elftools/structs.rb', line 107

def self_endian
  bindata_name[-2..] == 'be' ? :big : :little
end

Instance Method Details

#patchesHash{Integer => String}

Which bytes of this structure have been changed since it was read.

Every field answers alike, however deeply it is nested, because what is compared is the bytes the structure occupies rather than the assignments that were made to it. A field assigned the value it already held leaves nothing behind.

Examples:

header.e_ident.ei_abiversion = 41
header.patches
#=> { 8 => "\x29" }

Returns:

  • (Hash{Integer => String})

    Where each run of changed bytes starts, as an offset into the structure, and the bytes it is to be replaced with.



45
46
47
48
49
# File 'lib/elftools/structs.rb', line 45

def patches
  return {} if @source.nil?

  changed_runs(@source, to_binary_s)
end

#read(io) ⇒ ELFTools::Structs::ELFStruct

Reads the structure, remembering the bytes it was read from.

They are taken back off the stream. A stream that cannot be seeked is serialized instead.

Parameters:

  • io (#pos=, #read)

    The streaming object.

Returns:



27
28
29
30
# File 'lib/elftools/structs.rb', line 27

def read(io)
  start = io.pos if io.respond_to?(:pos)
  super.tap { @source = start.nil? ? to_binary_s : bytes_read(io, start) }
end