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
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'elftools/constants'
4
+ require 'elftools/util'
5
+
6
+ module ELFTools
7
+ module Sections
8
+ # Class of symbol.
9
+ class Symbol
10
+ attr_reader :header # @return [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] Section header.
11
+ attr_reader :stream # @return [#pos=, #read] Streaming object.
12
+
13
+ # Instantiate a {ELFTools::Sections::Symbol} object.
14
+ # @param [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] header
15
+ # The symbol header.
16
+ # @param [#pos=, #read] stream The streaming object.
17
+ # @param [ELFTools::Sections::StrTabSection, Proc] symstr
18
+ # The symbol string section.
19
+ # If +Proc+ is given, it will be called at the first time
20
+ # access {Symbol#name}.
21
+ # @param [Integer] machine
22
+ # The machine of the ELF file, which a name of a value depends on.
23
+ # @param [Proc] version
24
+ # Call this to get the version this symbol binds to, which only the
25
+ # symbols a file is loaded by have.
26
+ def initialize(header, stream, symstr: nil, machine: nil, version: nil)
27
+ @header = header
28
+ @stream = stream
29
+ @symstr = symstr
30
+ @machine = machine
31
+ @version = version
32
+ end
33
+
34
+ # Return the symbol name.
35
+ # @return [String] The name.
36
+ def name
37
+ @name ||= @symstr.call.name_at(header.st_name)
38
+ end
39
+
40
+ # The version this symbol binds to.
41
+ #
42
+ # Only the symbols a file is loaded by have one, and only where the file
43
+ # records the versions at all. {#name} is left as the file records it,
44
+ # without the version appended.
45
+ # @return [String, nil] The name of the version.
46
+ # @example
47
+ # elf.dynamic.symbol_by_name('printf').version
48
+ # #=> 'GLIBC_2.2.5'
49
+ def version
50
+ binding_version&.name
51
+ end
52
+
53
+ # Whether {#version} is one the symbol asks for by name rather than the
54
+ # default one of its name.
55
+ # @return [Boolean] The answer.
56
+ def version_hidden?
57
+ binding_version&.hidden? || false
58
+ end
59
+
60
+ # What this symbol is worth, which for most of them is the address of
61
+ # what they name.
62
+ #
63
+ # A symbol of a file that is not loaded anywhere records an offset into
64
+ # the section holding it instead, and one the linker is still to place,
65
+ # which {ELFTools::Constants::SHN_COMMON} marks, records the alignment it
66
+ # needs. The ABI leaves the field to the kind of symbol for that reason,
67
+ # and this answers with what is recorded either way.
68
+ # @return [Integer] The value.
69
+ # @example
70
+ # elf.section_by_name('.symtab').symbol_by_name('main').value
71
+ # #=> 4196061 # 0x4006dd
72
+ def value
73
+ header.st_value.to_i
74
+ end
75
+
76
+ # How many bytes what this symbol names takes.
77
+ # @return [Integer] The number, zero where the file records none.
78
+ # @example
79
+ # elf.section_by_name('.symtab').symbol_by_name('main').size
80
+ # #=> 142
81
+ def size
82
+ header.st_size.to_i
83
+ end
84
+
85
+ # What kind of entity this symbol refers to.
86
+ #
87
+ # The available types are listed in {ELFTools::Constants::STT}.
88
+ # @return [Integer] The type.
89
+ # @example
90
+ # symbol.type == ELFTools::Constants::STT_FUNC
91
+ # #=> true
92
+ def type
93
+ header.st_info & 0xf
94
+ end
95
+
96
+ # Sets what kind of entity this symbol refers to.
97
+ # @param [Integer] type The type.
98
+ # @raise [ArgumentError] If the four bits recording it cannot hold it.
99
+ # @example
100
+ # symbol.type = ELFTools::Constants::STT_FUNC
101
+ def type=(type)
102
+ header.st_info = (bind << 4) | Util.fits!(type, 4, 'Symbol type')
103
+ end
104
+
105
+ # The name of {#type}.
106
+ #
107
+ # A machine names types of its own, so the name is only known when the
108
+ # machine of the file is.
109
+ # @return [String] The name.
110
+ # @example
111
+ # symbol.type_name
112
+ # #=> 'STT_FUNC'
113
+ def type_name
114
+ Constants::STT.mapping(@machine, type)
115
+ end
116
+
117
+ # How this symbol is linked against others with the same name.
118
+ #
119
+ # The available bindings are listed in {ELFTools::Constants::STB}.
120
+ # @return [Integer] The binding.
121
+ # @example
122
+ # symbol.bind == ELFTools::Constants::STB_GLOBAL
123
+ # #=> true
124
+ def bind
125
+ header.st_info >> 4
126
+ end
127
+
128
+ # Sets how this symbol is linked against others with the same name.
129
+ # @param [Integer] bind The binding.
130
+ # @raise [ArgumentError] If the four bits recording it cannot hold it.
131
+ # @example
132
+ # symbol.bind = ELFTools::Constants::STB_WEAK
133
+ def bind=(bind)
134
+ header.st_info = (Util.fits!(bind, 4, 'Symbol binding') << 4) | type
135
+ end
136
+
137
+ # The name of {#bind}.
138
+ # @return [String] The name.
139
+ # @example
140
+ # symbol.bind_name
141
+ # #=> 'STB_GLOBAL'
142
+ def bind_name
143
+ Constants::STB.mapping(@machine, bind)
144
+ end
145
+
146
+ # How this symbol is accessed once it becomes part of an executable or
147
+ # shared object.
148
+ #
149
+ # The available visibilities are listed in {ELFTools::Constants::STV}.
150
+ # @return [Integer] The visibility.
151
+ # @example
152
+ # symbol.visibility == ELFTools::Constants::STV_HIDDEN
153
+ # #=> true
154
+ def visibility
155
+ header.st_other & 0x3
156
+ end
157
+
158
+ # Sets how this symbol is accessed once it becomes part of an executable
159
+ # or shared object.
160
+ #
161
+ # The rest of +st_other+ is left alone, which some machines record their
162
+ # own thing in.
163
+ # @param [Integer] visibility The visibility.
164
+ # @raise [ArgumentError] If the two bits recording it cannot hold it.
165
+ # @example
166
+ # symbol.visibility = ELFTools::Constants::STV_HIDDEN
167
+ def visibility=(visibility)
168
+ header.st_other = (header.st_other.to_i & 0xfc) | Util.fits!(visibility, 2, 'Symbol visibility')
169
+ end
170
+
171
+ # The name of {#visibility}.
172
+ # @return [String] The name.
173
+ # @example
174
+ # symbol.visibility_name
175
+ # #=> 'STV_DEFAULT'
176
+ def visibility_name
177
+ Constants::STV.mapping(@machine, visibility)
178
+ end
179
+
180
+ # The version this symbol binds to, whatever is asked of it.
181
+ # @return [ELFTools::Dynamic::Versions::Version, nil] The version.
182
+ def binding_version
183
+ return @binding_version if defined?(@binding_version)
184
+
185
+ @binding_version = @version&.call
186
+ end
187
+ private :binding_version
188
+
189
+ # The index of the section this symbol is defined in.
190
+ #
191
+ # Values in {ELFTools::Constants::SHN} have special meanings instead of
192
+ # being an index.
193
+ # @return [Integer] The section index.
194
+ # @example
195
+ # symbol.section_index == ELFTools::Constants::SHN_UNDEF
196
+ # #=> true # the symbol is undefined and to be resolved at runtime
197
+ def section_index
198
+ header.st_shndx.to_i
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'elftools/sections/section'
4
+ require 'elftools/version_tables'
5
+
6
+ module ELFTools
7
+ module Sections
8
+ # Class of the section recording the versions a file defines for what it
9
+ # exports.
10
+ #
11
+ # This section is usually named .gnu.version_d, and records the very
12
+ # versions the +DT_VERDEF+ tag points at.
13
+ class VersionDefinitionSection < Section
14
+ # Instantiate a {VersionDefinitionSection} object.
15
+ # @param [ELFTools::Structs::ELF_Shdr] header
16
+ # See {Section#initialize} for more information.
17
+ # @param [#pos=, #read] stream
18
+ # See {Section#initialize} for more information.
19
+ # @param [Proc] section_at
20
+ # The method for fetching other sections by index, which is where the
21
+ # names are recorded.
22
+ def initialize(header, stream, section_at: nil, **_kwargs)
23
+ @section_at = section_at
24
+ super
25
+ end
26
+
27
+ # The versions this file defines for what it exports.
28
+ # @return [Array<ELFTools::VersionTables::Definition>]
29
+ # The definitions, in the order the section records them.
30
+ # @example
31
+ # section.definitions.map(&:name).first(3)
32
+ # #=> ['libc.so.6', 'GLIBC_2.2.5', 'GLIBC_2.2.6']
33
+ def definitions
34
+ @definitions ||= VersionTables.new(stream, @section_at.call(header.sh_link),
35
+ endian: header.class.self_endian)
36
+ .definitions(header.sh_offset.to_i, header.sh_info.to_i)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'elftools/sections/section'
4
+ require 'elftools/version_tables'
5
+
6
+ module ELFTools
7
+ module Sections
8
+ # Class of the section recording the versions a file needs of the files it
9
+ # is loaded with.
10
+ #
11
+ # This section is usually named .gnu.version_r, and records the very
12
+ # versions the +DT_VERNEED+ tag points at.
13
+ class VersionNeedSection < Section
14
+ # Instantiate a {VersionNeedSection} object.
15
+ # @param [ELFTools::Structs::ELF_Shdr] header
16
+ # See {Section#initialize} for more information.
17
+ # @param [#pos=, #read] stream
18
+ # See {Section#initialize} for more information.
19
+ # @param [Proc] section_at
20
+ # The method for fetching other sections by index, which is where the
21
+ # names are recorded.
22
+ def initialize(header, stream, section_at: nil, **_kwargs)
23
+ @section_at = section_at
24
+ super
25
+ end
26
+
27
+ # The versions this file needs of the files it is loaded with.
28
+ # @return [Array<ELFTools::VersionTables::Requirement>]
29
+ # The requirements, in the order the section records them.
30
+ # @example
31
+ # section.requirements.map { |need| [need.file, need.versions.map(&:name)] }
32
+ # #=> [['libc.so.6', ['GLIBC_2.4', 'GLIBC_2.2.5']]]
33
+ def requirements
34
+ @requirements ||= VersionTables.new(stream, @section_at.call(header.sh_link),
35
+ endian: header.class.self_endian)
36
+ .requirements(header.sh_offset.to_i, header.sh_info.to_i)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'elftools/sections/section'
4
+
5
+ module ELFTools
6
+ module Sections
7
+ # Class of the section recording which version each symbol binds to.
8
+ #
9
+ # This section is usually named .gnu.version, and holds an index per symbol
10
+ # of the table its +sh_link+ names, the very indices the +DT_VERSYM+ tag
11
+ # points at.
12
+ class VersionSection < Section
13
+ # How many symbols the section records a version for.
14
+ # @return [Integer] The number.
15
+ def num_versions
16
+ header.sh_size.to_i / entry_size
17
+ end
18
+
19
+ # What the +n+-th symbol records as its version, which is an index into
20
+ # the versions a file needs or defines, with the highest bit marking a
21
+ # version the symbol asks for by name rather than the default one.
22
+ # @param [Integer] n The symbol index.
23
+ # @return [Integer, nil] The index, +nil+ if the section records none for it.
24
+ # @example
25
+ # section.version_at(1)
26
+ # #=> 2
27
+ def version_at(n)
28
+ return if n.negative? || n >= num_versions
29
+
30
+ stream.pos = header.sh_offset.to_i + (n * entry_size)
31
+ stream.read(entry_size).unpack1(header.class.self_endian == :big ? 'S>' : 'S<')
32
+ end
33
+
34
+ private
35
+
36
+ # What an entry takes, which the section records and the format fixes at
37
+ # two bytes either way.
38
+ # @return [Integer] The number of bytes.
39
+ def entry_size
40
+ 2
41
+ end
42
+ end
43
+ end
44
+ end
@@ -10,6 +10,7 @@ module ELFTools
10
10
  # This class knows how to get the list of dynamic tags.
11
11
  class DynamicSegment < Segment
12
12
  include Dynamic # rock!
13
+
13
14
  # Get the start address of tags.
14
15
  # @return [Integer] Start address of tags.
15
16
  def tag_start
@@ -49,6 +49,29 @@ module ELFTools
49
49
  mem_head + mem_size
50
50
  end
51
51
 
52
+ # Returns the lowest virtual address backed by this segment's file content.
53
+ #
54
+ # A segment is mapped from an aligned boundary, so the addresses preceding
55
+ # {#mem_head} within the same alignment unit are backed by this segment as
56
+ # well. The bound is clamped to the address of the first byte of the file,
57
+ # so that a malformed ELF, whose +p_offset+ and +p_vaddr+ are not congruent
58
+ # modulo +p_align+, cannot be converted into a negative file offset.
59
+ # @return [Integer]
60
+ # The vma.
61
+ def mapped_head
62
+ [header.p_vaddr.to_i & -header.p_align.to_i, mem_head - file_head].max
63
+ end
64
+
65
+ # Returns the end virtual address backed by this segment's file content.
66
+ #
67
+ # Addresses in between this and {#mem_tail} occupy memory but have no
68
+ # content in file, therefore cannot be converted into a file offset.
69
+ # @return [Integer]
70
+ # The vma.
71
+ def mapped_tail
72
+ mem_head + size
73
+ end
74
+
52
75
  # Query if the given file offset located in this segment.
53
76
  # @param [Integer] offset
54
77
  # File offset.
@@ -56,27 +79,32 @@ module ELFTools
56
79
  # Size.
57
80
  # @return [Boolean]
58
81
  def offset_in?(offset, size = 0)
59
- file_head <= offset && offset + size < file_tail
82
+ file_head <= offset && offset + size <= file_tail
60
83
  end
61
84
 
62
85
  # Convert file offset into virtual memory address.
86
+ #
87
+ # The conversion is a shift by a constant, so it holds no matter +p_offset+
88
+ # is aligned with +p_vaddr+ or not.
63
89
  # @param [Integer] offset
64
90
  # File offset.
65
91
  # @return [Integer]
66
92
  def offset_to_vma(offset)
67
- # XXX: What if file_head is not aligned with p_vaddr (which is invalid according to ELF spec)?
68
93
  offset - file_head + header.p_vaddr
69
94
  end
70
95
 
71
- # Query if the given virtual memory address located in this segment.
96
+ # Query if the given virtual memory address is backed by this segment's
97
+ # file content.
98
+ #
99
+ # Only addresses in between {#mapped_head} and {#mapped_tail} can be
100
+ # converted into a file offset, see {#vma_to_offset}.
72
101
  # @param [Integer] vma
73
102
  # Virtual memory address.
74
103
  # @param [Integer] size
75
104
  # Size.
76
105
  # @return [Boolean]
77
106
  def vma_in?(vma, size = 0)
78
- vma >= (header.p_vaddr & -header.p_align) &&
79
- vma + size <= mem_tail
107
+ vma >= mapped_head && vma + size <= mapped_tail
80
108
  end
81
109
 
82
110
  # Convert virtual memory address into file offset.
@@ -14,10 +14,14 @@ module ELFTools
14
14
  # Streaming object.
15
15
  # @param [Method] offset_from_vma
16
16
  # The method to get offset of file, given virtual memory address.
17
- def initialize(header, stream, offset_from_vma: nil)
17
+ # @param [Integer] machine
18
+ # The machine of the ELF file, which decides what the entries a
19
+ # segment points at mean. This should be +e_machine+ of the ELF header.
20
+ def initialize(header, stream, offset_from_vma: nil, machine: nil, **_kwargs)
18
21
  @header = header
19
22
  @stream = stream
20
23
  @offset_from_vma = offset_from_vma
24
+ @machine = machine
21
25
  end
22
26
 
23
27
  # Return +header.p_type+ in a simpler way.
@@ -37,19 +41,19 @@ module ELFTools
37
41
  # Is this segment readable?
38
42
  # @return [Boolean] True or false.
39
43
  def readable?
40
- (header.p_flags & 4) == 4
44
+ header.p_flags.allbits?(4)
41
45
  end
42
46
 
43
47
  # Is this segment writable?
44
48
  # @return [Boolean] True or false.
45
49
  def writable?
46
- (header.p_flags & 2) == 2
50
+ header.p_flags.allbits?(2)
47
51
  end
48
52
 
49
53
  # Is this segment executable?
50
54
  # @return [Boolean] True or false.
51
55
  def executable?
52
- (header.p_flags & 1) == 1
56
+ header.p_flags.allbits?(1)
53
57
  end
54
58
  end
55
59
  end
@@ -18,37 +18,88 @@ module ELFTools
18
18
  attr_accessor :elf_class # @return [Integer] 32 or 64.
19
19
  attr_accessor :offset # @return [Integer] The file offset of this header.
20
20
 
21
- # Records which fields have been patched.
22
- # @return [Hash{Integer => Integer}] Patches.
21
+ # Reads the structure, remembering the bytes it was read from.
22
+ #
23
+ # They are taken back off the stream. A stream that cannot be seeked is
24
+ # serialized instead.
25
+ # @param [#pos=, #read] io The streaming object.
26
+ # @return [ELFTools::Structs::ELFStruct] Itself.
27
+ def read(io)
28
+ start = io.pos if io.respond_to?(:pos)
29
+ super.tap { @source = start.nil? ? to_binary_s : bytes_read(io, start) }
30
+ end
31
+
32
+ # Which bytes of this structure have been changed since it was read.
33
+ #
34
+ # Every field answers alike, however deeply it is nested, because what is
35
+ # compared is the bytes the structure occupies rather than the
36
+ # assignments that were made to it. A field assigned the value it
37
+ # already held leaves nothing behind.
38
+ # @return [Hash{Integer => String}]
39
+ # Where each run of changed bytes starts, as an offset into the
40
+ # structure, and the bytes it is to be replaced with.
41
+ # @example
42
+ # header.e_ident.ei_abiversion = 41
43
+ # header.patches
44
+ # #=> { 8 => "\x29" }
23
45
  def patches
24
- @patches ||= {}
46
+ return {} if @source.nil?
47
+
48
+ changed_runs(@source, to_binary_s)
25
49
  end
26
50
 
27
51
  # BinData hash(Snapshot) that behaves like HashWithIndifferentAccess
28
52
  alias to_h snapshot
29
53
 
54
+ private
55
+
56
+ # The bytes a read has just taken from a stream, leaving it where the
57
+ # read left it.
58
+ # @param [#pos=, #read] io The streaming object.
59
+ # @param [Integer] start Where the read began.
60
+ # @return [String] The bytes.
61
+ def bytes_read(io, start)
62
+ here = io.pos
63
+ io.pos = start
64
+ io.read(here - start).tap { io.pos = here }
65
+ end
66
+
67
+ # Where two strings of bytes differ, as the runs of bytes that differ.
68
+ #
69
+ # Only as far as +before+ reaches, so that a patch never covers more of
70
+ # the file than the structure it came from.
71
+ # @param [String] before The bytes as they were.
72
+ # @param [String] after The bytes as they are.
73
+ # @return [Hash{Integer => String}] Where each run starts, and its bytes.
74
+ def changed_runs(before, after)
75
+ runs = {}
76
+ start = nil
77
+ (0..before.bytesize).each do |i|
78
+ if i < before.bytesize && before.getbyte(i) != after.getbyte(i)
79
+ start ||= i
80
+ elsif start
81
+ runs[start] = after.byteslice(start, i - start)
82
+ start = nil
83
+ end
84
+ end
85
+ runs
86
+ end
87
+
30
88
  class << self
31
89
  # Hooks the constructor.
32
90
  #
33
91
  # +BinData::Record+ doesn't allow us to override +#initialize+, so we hack +new+ here.
92
+ #
93
+ # Keyword arguments have to be taken as a trailing +Hash+ instead of +**kwargs+: bindata
94
+ # defines +new+ on each record class taking +*args+ only, then re-dispatches it to the
95
+ # endian-specific subclass this method actually runs on, which collapses the caller's
96
+ # keywords into a positional +Hash+ on the way. See +override_new_in_class+ in
97
+ # https://github.com/dmendel/bindata/blob/master/lib/bindata/dsl.rb, which is the same in
98
+ # 2.5.1, 3.0.0, and master.
34
99
  def new(*args)
35
- # XXX: The better implementation is +new(*args, **kwargs)+, but we can't do this unless bindata changed
36
- # lib/bindata/dsl.rb#override_new_in_class to invoke +new+ with both +args+ and +kwargs+.
37
100
  kwargs = args.last.is_a?(Hash) ? args.last : {}
38
101
  offset = kwargs.delete(:offset)
39
- super.tap do |obj|
40
- obj.offset = offset
41
- obj.field_names.each do |f|
42
- m = "#{f}=".to_sym
43
- old_method = obj.singleton_method(m)
44
- obj.singleton_class.send(:undef_method, m)
45
- obj.define_singleton_method(m) do |val|
46
- org = obj.send(f)
47
- obj.patches[org.abs_offset] = ELFStruct.pack(val, org.num_bytes)
48
- old_method.call(val)
49
- end
50
- end
51
- end
102
+ super.tap { |obj| obj.offset = offset }
52
103
  end
53
104
 
54
105
  # Gets the endianness of current class.
@@ -58,6 +109,10 @@ module ELFTools
58
109
  end
59
110
 
60
111
  # Packs an integer to string.
112
+ #
113
+ # @deprecated
114
+ # Nothing here packs a patch by hand anymore, see {ELFStruct#patches}.
115
+ # This is kept for anyone who called it and goes in the next major.
61
116
  # @param [Integer] val
62
117
  # @param [Integer] bytes
63
118
  # @return [String]
@@ -179,6 +234,65 @@ module ELFTools
179
234
  64 => ELF64_sym
180
235
  }.freeze
181
236
 
237
+ # Header of the symbol hash table +DT_HASH+ points at.
238
+ class ELF_Hash < ELFStruct
239
+ endian :big_and_little
240
+ uint32 :nbucket # Number of buckets
241
+ uint32 :nchain # Number of chains, which is how many symbols there are
242
+ end
243
+
244
+ # Header of the symbol hash table +DT_GNU_HASH+ points at.
245
+ class ELF_GnuHash < ELFStruct
246
+ endian :big_and_little
247
+ uint32 :nbuckets # Number of buckets
248
+ uint32 :symndx # The first symbol index the table indexes
249
+ uint32 :maskwords # Number of words the bloom filter takes
250
+ uint32 :shift2 # The second shift the bloom filter is built with
251
+ end
252
+
253
+ # An entry of the table of versions a file needs of another, which the
254
+ # +vn_next+ of the one before it points at.
255
+ class ELF_Verneed < ELFStruct
256
+ endian :big_and_little
257
+ uint16 :vn_version # Revision of this structure
258
+ uint16 :vn_cnt # How many versions of the file are needed
259
+ uint32 :vn_file # Name of the file, as an offset into the string table
260
+ uint32 :vn_aux # Where the versions start, as an offset from here
261
+ uint32 :vn_next # Where the next entry is, as an offset from here
262
+ end
263
+
264
+ # A version an {ELF_Verneed} needs, which the +vna_next+ of the one before
265
+ # it points at.
266
+ class ELF_Vernaux < ELFStruct
267
+ endian :big_and_little
268
+ uint32 :vna_hash # Hash of the name
269
+ uint16 :vna_flags # Flags
270
+ uint16 :vna_other # The index the symbols name this version with
271
+ uint32 :vna_name # The name, as an offset into the string table
272
+ uint32 :vna_next # Where the next version is, as an offset from here
273
+ end
274
+
275
+ # An entry of the table of versions a file defines, which the +vd_next+ of
276
+ # the one before it points at.
277
+ class ELF_Verdef < ELFStruct
278
+ endian :big_and_little
279
+ uint16 :vd_version # Revision of this structure
280
+ uint16 :vd_flags # Flags
281
+ uint16 :vd_ndx # The index the symbols name this version with
282
+ uint16 :vd_cnt # How many names follow, the version and its ancestors
283
+ uint32 :vd_hash # Hash of the name
284
+ uint32 :vd_aux # Where the names start, as an offset from here
285
+ uint32 :vd_next # Where the next entry is, as an offset from here
286
+ end
287
+
288
+ # A name an {ELF_Verdef} records, its own or an ancestor's, which the
289
+ # +vda_next+ of the one before it points at.
290
+ class ELF_Verdaux < ELFStruct
291
+ endian :big_and_little
292
+ uint32 :vda_name # The name, as an offset into the string table
293
+ uint32 :vda_next # Where the next name is, as an offset from here
294
+ end
295
+
182
296
  # Note header.
183
297
  class ELF_Nhdr < ELFStruct
184
298
  endian :big_and_little