elftools 1.3.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +193 -7
  3. data/lib/elftools/constants/machine.rb +248 -0
  4. data/lib/elftools/constants/machine_names.rb +246 -0
  5. data/lib/elftools/constants/relocation/aarch64.rb +213 -0
  6. data/lib/elftools/constants/relocation/alpha.rb +47 -0
  7. data/lib/elftools/constants/relocation/amdgpu.rb +28 -0
  8. data/lib/elftools/constants/relocation/arm.rb +150 -0
  9. data/lib/elftools/constants/relocation/avr.rb +51 -0
  10. data/lib/elftools/constants/relocation/bfin.rb +72 -0
  11. data/lib/elftools/constants/relocation/bpf.rb +21 -0
  12. data/lib/elftools/constants/relocation/cr16.rb +46 -0
  13. data/lib/elftools/constants/relocation/cris.rb +46 -0
  14. data/lib/elftools/constants/relocation/crx.rb +35 -0
  15. data/lib/elftools/constants/relocation/csky.rb +79 -0
  16. data/lib/elftools/constants/relocation/d10v.rb +23 -0
  17. data/lib/elftools/constants/relocation/d30v.rb +27 -0
  18. data/lib/elftools/constants/relocation/dlx.rb +24 -0
  19. data/lib/elftools/constants/relocation/epiphany.rb +28 -0
  20. data/lib/elftools/constants/relocation/fr30.rb +27 -0
  21. data/lib/elftools/constants/relocation/frv.rb +57 -0
  22. data/lib/elftools/constants/relocation/ft32.rb +27 -0
  23. data/lib/elftools/constants/relocation/h8.rb +69 -0
  24. data/lib/elftools/constants/relocation/hppa.rb +135 -0
  25. data/lib/elftools/constants/relocation/i370.rb +26 -0
  26. data/lib/elftools/constants/relocation/i386.rb +59 -0
  27. data/lib/elftools/constants/relocation/i860.rb +48 -0
  28. data/lib/elftools/constants/relocation/i960.rb +22 -0
  29. data/lib/elftools/constants/relocation/ia64.rb +117 -0
  30. data/lib/elftools/constants/relocation/ip2k.rb +29 -0
  31. data/lib/elftools/constants/relocation/iq2000.rb +27 -0
  32. data/lib/elftools/constants/relocation/kvx.rb +97 -0
  33. data/lib/elftools/constants/relocation/lm32.rb +32 -0
  34. data/lib/elftools/constants/relocation/loongarch.rb +144 -0
  35. data/lib/elftools/constants/relocation/m32c.rb +27 -0
  36. data/lib/elftools/constants/relocation/m32r.rb +57 -0
  37. data/lib/elftools/constants/relocation/m68hc11.rb +35 -0
  38. data/lib/elftools/constants/relocation/m68k.rb +57 -0
  39. data/lib/elftools/constants/relocation/mcore.rb +27 -0
  40. data/lib/elftools/constants/relocation/mep.rb +36 -0
  41. data/lib/elftools/constants/relocation/metag.rb +63 -0
  42. data/lib/elftools/constants/relocation/microblaze.rb +48 -0
  43. data/lib/elftools/constants/relocation/mips.rb +125 -0
  44. data/lib/elftools/constants/relocation/mmix.rb +51 -0
  45. data/lib/elftools/constants/relocation/mn10200.rb +22 -0
  46. data/lib/elftools/constants/relocation/mn10300.rb +49 -0
  47. data/lib/elftools/constants/relocation/moxie.rb +17 -0
  48. data/lib/elftools/constants/relocation/msp430.rb +27 -0
  49. data/lib/elftools/constants/relocation/msp430x.rb +37 -0
  50. data/lib/elftools/constants/relocation/mt.rb +21 -0
  51. data/lib/elftools/constants/relocation/nds32.rb +167 -0
  52. data/lib/elftools/constants/relocation/nfp.rb +41 -0
  53. data/lib/elftools/constants/relocation/nfp3200.rb +46 -0
  54. data/lib/elftools/constants/relocation/nios2.rb +74 -0
  55. data/lib/elftools/constants/relocation/or1k.rb +69 -0
  56. data/lib/elftools/constants/relocation/pj.rb +24 -0
  57. data/lib/elftools/constants/relocation/ppc.rb +131 -0
  58. data/lib/elftools/constants/relocation/ppc64.rb +177 -0
  59. data/lib/elftools/constants/relocation/pru.rb +31 -0
  60. data/lib/elftools/constants/relocation/riscv.rb +74 -0
  61. data/lib/elftools/constants/relocation/rl78.rb +70 -0
  62. data/lib/elftools/constants/relocation/rx.rb +81 -0
  63. data/lib/elftools/constants/relocation/s12z.rb +22 -0
  64. data/lib/elftools/constants/relocation/s390.rb +82 -0
  65. data/lib/elftools/constants/relocation/score.rb +36 -0
  66. data/lib/elftools/constants/relocation/sh.rb +124 -0
  67. data/lib/elftools/constants/relocation/sparc.rb +108 -0
  68. data/lib/elftools/constants/relocation/spu.rb +32 -0
  69. data/lib/elftools/constants/relocation/tic6x.rb +48 -0
  70. data/lib/elftools/constants/relocation/tilegx.rb +134 -0
  71. data/lib/elftools/constants/relocation/tilepro.rb +105 -0
  72. data/lib/elftools/constants/relocation/v800.rb +101 -0
  73. data/lib/elftools/constants/relocation/v850.rb +66 -0
  74. data/lib/elftools/constants/relocation/vax.rb +29 -0
  75. data/lib/elftools/constants/relocation/visium.rb +30 -0
  76. data/lib/elftools/constants/relocation/wasm32.rb +16 -0
  77. data/lib/elftools/constants/relocation/x86_64.rb +68 -0
  78. data/lib/elftools/constants/relocation/xgate.rb +34 -0
  79. data/lib/elftools/constants/relocation/xstormy16.rb +29 -0
  80. data/lib/elftools/constants/relocation/xtensa.rb +75 -0
  81. data/lib/elftools/constants/relocation/z80.rb +28 -0
  82. data/lib/elftools/constants/relocation.rb +181 -0
  83. data/lib/elftools/constants.rb +168 -231
  84. data/lib/elftools/dynamic/hash_table.rb +232 -0
  85. data/lib/elftools/dynamic/string_table.rb +30 -0
  86. data/lib/elftools/dynamic/symbols.rb +174 -0
  87. data/lib/elftools/dynamic/tag.rb +66 -0
  88. data/lib/elftools/dynamic/versions.rb +87 -0
  89. data/lib/elftools/dynamic.rb +119 -62
  90. data/lib/elftools/elf_file.rb +74 -22
  91. data/lib/elftools/lazy_array.rb +42 -4
  92. data/lib/elftools/note.rb +6 -3
  93. data/lib/elftools/relative_relocations.rb +88 -0
  94. data/lib/elftools/relocation.rb +144 -0
  95. data/lib/elftools/sections/dynamic_section.rb +13 -0
  96. data/lib/elftools/sections/relative_relocation_section.rb +46 -0
  97. data/lib/elftools/sections/relocation_section.rb +24 -43
  98. data/lib/elftools/sections/section.rb +35 -4
  99. data/lib/elftools/sections/sections.rb +8 -0
  100. data/lib/elftools/sections/sym_tab_section.rb +47 -35
  101. data/lib/elftools/sections/symbol.rb +202 -0
  102. data/lib/elftools/sections/version_definition_section.rb +40 -0
  103. data/lib/elftools/sections/version_need_section.rb +40 -0
  104. data/lib/elftools/sections/version_section.rb +44 -0
  105. data/lib/elftools/segments/dynamic_segment.rb +1 -0
  106. data/lib/elftools/segments/load_segment.rb +33 -5
  107. data/lib/elftools/segments/segment.rb +8 -4
  108. data/lib/elftools/structs.rb +132 -18
  109. data/lib/elftools/util.rb +69 -12
  110. data/lib/elftools/version.rb +1 -1
  111. data/lib/elftools/version_tables.rb +155 -0
  112. metadata +108 -11
@@ -1,5 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'elftools/constants'
4
+ require 'elftools/dynamic/string_table'
5
+ require 'elftools/dynamic/symbols'
6
+ require 'elftools/dynamic/versions'
7
+ require 'elftools/dynamic/tag'
8
+ require 'elftools/exceptions'
9
+ require 'elftools/relative_relocations'
10
+ require 'elftools/relocation'
11
+ require 'elftools/structs'
12
+
3
13
  module ELFTools
4
14
  # Define common methods for dynamic sections and dynamic segments.
5
15
  #
@@ -8,6 +18,9 @@ module ELFTools
8
18
  # and {ELFTools::Segments::DynamicSegment} because methods here assume some
9
19
  # attributes exist.
10
20
  module Dynamic
21
+ include Symbols
22
+ include Versions
23
+
11
24
  # Iterate all tags.
12
25
  #
13
26
  # @note
@@ -18,8 +31,8 @@ module ELFTools
18
31
  # @return [Enumerator<ELFTools::Dynamic::Tag>, Array<ELFTools::Dynamic::Tag>]
19
32
  # If block is not given, an enumerator will be returned.
20
33
  # Otherwise, return array of tags.
21
- def each_tags(&block)
22
- return enum_for(:each_tags) unless block_given?
34
+ def each_tag(&block)
35
+ return enum_for(:each_tag) unless block_given?
23
36
 
24
37
  arr = []
25
38
  0.step do |i|
@@ -30,11 +43,14 @@ module ELFTools
30
43
  arr
31
44
  end
32
45
 
46
+ # The name this used to go by, kept so that it keeps working.
47
+ alias each_tags each_tag
48
+
33
49
  # Use {#tags} to get all tags.
34
50
  # @return [Array<ELFTools::Dynamic::Tag>]
35
51
  # Array of tags.
36
52
  def tags
37
- @tags ||= each_tags.to_a
53
+ @tags ||= each_tag.to_a
38
54
  end
39
55
 
40
56
  # Get a tag of specific type.
@@ -63,7 +79,7 @@ module ELFTools
63
79
  # #=> #<ELFTools::Dynamic::Tag:0x0055d3d2d91b28 @header={:d_tag=>3, :d_val=>6295552}>
64
80
  def tag_by_type(type)
65
81
  type = Util.to_constant(Constants::DT, type)
66
- each_tags.find { |tag| tag.header.d_tag == type }
82
+ each_tag.find { |tag| tag.header.d_tag == type }
67
83
  end
68
84
 
69
85
  # Get tags of specific type.
@@ -75,7 +91,7 @@ module ELFTools
75
91
  # @see #tag_by_type
76
92
  def tags_by_type(type)
77
93
  type = Util.to_constant(Constants::DT, type)
78
- each_tags.select { |tag| tag.header.d_tag == type }
94
+ each_tag.select { |tag| tag.header.d_tag == type }
79
95
  end
80
96
 
81
97
  # Get the +n+-th tag.
@@ -100,7 +116,25 @@ module ELFTools
100
116
  dyn.elf_class = header.elf_class
101
117
  stream.pos = tag_start + n * dyn.num_bytes
102
118
  dyn.offset = stream.pos
103
- @tag_at_map[n] = Tag.new(dyn.read(stream), stream, method(:str_offset))
119
+ @tag_at_map[n] = Tag.new(dyn.read(stream), stream, string_table)
120
+ end
121
+
122
+ # The relocations the tags point at.
123
+ #
124
+ # Three tables record them: the one +DT_REL+ or +DT_RELA+ names, the one
125
+ # +DT_JMPREL+ names, whose entries are of the kind +DT_PLTREL+ names, and
126
+ # the one +DT_RELR+ names, which packs the relocations that only add the
127
+ # load bias into a bitmap and so records no type of its own.
128
+ # @return [Array<ELFTools::Relocation>] The relocations, in the order the
129
+ # tags record them, the packed ones last.
130
+ # @raise [ELFTools::ELFError]
131
+ # If a table is not in any loadable segment.
132
+ # @example
133
+ # elf.dynamic.relocations.map(&:type_name).uniq
134
+ # #=> ['R_X86_64_GLOB_DAT', 'R_X86_64_JUMP_SLOT']
135
+ def relocations
136
+ @relocations ||= relocation_tables.flat_map { |start, size, rela| read_relocations(start, size, rela) } +
137
+ packed_relocations
104
138
  end
105
139
 
106
140
  private
@@ -109,69 +143,92 @@ module ELFTools
109
143
  header.class.self_endian
110
144
  end
111
145
 
112
- # Get the DT_STRTAB's +d_val+ offset related to file.
113
- def str_offset
114
- # TODO: handle DT_STRTAB not exitsts.
115
- @str_offset ||= @offset_from_vma.call(tag_by_type(:strtab).header.d_val.to_i)
146
+ # Where each table of relocations starts, the tag recording how many bytes
147
+ # it takes, and whether its entries record an addend.
148
+ # @return [Array<Array(ELFTools::Dynamic::Tag, ELFTools::Dynamic::Tag, Boolean)>] The tables.
149
+ def relocation_tables
150
+ tables = %i[rel rela].filter_map do |type|
151
+ tag = tag_by_type(type)
152
+ [tag, tag_by_type(:"#{type}sz"), type == :rela] if tag
153
+ end
154
+ jmprel = tag_by_type(:jmprel)
155
+ return tables if jmprel.nil?
156
+
157
+ tables << [jmprel, tag_by_type(:pltrelsz), tag_by_type(:pltrel).header.d_val.to_i == Constants::DT_RELA]
116
158
  end
117
159
 
118
- # A tag class.
119
- class Tag
120
- attr_reader :header # @return [ELFTools::Structs::ELF_Dyn] The dynamic tag header.
121
- attr_reader :stream # @return [#pos=, #read] Streaming object.
122
-
123
- # Instantiate a {ELFTools::Dynamic::Tag} object.
124
- # @param [ELF_Dyn] header The dynamic tag header.
125
- # @param [#pos=, #read] stream Streaming object.
126
- # @param [Method] str_offset
127
- # Call this method to get the string offset related
128
- # to file.
129
- def initialize(header, stream, str_offset)
130
- @header = header
131
- @stream = stream
132
- @str_offset = str_offset
133
- end
160
+ # Reads the table +DT_RELR+ names, which is absent from most files.
161
+ # @return [Array<ELFTools::Relocation>] The relocations, empty without it.
162
+ def packed_relocations
163
+ start = tag_by_type(:relr)
164
+ return [] if start.nil?
134
165
 
135
- # Some dynamic have name.
136
- TYPE_WITH_NAME = [Constants::DT_NEEDED,
137
- Constants::DT_SONAME,
138
- Constants::DT_RPATH,
139
- Constants::DT_RUNPATH].freeze
140
- # Return the content of this tag records.
141
- #
142
- # For normal tags, this method just return
143
- # +header.d_val+. For tags with +header.d_val+
144
- # in meaning of string offset (e.g. DT_NEEDED), this method would
145
- # return the string it specified.
146
- # Tags with type in {TYPE_WITH_NAME} are those tags with name.
147
- # @return [Integer, String] The content this tag records.
148
- # @example
149
- # dynamic = elf.segment_by_type(:dynamic)
150
- # dynamic.tag_by_type(:init).value
151
- # #=> 4195600 # 0x400510
152
- # dynamic.tag_by_type(:needed).value
153
- # #=> 'libc.so.6'
154
- def value
155
- name || header.d_val.to_i
156
- end
166
+ offset = offset_of(start)
167
+ RelativeRelocations.new(stream, offset...(offset + tag_by_type(:relrsz).header.d_val.to_i),
168
+ elf_class: header.elf_class, endian:, machine: @machine).to_a
169
+ end
157
170
 
158
- # Is this tag has a name?
159
- #
160
- # The criteria here is if this tag's type is in {TYPE_WITH_NAME}.
161
- # @return [Boolean] Is this tag has a name.
162
- def name?
163
- TYPE_WITH_NAME.include?(header.d_tag)
171
+ # Reads one table of relocations.
172
+ # @return [Array<ELFTools::Relocation>] The relocations.
173
+ def read_relocations(start, size, rela)
174
+ klass = rela ? Structs::ELF_Rela : Structs::ELF_Rel
175
+ offset = offset_of(start)
176
+ # An entry takes what its structure takes. DT_RELAENT and DT_RELENT
177
+ # record the same number, which a file has no way of disagreeing with
178
+ # and every file here agrees with.
179
+ entsize = struct(klass).num_bytes
180
+ Array.new(size.header.d_val.to_i / entsize) do |i|
181
+ Relocation.new(read_struct(klass, offset + (i * entsize)), stream, machine: @machine)
164
182
  end
183
+ end
184
+
185
+ # A structure of the endianness and the class the file records it in.
186
+ # @param [Class] klass The structure class.
187
+ # @return [ELFTools::Structs::ELFStruct] The structure, before it is read.
188
+ def struct(klass)
189
+ struct = klass.new(endian:)
190
+ struct.elf_class = header.elf_class
191
+ struct
192
+ end
193
+
194
+ # Reads a structure the file records at a file offset.
195
+ # @param [Class] klass The structure class.
196
+ # @param [Integer] offset The file offset.
197
+ # @return [ELFTools::Structs::ELFStruct] The structure.
198
+ def read_struct(klass, offset)
199
+ struct = struct(klass)
200
+ struct.offset = offset
201
+ stream.pos = offset
202
+ struct.read(stream)
203
+ end
165
204
 
166
- # Return the name of this tag.
167
- #
168
- # Only tags with name would return a name.
169
- # Others would return +nil+.
170
- # @return [String, nil] The name.
171
- def name
172
- return nil unless name?
205
+ # The file offset the address a tag records points at.
206
+ # @param [ELFTools::Dynamic::Tag] tag The tag.
207
+ # @return [Integer] The file offset.
208
+ # @raise [ELFTools::ELFError]
209
+ # If the address is not in any loadable segment.
210
+ def offset_of(tag)
211
+ vma = tag.header.d_val.to_i
212
+ @offset_from_vma.call(vma) ||
213
+ raise(ELFError, format('Invalid %s address 0x%x', Constants::DT.mapping(@machine, tag.header.d_tag.to_i), vma))
214
+ end
215
+
216
+ # The names the tags and the symbols point at.
217
+ # @return [ELFTools::Dynamic::StringTable] The string table.
218
+ def string_table
219
+ @string_table ||= StringTable.new(stream, method(:str_offset))
220
+ end
221
+
222
+ # Get the DT_STRTAB's +d_val+ offset related to file.
223
+ # @return [Integer] The file offset.
224
+ # @raise [ELFTools::ELFError]
225
+ # If DT_STRTAB is absent, or its address is not in any loadable segment.
226
+ def str_offset
227
+ @str_offset ||= begin
228
+ strtab = tag_by_type(:strtab)
229
+ raise ELFError, 'DT_STRTAB not found' if strtab.nil?
173
230
 
174
- Util.cstring(stream, @str_offset.call + header.d_val.to_i)
231
+ offset_of(strtab)
175
232
  end
176
233
  end
177
234
  end
@@ -59,6 +59,25 @@ module ELFTools
59
59
  note.desc.unpack1('H*')
60
60
  end
61
61
 
62
+ # The dynamic tags of this file, read from the view that its type makes
63
+ # authoritative.
64
+ #
65
+ # A relocatable file is linked by its sections, so its sections answer and
66
+ # any segment it carries is disregarded, being something no linker reads.
67
+ # An executable or a shared object is loaded by its segments alone, so the
68
+ # segment answers and the section recording the same tags is metadata a
69
+ # tool may have stripped or rewritten.
70
+ # @return [ELFTools::Segments::DynamicSegment, ELFTools::Sections::DynamicSection, nil]
71
+ # The tags, +nil+ if the view that decides records none.
72
+ # @example
73
+ # elf.dynamic.tag_by_type(:needed).name
74
+ # #=> 'libc.so.6'
75
+ def dynamic
76
+ return sections_by_type(:dynamic).first if header.e_type.to_i == Constants::ET_REL
77
+
78
+ segment_by_type(:dynamic) || sections_by_type(:dynamic).first
79
+ end
80
+
62
81
  # Get machine architecture.
63
82
  #
64
83
  # Mappings of architecture can be found
@@ -67,7 +86,7 @@ module ELFTools
67
86
  # Name of architecture.
68
87
  # @example
69
88
  # elf.machine
70
- # #=> 'Advanced Micro Devices X86-64'
89
+ # #=> 'Advanced Micro Devices X86-64 processor'
71
90
  def machine
72
91
  ELFTools::Constants::EM.mapping(header.e_machine)
73
92
  end
@@ -105,7 +124,7 @@ module ELFTools
105
124
  # elf.section_by_name('no such section')
106
125
  # #=> nil
107
126
  def section_by_name(name)
108
- each_sections.find { |sec| sec.name == name }
127
+ each_section.find { |sec| sec.name == name }
109
128
  end
110
129
 
111
130
  # Iterate all sections.
@@ -119,19 +138,22 @@ module ELFTools
119
138
  # @return [Enumerator<ELFTools::Sections::Section>, Array<ELFTools::Sections::Section>]
120
139
  # As +Array#each+, if block is not given, a enumerator will be returned,
121
140
  # otherwise, the whole sections will be returned.
122
- def each_sections(&block)
123
- return enum_for(:each_sections) unless block_given?
141
+ def each_section(&block)
142
+ return enum_for(:each_section) unless block_given?
124
143
 
125
144
  Array.new(num_sections) do |i|
126
145
  section_at(i).tap(&block)
127
146
  end
128
147
  end
129
148
 
149
+ # The name this used to go by, kept so that it keeps working.
150
+ alias each_sections each_section
151
+
130
152
  # Simply use {#sections} to get all sections.
131
153
  # @return [Array<ELFTools::Sections::Section>]
132
154
  # Whole sections.
133
155
  def sections
134
- each_sections.to_a
156
+ each_section.to_a
135
157
  end
136
158
 
137
159
  # Acquire the +n+-th section, 0-based.
@@ -160,17 +182,21 @@ module ELFTools
160
182
  # elf.sections_by_type(:rela)
161
183
  # #=> [#<ELFTools::Sections::RelocationSection:0x00563cd3219970>,
162
184
  # # #<ELFTools::Sections::RelocationSection:0x00563cd3b89d70>]
163
- def sections_by_type(type, &block)
185
+ def sections_by_type(type, &)
164
186
  type = Util.to_constant(Constants::SHT, type)
165
- Util.select_by_type(each_sections, type, &block)
187
+ Util.select_by_type(each_section, type, &)
166
188
  end
167
189
 
168
- # Get the string table section.
190
+ # The section the names of the sections are recorded in, which the ELF
191
+ # header names by index.
169
192
  #
170
- # This section is acquired by using the +e_shstrndx+
171
- # in ELF header.
172
- # @return [ELFTools::Sections::StrTabSection] The desired section.
173
- def strtab_section
193
+ # It is not the section the names of the symbols are recorded in, which
194
+ # {ELFTools::Sections::SymTabSection#symstr} answers.
195
+ # @return [ELFTools::Sections::StrTabSection] The section.
196
+ # @example
197
+ # elf.section_name_table.name
198
+ # #=> '.shstrtab'
199
+ def section_name_table
174
200
  section_at(header.e_shstrndx)
175
201
  end
176
202
 
@@ -192,19 +218,22 @@ module ELFTools
192
218
  # @yieldreturn [void]
193
219
  # @return [Array<ELFTools::Segments::Segment>]
194
220
  # Whole segments will be returned.
195
- def each_segments(&block)
196
- return enum_for(:each_segments) unless block_given?
221
+ def each_segment(&block)
222
+ return enum_for(:each_segment) unless block_given?
197
223
 
198
224
  Array.new(num_segments) do |i|
199
225
  segment_at(i).tap(&block)
200
226
  end
201
227
  end
202
228
 
229
+ # The name this used to go by, kept so that it keeps working.
230
+ alias each_segments each_segment
231
+
203
232
  # Simply use {#segments} to get all segments.
204
233
  # @return [Array<ELFTools::Segments::Segment>]
205
234
  # Whole segments.
206
235
  def segments
207
- each_segments.to_a
236
+ each_segment.to_a
208
237
  end
209
238
 
210
239
  # Get the first segment with +p_type=type+.
@@ -250,7 +279,7 @@ module ELFTools
250
279
  # #=> nil # no such segment exists
251
280
  def segment_by_type(type)
252
281
  type = Util.to_constant(Constants::PT, type)
253
- each_segments.find { |seg| seg.header.p_type == type }
282
+ each_segment.find { |seg| seg.header.p_type == type }
254
283
  end
255
284
 
256
285
  # Fetch all segments with specific type.
@@ -263,9 +292,9 @@ module ELFTools
263
292
  # @yieldparam [ELFTools::Segments::Segment] segment A segment in specific type.
264
293
  # @yieldreturn [void]
265
294
  # @return [Array<ELFTools::Segments::Segment>] The target segments.
266
- def segments_by_type(type, &block)
295
+ def segments_by_type(type, &)
267
296
  type = Util.to_constant(Constants::PT, type)
268
- Util.select_by_type(each_segments, type, &block)
297
+ Util.select_by_type(each_segment, type, &)
269
298
  end
270
299
 
271
300
  # Acquire the +n+-th segment, 0-based.
@@ -285,7 +314,7 @@ module ELFTools
285
314
  # This method should work no matter ELF is a PIE or not.
286
315
  # This method refers from (actually equals to) binutils/readelf.c#offset_from_vma.
287
316
  # @param [Integer] vma The virtual address to be queried.
288
- # @return [Integer] Related file offset.
317
+ # @return [Integer?] Related file offset. +nil+ if the queried region has no content in file.
289
318
  # @example
290
319
  # elf = ELFTools::ELFFile.new(File.open('/bin/cat'))
291
320
  # elf.offset_from_vma(0x401337)
@@ -294,6 +323,25 @@ module ELFTools
294
323
  segments_by_type(:load) do |seg|
295
324
  return seg.vma_to_offset(vma) if seg.vma_in?(vma, size)
296
325
  end
326
+
327
+ nil
328
+ end
329
+
330
+ # Get virtual address given offset in file
331
+ #
332
+ # @param [Integer] offset The file offset to be queried.
333
+ # @return [Integer?] Related virtual address. Note this is raw address, not always adjusted for base address.
334
+ # +nil+ if offset is invalid.
335
+ # @example
336
+ # elf = ELFTools::ELFFile.new(File.open('/bin/cat'))
337
+ # elf.vma_from_offset(0x1337)
338
+ # #=> 0x401337
339
+ def vma_from_offset(offset, size = 1)
340
+ segments_by_type(:load) do |seg|
341
+ return seg.offset_to_vma(offset) if seg.offset_in?(offset, size)
342
+ end
343
+
344
+ nil
297
345
  end
298
346
 
299
347
  # The patch status.
@@ -363,15 +411,19 @@ module ELFTools
363
411
  shdr.read(stream)
364
412
  Sections::Section.create(shdr, stream,
365
413
  offset_from_vma: method(:offset_from_vma),
366
- strtab: method(:strtab_section),
367
- section_at: method(:section_at))
414
+ section_name_table: method(:section_name_table),
415
+ section_at: method(:section_at),
416
+ sections: method(:sections),
417
+ machine: header.e_machine.to_i)
368
418
  end
369
419
 
370
420
  def create_segment(n)
371
421
  stream.pos = header.e_phoff + n * header.e_phentsize
372
422
  phdr = Structs::ELF_Phdr[elf_class].new(endian:, offset: stream.pos)
373
423
  phdr.elf_class = elf_class
374
- Segments::Segment.create(phdr.read(stream), stream, offset_from_vma: method(:offset_from_vma))
424
+ Segments::Segment.create(phdr.read(stream), stream,
425
+ offset_from_vma: method(:offset_from_vma),
426
+ machine: header.e_machine.to_i)
375
427
  end
376
428
  end
377
429
  end
@@ -7,6 +7,9 @@ module ELFTools
7
7
  # 'lazy loading' objects.
8
8
  # Mainly used when loading sections, segments, and
9
9
  # symbols.
10
+ #
11
+ # Only {#[]} loads an element on demand, any other method
12
+ # of +Array+ loads all elements before it operates.
10
13
  class LazyArray < SimpleDelegator
11
14
  # Instantiate a {LazyArray} object.
12
15
  # @param [Integer] size
@@ -28,7 +31,8 @@ module ELFTools
28
31
  # p arr[3]
29
32
  # # 9
30
33
  def initialize(size, &block)
31
- super(Array.new(size))
34
+ @array = Array.new(size)
35
+ super(@array)
32
36
  @block = block
33
37
  end
34
38
 
@@ -36,14 +40,48 @@ module ELFTools
36
40
  #
37
41
  # Elements are lazy loaded at the first time
38
42
  # access it.
43
+ # @param [Integer] i
44
+ # The index, negative index is *not* supported.
39
45
  # @return [Object]
40
46
  # The element, returned type is the
41
47
  # return type of block given in {#initialize}.
48
+ # +nil+ if +i+ is out of bound.
42
49
  def [](i)
43
- # XXX: support negative index?
44
- return nil unless i.between?(0, __getobj__.size - 1)
50
+ return nil unless i.between?(0, size - 1)
45
51
 
46
- __getobj__[i] ||= @block.call(i)
52
+ @array[i] ||= @block.call(i)
53
+ end
54
+
55
+ # The size of this array.
56
+ #
57
+ # This method never loads any element.
58
+ # @return [Integer]
59
+ # The size given in {#initialize}.
60
+ def size
61
+ @array.size
62
+ end
63
+ alias length size
64
+
65
+ # Loads all elements.
66
+ #
67
+ # Called whenever a method other than {#[]} and {#size} is invoked, so that
68
+ # those methods operate on a fully loaded array.
69
+ # @return [Array]
70
+ # The loaded array.
71
+ # @example
72
+ # arr = LazyArray.new(3) { |i| i * i }
73
+ # p arr.map { |v| v + 1 }
74
+ # # [1, 2, 5]
75
+ def __getobj__
76
+ @array.each_index { |i| self[i] }
77
+ @array
78
+ end
79
+
80
+ private
81
+
82
+ # Queries the array without loading any element.
83
+ def respond_to_missing?(name, include_private = false)
84
+ @array.respond_to?(name, include_private)
47
85
  end
48
86
  end
49
87
  end
data/lib/elftools/note.rb CHANGED
@@ -41,8 +41,8 @@ module ELFTools
41
41
  # @return [Enumerator<ELFTools::Note::Note>, Array<ELFTools::Note::Note>]
42
42
  # If block is not given, an enumerator will be returned.
43
43
  # Otherwise, return the array of notes.
44
- def each_notes
45
- return enum_for(:each_notes) unless block_given?
44
+ def each_note
45
+ return enum_for(:each_note) unless block_given?
46
46
 
47
47
  @notes_offset_map ||= {}
48
48
  cur = note_start
@@ -61,11 +61,14 @@ module ELFTools
61
61
  notes
62
62
  end
63
63
 
64
+ # The name this used to go by, kept so that it keeps working.
65
+ alias each_notes each_note
66
+
64
67
  # Simply +#notes+ to get all notes.
65
68
  # @return [Array<ELFTools::Note::Note>]
66
69
  # Whole notes.
67
70
  def notes
68
- each_notes.to_a
71
+ each_note.to_a
69
72
  end
70
73
 
71
74
  private
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'elftools/constants'
4
+ require 'elftools/relocation'
5
+ require 'elftools/structs'
6
+
7
+ module ELFTools
8
+ # The relocations a file packs into a bitmap instead of recording one by one.
9
+ #
10
+ # Almost every relocation of a file that is loaded anywhere only adds the
11
+ # load bias to a word, which takes an entry recording an address, a type that
12
+ # is the same every time, and an addend that repeats what the word already
13
+ # holds. A file may pack them instead, as a run of addresses in ascending
14
+ # order, and spend a bit rather than an entry on each.
15
+ #
16
+ # An even entry is an address, and relocates the word there. An odd entry is
17
+ # a bitmap of the words following the last address, a set bit relocating one
18
+ # of them. Nothing records a type, because every relocation here is the one
19
+ # {ELFTools::Constants::R.relative} names.
20
+ class RelativeRelocations
21
+ # Instantiate a {ELFTools::RelativeRelocations} object.
22
+ # @param [#pos=, #read] stream Streaming object.
23
+ # @param [Range<Integer>] bytes The file offsets the table occupies.
24
+ # @param [Integer] elf_class 32 or 64, the width of an entry.
25
+ # @param [Symbol] endian +:little+ or +:big+.
26
+ # @param [Integer] machine
27
+ # The machine of the file, which decides what these relocations are of.
28
+ def initialize(stream, bytes, elf_class:, endian:, machine:)
29
+ @stream = stream
30
+ @bytes = bytes
31
+ @elf_class = elf_class
32
+ @endian = endian
33
+ @machine = machine
34
+ end
35
+
36
+ # The relocations the table packs.
37
+ # @return [Array<ELFTools::Relocation>]
38
+ # The relocations, in the ascending order the table records them.
39
+ def to_a
40
+ type = Constants::R.relative(@machine)
41
+ addresses.map do |address, from|
42
+ rel = Structs::ELF_Rel.new(endian: @endian, offset: from)
43
+ rel.elf_class = @elf_class
44
+ rel.r_offset = address
45
+ relocation = Relocation.new(rel, @stream, machine: @machine)
46
+ # Through the relocation, so that the type is laid out in +r_info+ the
47
+ # way the machine lays it out.
48
+ relocation.type = type if type
49
+ relocation
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ # How many bytes an entry takes, which is the width of an address.
56
+ # @return [Integer] The number.
57
+ def width
58
+ @elf_class / 8
59
+ end
60
+
61
+ # Every address the table relocates, and the entry it was read from.
62
+ # @return [Array<Array(Integer, Integer)>] The addresses.
63
+ def addresses
64
+ found = []
65
+ # Where a bitmap counts from, which an address moves to just past itself.
66
+ here = 0
67
+ entries.each do |entry, from|
68
+ if entry.even?
69
+ found << [entry, from]
70
+ here = entry + width
71
+ else
72
+ # The lowest bit says the entry is a bitmap rather than an address.
73
+ (1...(width * 8)).each { |bit| found << [here + ((bit - 1) * width), from] if entry[bit] == 1 }
74
+ here += ((width * 8) - 1) * width
75
+ end
76
+ end
77
+ found
78
+ end
79
+
80
+ # What the table records, and where each was read from.
81
+ # @return [Array<Array(Integer, Integer)>] The entries.
82
+ def entries
83
+ @stream.pos = @bytes.begin
84
+ format = "#{width == 8 ? 'Q' : 'L'}#{@endian == :big ? '>' : '<'}"
85
+ @stream.read(@bytes.size).to_s.unpack("#{format}*").each_with_index.map { |e, i| [e, @bytes.begin + (i * width)] }
86
+ end
87
+ end
88
+ end