elftools 2.0.0 → 2.2.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.
@@ -1,17 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'elftools/constants'
4
+ require 'elftools/structs'
5
+ require 'elftools/util'
4
6
 
5
7
  module ELFTools
6
8
  module Sections
7
9
  # Class of symbol.
8
10
  class Symbol
9
- attr_reader :header # @return [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] Section header.
10
11
  attr_reader :stream # @return [#pos=, #read] Streaming object.
11
12
 
12
13
  # Instantiate a {ELFTools::Sections::Symbol} object.
13
- # @param [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] header
14
- # The symbol header.
14
+ # @param [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym, ELFTools::Structs::Fields] header
15
+ # The symbol header, or what the file records in one. {ELFTools::Structs::Fields}
16
+ # answers what the symbol records until something asks for {#header}
17
+ # itself, which builds the structure then.
15
18
  # @param [#pos=, #read] stream The streaming object.
16
19
  # @param [ELFTools::Sections::StrTabSection, Proc] symstr
17
20
  # The symbol string section.
@@ -19,17 +22,76 @@ module ELFTools
19
22
  # access {Symbol#name}.
20
23
  # @param [Integer] machine
21
24
  # The machine of the ELF file, which a name of a value depends on.
22
- def initialize(header, stream, symstr: nil, machine: nil)
23
- @header = header
25
+ # @param [Proc] version
26
+ # Call this to get the version this symbol binds to, which only the
27
+ # symbols a file is loaded by have.
28
+ def initialize(header, stream, symstr: nil, machine: nil, version: nil)
29
+ @fields = header.is_a?(Structs::Fields) ? header : Structs::Fields.of(header)
24
30
  @stream = stream
25
31
  @symstr = symstr
26
32
  @machine = machine
33
+ @version = version
34
+ end
35
+
36
+ # The structure the file records this symbol in.
37
+ #
38
+ # One is built here for a symbol read without it, which is what assigning
39
+ # to a field of a symbol needs and what {ELFTools::ELFFile#patches}
40
+ # reports the changes of.
41
+ # @return [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] The structure.
42
+ def header
43
+ @fields.struct
27
44
  end
28
45
 
29
46
  # Return the symbol name.
30
47
  # @return [String] The name.
31
48
  def name
32
- @name ||= @symstr.call.name_at(header.st_name)
49
+ @name ||= @symstr.call.name_at(@fields[:st_name])
50
+ end
51
+
52
+ # The version this symbol binds to.
53
+ #
54
+ # Only the symbols a file is loaded by have one, and only where the file
55
+ # records the versions at all. {#name} is left as the file records it,
56
+ # without the version appended.
57
+ # @return [String, nil] The name of the version.
58
+ # @example
59
+ # elf.dynamic.symbol_by_name('printf').version
60
+ # #=> 'GLIBC_2.2.5'
61
+ def version
62
+ binding_version&.name
63
+ end
64
+
65
+ # Whether {#version} is one the symbol asks for by name rather than the
66
+ # default one of its name.
67
+ # @return [Boolean] The answer.
68
+ def version_hidden?
69
+ binding_version&.hidden? || false
70
+ end
71
+
72
+ # What this symbol is worth, which for most of them is the address of
73
+ # what they name.
74
+ #
75
+ # A symbol of a file that is not loaded anywhere records an offset into
76
+ # the section holding it instead, and one the linker is still to place,
77
+ # which {ELFTools::Constants::SHN_COMMON} marks, records the alignment it
78
+ # needs. The ABI leaves the field to the kind of symbol for that reason,
79
+ # and this answers with what is recorded either way.
80
+ # @return [Integer] The value.
81
+ # @example
82
+ # elf.section_by_name('.symtab').symbol_by_name('main').value
83
+ # #=> 4196061 # 0x4006dd
84
+ def value
85
+ @fields[:st_value]
86
+ end
87
+
88
+ # How many bytes what this symbol names takes.
89
+ # @return [Integer] The number, zero where the file records none.
90
+ # @example
91
+ # elf.section_by_name('.symtab').symbol_by_name('main').size
92
+ # #=> 142
93
+ def size
94
+ @fields[:st_size]
33
95
  end
34
96
 
35
97
  # What kind of entity this symbol refers to.
@@ -40,7 +102,16 @@ module ELFTools
40
102
  # symbol.type == ELFTools::Constants::STT_FUNC
41
103
  # #=> true
42
104
  def type
43
- header.st_info & 0xf
105
+ @fields[:st_info] & 0xf
106
+ end
107
+
108
+ # Sets what kind of entity this symbol refers to.
109
+ # @param [Integer] type The type.
110
+ # @raise [ArgumentError] If the four bits recording it cannot hold it.
111
+ # @example
112
+ # symbol.type = ELFTools::Constants::STT_FUNC
113
+ def type=(type)
114
+ header.st_info = (bind << 4) | Util.fits!(type, 4, 'Symbol type')
44
115
  end
45
116
 
46
117
  # The name of {#type}.
@@ -63,7 +134,16 @@ module ELFTools
63
134
  # symbol.bind == ELFTools::Constants::STB_GLOBAL
64
135
  # #=> true
65
136
  def bind
66
- header.st_info >> 4
137
+ @fields[:st_info] >> 4
138
+ end
139
+
140
+ # Sets how this symbol is linked against others with the same name.
141
+ # @param [Integer] bind The binding.
142
+ # @raise [ArgumentError] If the four bits recording it cannot hold it.
143
+ # @example
144
+ # symbol.bind = ELFTools::Constants::STB_WEAK
145
+ def bind=(bind)
146
+ header.st_info = (Util.fits!(bind, 4, 'Symbol binding') << 4) | type
67
147
  end
68
148
 
69
149
  # The name of {#bind}.
@@ -84,7 +164,20 @@ module ELFTools
84
164
  # symbol.visibility == ELFTools::Constants::STV_HIDDEN
85
165
  # #=> true
86
166
  def visibility
87
- header.st_other & 0x3
167
+ @fields[:st_other] & 0x3
168
+ end
169
+
170
+ # Sets how this symbol is accessed once it becomes part of an executable
171
+ # or shared object.
172
+ #
173
+ # The rest of +st_other+ is left alone, which some machines record their
174
+ # own thing in.
175
+ # @param [Integer] visibility The visibility.
176
+ # @raise [ArgumentError] If the two bits recording it cannot hold it.
177
+ # @example
178
+ # symbol.visibility = ELFTools::Constants::STV_HIDDEN
179
+ def visibility=(visibility)
180
+ header.st_other = (header.st_other.to_i & 0xfc) | Util.fits!(visibility, 2, 'Symbol visibility')
88
181
  end
89
182
 
90
183
  # The name of {#visibility}.
@@ -96,6 +189,15 @@ module ELFTools
96
189
  Constants::STV.mapping(@machine, visibility)
97
190
  end
98
191
 
192
+ # The version this symbol binds to, whatever is asked of it.
193
+ # @return [ELFTools::Dynamic::Versions::Version, nil] The version.
194
+ def binding_version
195
+ return @binding_version if defined?(@binding_version)
196
+
197
+ @binding_version = @version&.call
198
+ end
199
+ private :binding_version
200
+
99
201
  # The index of the section this symbol is defined in.
100
202
  #
101
203
  # Values in {ELFTools::Constants::SHN} have special meanings instead of
@@ -105,7 +207,7 @@ module ELFTools
105
207
  # symbol.section_index == ELFTools::Constants::SHN_UNDEF
106
208
  # #=> true # the symbol is undefined and to be resolved at runtime
107
209
  def section_index
108
- header.st_shndx.to_i
210
+ @fields[:st_shndx]
109
211
  end
110
212
  end
111
213
  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