ony-ruby-elf 1.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.
data/lib/elf/gnu.rb ADDED
@@ -0,0 +1,174 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Simple ELF parser for Ruby
3
+ #
4
+ # Copyright © 2007-2010 Diego Elio Pettenò <flameeyes@flameeyes.eu>
5
+ # Portions inspired by elf.py
6
+ # Copyright © 2002 Netgraft Corporation
7
+ # Portions inspired by elf.h
8
+ # Copyright © 1995-2006 Free Software Foundation, Inc.
9
+ #
10
+ # This program is free software; you can redistribute it and/or modify
11
+ # it under the terms of the GNU General Public License as published by
12
+ # the Free Software Foundation; either version 2 of the License, or
13
+ # (at your option) any later version.
14
+ #
15
+ # This program is distributed in the hope that it will be useful,
16
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ # GNU General Public License for more details.
19
+ #
20
+ # You should have received a copy of the GNU General Public License
21
+ # along with this generator; if not, write to the Free Software
22
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23
+
24
+ module Elf
25
+ # GNU extensions to the ELF formats.
26
+ # 'nuff said.
27
+ module GNU
28
+ class SymbolVersionUnknown < Exception
29
+ def initialize(val)
30
+ super("GNU Symbol versioning version #{val} unknown")
31
+ end
32
+ end
33
+
34
+ class SymbolVersionTable < Section
35
+ def load_internal
36
+ @versions = []
37
+ for i in 1..(@numentries)
38
+ @versions << @file.read_versym
39
+ end
40
+ end
41
+
42
+ def count
43
+ load unless @versions
44
+
45
+ @versions.size
46
+ end
47
+
48
+ def [](idx)
49
+ load unless @versions
50
+
51
+ @versions[idx]
52
+ end
53
+ end
54
+
55
+ class SymbolVersionDef < Section
56
+ FlagBase = 0x0001
57
+ FlagWeak = 0x0002
58
+
59
+ def load_internal
60
+ link.load # do this now for safety
61
+
62
+ @defined_versions = {}
63
+ entry_off = @offset
64
+ loop do
65
+ @file.seek(entry_off)
66
+
67
+ entry = {}
68
+ version = @file.read_half
69
+ raise SymbolVersionUnknown.new(version) if version != 1
70
+ entry[:flags] = @file.read_half
71
+ ndx = @file.read_half
72
+ aux_count = @file.read_half
73
+ entry[:hash] = @file.read_word
74
+ name_off = entry_off + @file.read_word
75
+ next_entry_off = @file.read_word
76
+
77
+ entry[:names] = []
78
+ for i in 1..aux_count
79
+ @file.seek(name_off)
80
+ entry[:names] << link[@file.read_word]
81
+ next_name_off = @file.read_word
82
+ break unless next_name_off != 0
83
+ name_off += next_name_off
84
+ end
85
+
86
+ @defined_versions[ndx] = entry
87
+
88
+ break unless next_entry_off != 0
89
+
90
+ entry_off += next_entry_off
91
+ end
92
+ end
93
+
94
+ def count
95
+ load unless @defined_versions
96
+
97
+ @defined_versions.size
98
+ end
99
+
100
+ def [](idx)
101
+ load unless @defined_versions
102
+
103
+ @defined_versions[idx]
104
+ end
105
+
106
+ # Allow to iterate over all the versions defined in the ELF
107
+ # file.
108
+ def each_version(&block)
109
+ load unless @defined_versions
110
+
111
+ @defined_versions.each_value(&block)
112
+ end
113
+ end
114
+
115
+ class SymbolVersionNeed < Section
116
+ def load_internal
117
+ link.load # do this now for safety
118
+
119
+ @needed_versions = {}
120
+ loop do
121
+ version = @file.read_half
122
+ raise SymbolVersionUnknown.new(version) if version != 1
123
+ aux_count = @file.read_half
124
+ file = link[@file.read_word]
125
+ # discard the next, it's used for non-sequential reading.
126
+ @file.read_word
127
+ # This one is interesting only when we need to stop the
128
+ # read loop.
129
+ more = @file.read_word != 0
130
+
131
+ for i in 1..aux_count
132
+ entry = {}
133
+
134
+ entry[:file] = file
135
+ entry[:hash] = @file.read_word
136
+ entry[:flags] = @file.read_half
137
+
138
+ tmp = @file.read_half # damn Drepper and overloaded values
139
+ entry[:private] = !(tmp & (1 << 15) == 0)
140
+ index = tmp & ~(1 << 15)
141
+
142
+ entry[:name] = link[@file.read_word]
143
+
144
+ @needed_versions[index] = entry
145
+
146
+ break unless @file.read_word != 0
147
+ end
148
+
149
+ break unless more
150
+ end
151
+ end
152
+
153
+ def count
154
+ load unless @needed_versions
155
+
156
+ @needed_versions.size
157
+ end
158
+
159
+ def [](idx)
160
+ load unless @needed_versions
161
+
162
+ @needed_versions[idx]
163
+ end
164
+
165
+ # Allow to iterate over all the versions needed in the ELF
166
+ # file.
167
+ def each_version(&block)
168
+ load unless @needed_versions
169
+
170
+ @needed_versions.each_value(&block)
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,348 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Simple ELF parser for Ruby
3
+ #
4
+ # Copyright © 2007-2010 Diego Elio Pettenò <flameeyes@flameeyes.eu>
5
+ # Portions inspired by elf.py
6
+ # Copyright © 2002 Netgraft Corporation
7
+ # Portions inspired by elf.h
8
+ # Copyright © 1995-2006 Free Software Foundation, Inc.
9
+ #
10
+ # This program is free software; you can redistribute it and/or modify
11
+ # it under the terms of the GNU General Public License as published by
12
+ # the Free Software Foundation; either version 2 of the License, or
13
+ # (at your option) any later version.
14
+ #
15
+ # This program is distributed in the hope that it will be useful,
16
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ # GNU General Public License for more details.
19
+ #
20
+ # You should have received a copy of the GNU General Public License
21
+ # along with this generator; if not, write to the Free Software
22
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23
+
24
+ require 'set'
25
+
26
+ module Elf
27
+ class Section
28
+ # Reserved sections' indexes
29
+ Undef = nil # Would be '0', but this fits enough
30
+
31
+ Reserved = 0xff00..0xffff
32
+ ProcSpecific = 0xff00..0xff1f
33
+ OsSpecific = 0xff20..0xff3f
34
+
35
+ # Sun-specific range, subset of OS-specific range
36
+ SunW = 0xff3f..0xff3f
37
+
38
+ SunWIgnore = 0xff3f
39
+ Abs = 0xfff1 # Absolute symbols
40
+ Common = 0xfff2 # Common symbols
41
+ XIndex = 0xffff
42
+
43
+ class UnknownType < Exception
44
+ def initialize(type_id, section_name)
45
+ @type_id = type_id
46
+ @section_name = section_name
47
+ super(sprintf("Unknown section type 0x%08x for section #{@section_name}", @type_id))
48
+ end
49
+
50
+ attr_reader :type_id, :section_name
51
+ end
52
+
53
+ # Create a new Section object reading the section's header from
54
+ # the file.
55
+ # This function assumes that the elf file is aligned ad the
56
+ # start of a section header, and returns the file moved at the
57
+ # start of the next header.
58
+ def Section.read(elf, index, sectdata)
59
+ begin
60
+ if Type::ProcSpecific.include?(sectdata[:type_id])
61
+ begin
62
+ case elf.machine
63
+ when Elf::Machine::ARM
64
+ type = Type::ProcARM[sectdata[:type_id]]
65
+ else
66
+ type = Type[sectdata[:type_id]]
67
+ end
68
+ rescue Value::OutOfBound
69
+ type = Type[sectdata[:type_id]]
70
+ end
71
+ elsif Type::OsSpecific.include?(sectdata[:type_id])
72
+ begin
73
+ # Unfortunately, even though OS ABIs are well-defined for both
74
+ # GNU/Linux and Solaris, they don't seem to get used at all.
75
+ #
76
+ # For this reason, instead of basing ourselves on (just) the
77
+ # OS ABI, the name of the section is used to identify the type
78
+ # of section to use
79
+
80
+ # Don't set the name if there is no string table loaded
81
+ name = elf.string_table ? elf.string_table[sectdata[:name_idx]] : ""
82
+ if elf.abi == Elf::OsAbi::Solaris or
83
+ name =~ /^\.SUNW_/
84
+ type = Type::SunW[sectdata[:type_id]]
85
+ elsif elf.abi == Elf::OsAbi::Linux or
86
+ name =~ /^\.gnu\./
87
+ type = Type::GNU[sectdata[:type_id]]
88
+ else
89
+ type = Type[sectdata[:type_id]]
90
+ end
91
+ rescue Value::OutOfBound
92
+ type = Type[sectdata[:type_id]]
93
+ end
94
+ else
95
+ type = Type[sectdata[:type_id]]
96
+ end
97
+ type = nil if Type.is_a? Value::Unknown
98
+ rescue Value::OutOfBound
99
+ type = nil
100
+ end
101
+
102
+ raise UnknownType.new(sectdata[:type_id],
103
+ elf.string_table ? elf.string_table[sectdata[:name_idx]] : sectdata[:name_idx]
104
+ ) if type.nil?
105
+
106
+ if Type::Class[type]
107
+ return Type::Class[type].new(elf, index, sectdata, type)
108
+ else
109
+ return Section.new(elf, index, sectdata, type)
110
+ end
111
+ end
112
+
113
+ attr_reader :file, :index, :type, :addr, :offset, :size
114
+ attr_reader :addralign, :entsize, :info
115
+
116
+ def initialize(elf, index, sectdata, type)
117
+ @file = elf
118
+ @index = index
119
+ @type = type
120
+ @name = sectdata[:name_idx]
121
+ @flags_val = sectdata[:flags_val]
122
+ @addr = sectdata[:addr]
123
+ @offset = sectdata[:offset]
124
+ @size = sectdata[:size]
125
+ @link = sectdata[:link]
126
+ @info = sectdata[:info]
127
+ @addralign = sectdata[:addralign]
128
+ @entsize = sectdata[:entsize]
129
+
130
+ @numentries = @size/@entsize unless @entsize == 0
131
+ end
132
+
133
+ def ==(other)
134
+ # For the sake of retrocompatibility and code readability,
135
+ # accept these two types as a valid (albeit false) comparison.
136
+ return false if other.nil? or other.is_a? Integer
137
+
138
+ raise TypeError.new("wrong argument type #{other.class} (expected Elf::Section)") unless
139
+ other.is_a? Section
140
+
141
+ other.file == @file and other.addr == @addr
142
+ end
143
+
144
+ def name
145
+ # We didn't read the name in form of string yet;
146
+ # Check if the file has loaded a string table yet
147
+ if @name.is_a? Integer and @file.string_table
148
+ @name = @file.string_table[@name]
149
+ end
150
+
151
+ @name
152
+ end
153
+
154
+ # Alias to_s to name so that using this in a string will report the name
155
+ alias :to_s :name
156
+ alias :to_i :index
157
+
158
+ def link
159
+ # We didn't get the linked section header yet
160
+ if @link.is_a? Integer
161
+ @link = @file[@link]
162
+ end
163
+
164
+ @link
165
+ end
166
+
167
+ def load
168
+ oldpos = @file.tell
169
+ @file.seek(@offset, IO::SEEK_SET)
170
+
171
+ load_internal
172
+
173
+ @file.seek(oldpos, IO::SEEK_SET)
174
+ end
175
+
176
+ def summary
177
+ $stdout.puts "#{name}\t\t#{@type}\t#{@flags_val}\t#{@addr}\t#{@offset}\t#{@size}\t#{@link}\t#{@info}\t#{@addralign}\t#{@entsize}"
178
+ end
179
+
180
+ class Flags < Value
181
+ fill(
182
+ 0x00000001 => [ :Write, 'Writable' ],
183
+ 0x00000002 => [ :Alloc, 'Allocated' ],
184
+ 0x00000004 => [ :ExecInstr, 'Executable' ],
185
+ 0x00000010 => [ :Merge, 'Mergeable' ],
186
+ 0x00000020 => [ :Strings, 'Contains null-terminated strings' ],
187
+ 0x00000040 => [ :InfoLink, 'sh_info contains SHT index' ],
188
+ 0x00000080 => [ :LinkOrder, 'Preserve order after combining' ],
189
+ 0x00000100 => [ :OsNonConforming, 'Non-standard OS specific handling required' ],
190
+ 0x00000200 => [ :Group, 'Section is member of a group' ],
191
+ 0x00000400 => [ :TLS, 'Section hold thread-local data' ],
192
+ 0x40000000 => [ :Ordered, 'Special ordering requirement' ],
193
+ 0x80000000 => [ :Exclude, 'Section is excluded unless referenced or allocated' ]
194
+ )
195
+
196
+ # OS-specific flags mask
197
+ MaskOS = 0x0ff00000
198
+ # Processor-specific flags mask
199
+ MaskProc = 0xf0000000
200
+ end
201
+
202
+ # Return a set of flag items, easier to check for single elements.
203
+ def flags
204
+ return @flags if @flags
205
+
206
+ @flags = Set.new
207
+ Flags.each do |flag|
208
+ flags.add(flag) if (@flags_val & flag.val) == flag.val
209
+ end
210
+
211
+ @flags
212
+ end
213
+
214
+ FlagsToChars = [
215
+ [Flags::Write, "W"],
216
+ [Flags::Alloc, "A"],
217
+ [Flags::ExecInstr, "X"],
218
+ [Flags::Merge, "M"],
219
+ [Flags::Strings, "S"],
220
+ [Flags::InfoLink, "I"],
221
+ [Flags::LinkOrder, "L"],
222
+ [Flags::Group, "G"],
223
+ [Flags::TLS, "T" ],
224
+ [Flags::Exclude, "E"],
225
+ ]
226
+
227
+ def flags_s
228
+ return @flags_s if @flags_s
229
+
230
+ @flags_s = FlagsToChars.collect { |flag, char|
231
+ flags.include?(flag) ? char : ""
232
+ }.join
233
+
234
+ @flags_s << 'o' if (@flags_val & Flags::MaskOS) != 0
235
+ @flags_s << 'p' if (@flags_val & Flags::MaskProc) != 0
236
+
237
+ return @flags_s
238
+ end
239
+
240
+ def flags_i
241
+ @flags_val
242
+ end
243
+ end
244
+ end
245
+
246
+ require 'elf/stringtable'
247
+ require 'elf/symboltable'
248
+ require 'elf/dynamic'
249
+ require 'elf/sunw'
250
+ require 'elf/gnu'
251
+
252
+ module Elf
253
+ class Section
254
+ class Type < Value
255
+ fill(
256
+ 0 => [ :Null, 'Unused' ],
257
+ 1 => [ :ProgBits, 'Program data' ],
258
+ 2 => [ :SymTab, 'Symbol table' ],
259
+ 3 => [ :StrTab, 'String table' ],
260
+ 4 => [ :RelA, 'Relocation entries with addends' ],
261
+ 5 => [ :Hash, 'Symbol hash table' ],
262
+ 6 => [ :Dynamic, 'Dynamic linking information' ],
263
+ 7 => [ :Note, 'Notes' ],
264
+ 8 => [ :NoBits, 'Program space with no data (bss)' ],
265
+ 9 => [ :Rel, 'Relocation entries, no addends' ],
266
+ 10 => [ :ShLib, 'Reserved' ],
267
+ 11 => [ :DynSym, 'Dynamic linker symbol table' ],
268
+ 14 => [ :InitArray, 'Array of constructors' ],
269
+ 15 => [ :FiniArray, 'Array of destructors' ],
270
+ 16 => [ :PreinitArray, 'Array of pre-constructors' ],
271
+ 17 => [ :Group, 'Section group' ],
272
+ 18 => [ :SymTabShndx, 'Extended section indeces' ],
273
+ # OS-specific range start
274
+ 0x6ffffff8 => [ :Checksum, 'Checksum for DSO content' ]
275
+ # OS-specific range end
276
+ )
277
+
278
+ # Sun-specific range
279
+ SunWSpecific = 0x6ffffff1..0x6fffffff
280
+
281
+ class SunW < Value
282
+ fill(
283
+ SunWSpecific.min+0x0 => [ :SymSort, nil ],
284
+ SunWSpecific.min+0x1 => [ :TLSSort, nil ],
285
+ SunWSpecific.min+0x2 => [ :LDynSym, nil ],
286
+ SunWSpecific.min+0x3 => [ :DOF, nil ],
287
+ SunWSpecific.min+0x4 => [ :Cap, "Software/Hardware Capabilities" ],
288
+ SunWSpecific.min+0x5 => [ :Signature, nil ],
289
+ SunWSpecific.min+0x6 => [ :Annotate, nil ],
290
+ SunWSpecific.min+0x7 => [ :DebugStr, nil ],
291
+ SunWSpecific.min+0x8 => [ :Debug, nil ],
292
+ SunWSpecific.min+0x9 => [ :Move, nil ],
293
+ SunWSpecific.min+0xa => [ :ComDat, nil ],
294
+ SunWSpecific.min+0xb => [ :SymInfo, nil ],
295
+ SunWSpecific.min+0xc => [ :VerDef, nil ],
296
+ SunWSpecific.min+0xd => [ :VerNeed, nil ],
297
+ SunWSpecific.min+0xe => [ :VerSym, nil ]
298
+ )
299
+ end
300
+
301
+ # Type values for GNU-specific sections. These sections are
302
+ # generally available just for glibc-based systems using GNU
303
+ # binutils, but might be used by other Operating Systems too.
304
+ class GNU < Value
305
+ fill(
306
+ 0x6ffffff6 => [ :Hash, 'GNU-style hash table' ],
307
+ 0x6ffffff7 => [ :Liblist, 'Prelink library list' ],
308
+ 0x6ffffffd => [ :VerDef, 'Version definition section' ],
309
+ 0x6ffffffe => [ :VerNeed, 'Version needs section' ],
310
+ 0x6fffffff => [ :VerSym, 'Version symbol table' ]
311
+ )
312
+ end
313
+
314
+ class ProcARM < Value
315
+ fill(
316
+ 0x70000001 => [ :ARMExIdx, 'ARM Exception Index' ],
317
+ 0x70000002 => [ :ARMPreemptMap, 'ARM BDAPI DLL dynamic linking pre-emption map' ],
318
+ 0x70000003 => [ :ARMAttributes, 'ARM Attributes' ],
319
+ 0x70000004 => [ :ARMDebugOverlay, 'ARM Debug Overlay' ],
320
+ 0x70000005 => [ :ARMOverlaySection, 'ARM Overlay Table' ]
321
+ )
322
+ end
323
+
324
+ OsSpecific = 0x60000000..0x6fffffff
325
+ ProcSpecific = 0x70000000..0x7fffffff
326
+ # Application-specific range
327
+ UserSpecific = 0x80000000..0x8fffffff
328
+
329
+ SpecialRanges = {
330
+ "SHT_LOOS" => OsSpecific,
331
+ "SHT_LOPROC" => ProcSpecific,
332
+ "SHT_LOUSER" => UserSpecific
333
+ }
334
+
335
+ Class = {
336
+ StrTab => Elf::StringTable,
337
+ SymTab => Elf::SymbolTable,
338
+ DynSym => Elf::SymbolTable,
339
+ Dynamic => Elf::Dynamic,
340
+ GNU::VerSym => Elf::GNU::SymbolVersionTable,
341
+ GNU::VerDef => Elf::GNU::SymbolVersionDef,
342
+ GNU::VerNeed => Elf::GNU::SymbolVersionNeed,
343
+ SunW::Cap => Elf::SunW::Capabilities
344
+ }
345
+
346
+ end
347
+ end
348
+ end