Class: ELFTools::Structs::ELFStruct
- Inherits:
-
BinData::Record
- Object
- BinData::Record
- ELFTools::Structs::ELFStruct
- Defined in:
- lib/elftools/structs.rb
Overview
The base structure to define common methods.
Direct Known Subclasses
ELF32_Phdr, ELF32_sym, ELF64_Phdr, ELF64_sym, ELF_Dyn, ELF_Ehdr, ELF_GnuHash, ELF_Hash, ELF_Nhdr, ELF_Rel, ELF_Rela, ELF_Shdr, ELF_Verdaux, ELF_Verdef, ELF_Vernaux, ELF_Verneed
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
- UNPACK_TEMPLATES =
How an integer of each width is packed, for
String#unpack, whether it records a sign or not. { little: { false => { 1 => 'C', 2 => 'v', 4 => 'V', 8 => 'Q<' }, true => { 1 => 'c', 2 => 's<', 4 => 'l<', 8 => 'q<' } }, big: { false => { 1 => 'C', 2 => 'n', 4 => 'N', 8 => 'Q>' }, true => { 1 => 'c', 2 => 's>', 4 => 'l>', 8 => 'q>' } } }.freeze
- SET_BITS =
Bytes of set bits to read a prototype from, longer than any structure here, so that reading one never runs short of them.
("\xff" * 256).b.freeze
Instance Attribute Summary collapse
-
#elf_class ⇒ Integer
32 or 64.
-
#offset ⇒ Integer
The file offset of this header.
Class Method Summary collapse
-
.new(*args) ⇒ Object
Hooks the constructor.
-
.num_bytes(elf_class:, endian:) ⇒ Integer
How many bytes a structure of this kind takes.
-
.pack(val, bytes) ⇒ String
deprecated
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.
-
.self_endian ⇒ :little, :big
Gets the endianness of current class.
-
.unpack_fields(bytes, elf_class:, endian:) ⇒ Hash{Symbol => Integer}
What the fields of a structure record, read straight from its bytes.
Instance Method Summary collapse
-
#patches ⇒ Hash{Integer => String}
Which bytes of this structure have been changed since it was read.
-
#read(io) ⇒ ELFTools::Structs::ELFStruct
Reads the structure, remembering the bytes it was read from.
Instance Attribute Details
#elf_class ⇒ Integer
Returns 32 or 64.
37 38 39 |
# File 'lib/elftools/structs.rb', line 37 def elf_class @elf_class end |
#offset ⇒ Integer
Returns The file offset of this header.
38 39 40 |
# File 'lib/elftools/structs.rb', line 38 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.
118 119 120 121 122 |
# File 'lib/elftools/structs.rb', line 118 def new(*args) kwargs = args.last.is_a?(Hash) ? args.last : {} offset = kwargs.delete(:offset) super.tap { |obj| obj.offset = offset } end |
.num_bytes(elf_class:, endian:) ⇒ Integer
How many bytes a structure of this kind takes.
163 164 165 166 167 168 |
# File 'lib/elftools/structs.rb', line 163 def num_bytes(elf_class:, endian:) @num_bytes ||= {} # Nested rather than keyed by the pair, which would make an array of # it for every structure read. (@num_bytes[elf_class] ||= {})[endian] ||= prototype(elf_class, endian).num_bytes end |
.pack(val, bytes) ⇒ String
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.
178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/elftools/structs.rb', line 178 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.
A class is of one endianness for as long as it exists, and asking bindata what it is named costs more than remembering the answer.
129 130 131 |
# File 'lib/elftools/structs.rb', line 129 def self_endian @self_endian ||= bindata_name[-2..] == 'be' ? :big : :little end |
.unpack_fields(bytes, elf_class:, endian:) ⇒ Hash{Symbol => Integer}
What the fields of a structure record, read straight from its bytes.
Reading a table of structures costs a structure for every entry of it otherwise, which is most of what reading the table costs. Nothing is remembered of the bytes, so a caller that means to assign to a field wants a structure instead.
147 148 149 150 151 152 153 154 |
# File 'lib/elftools/structs.rb', line 147 def unpack_fields(bytes, elf_class:, endian:) values = bytes.unpack(unpack_template(elf_class, endian)) fields = {} # Paired by hand rather than zipped, which would make an array for # every field of every structure read. field_names(elf_class, endian).each_with_index { |name, i| fields[name] = values[i] } fields end |
Instance Method Details
#patches ⇒ Hash{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.
64 65 66 67 68 |
# File 'lib/elftools/structs.rb', line 64 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.
46 47 48 49 |
# File 'lib/elftools/structs.rb', line 46 def read(io) start = io.pos if io.respond_to?(:pos) super.tap { @source = start.nil? ? to_binary_s : bytes_read(io, start) } end |