ruby-elf 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,49 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Simple ELF parser for Ruby
3
+ #
4
+ # Copyright © 2007-2010 Diego E. "Flameeyes" Pettenò <flameeyes@gmail.com>
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 'elf/section'
25
+
26
+ module Elf
27
+ class StringTable < Section
28
+ def load_internal
29
+ @rawtable = @file.readexactly(@size)
30
+ end
31
+
32
+ class InvalidIndex < Exception
33
+ def initialize(idx, max_idx)
34
+ super("Invalid index #{idx} (maximum index: #{max_idx})")
35
+ end
36
+ end
37
+
38
+ def [](idx)
39
+ load unless @rawtable
40
+
41
+ raise InvalidIndex.new(idx, @rawtable.size) if
42
+ idx >= @rawtable.size
43
+
44
+ endidx = @rawtable.index("\x00", idx)
45
+
46
+ return @rawtable[idx..endidx].chomp("\x00")
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,158 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Simple ELF parser for Ruby
3
+ #
4
+ # Copyright © 2008-2010 Diego E. "Flameeyes" Pettenò <flameeyes@gmail.com>
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
+ # Constants derived from OpenSolaris elf.h and documentation
10
+ # Copyright © 2007 Sun Microsystems, Inc.
11
+ #
12
+ # This program is free software; you can redistribute it and/or modify
13
+ # it under the terms of the GNU General Public License as published by
14
+ # the Free Software Foundation; either version 2 of the License, or
15
+ # (at your option) any later version.
16
+ #
17
+ # This program is distributed in the hope that it will be useful,
18
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ # GNU General Public License for more details.
21
+ #
22
+ # You should have received a copy of the GNU General Public License
23
+ # along with this generator; if not, write to the Free Software
24
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
+
26
+ module Elf
27
+ # Sun-specific sections parsing
28
+ module SunW
29
+
30
+ class Capabilities < Section
31
+ class Tag < Value
32
+ fill(
33
+ 0 => [ :Null, nil ],
34
+ 1 => [ :HW1, "Hardware capabilities" ],
35
+ 2 => [ :SF1, "Software capabilities" ]
36
+ )
37
+ end
38
+
39
+ class Software1 < Value
40
+ fill(
41
+ 0x0001 => [ :FramePointerKnown, "Frame pointer use is known" ],
42
+ 0x0002 => [ :FramePointerUsed, "Frame pointer is used" ]
43
+ )
44
+
45
+ # Known capabilities mask
46
+ KnownMask = 0x003
47
+ end
48
+
49
+ module Hardware1
50
+ class Sparc < Value
51
+ fill(
52
+ 0x0001 => [ :Mul32, "Uses 32x32-bit smul/umul" ],
53
+ 0x0002 => [ :Div32, "Uses 32x32-bit sdiv/udiv" ],
54
+ 0x0004 => [ :Fsmuld, "Uses fsmuld instruction" ],
55
+ 0x0008 => [ :V8Plus, "Uses V9 intructins in 32-bit apps" ],
56
+ 0x0010 => [ :Popc, "Uses popc instruction" ],
57
+ 0x0020 => [ :Vis, "Uses VIS instruction set" ],
58
+ 0x0040 => [ :Vis2, "Uses VIS2 instruction set" ],
59
+ 0x0080 => [ :AsiBlkInit, nil ],
60
+ 0x0100 => [ :Fmaf, "Uses Fused Multiply-Add" ],
61
+ 0x0200 => [ :FjFmau, "Uses Fujitsu Unfused Multiply-Add" ],
62
+ 0x0400 => [ :Ima, "Uses Integer Multiply-Add" ]
63
+ )
64
+ end
65
+
66
+ class I386 < Value
67
+ fill(
68
+ 0x00000001 => [ :FPU, "Uses x87-style floating point" ],
69
+ 0x00000002 => [ :TSC, "Uses rdtsc instruction" ],
70
+ 0x00000004 => [ :CX8, "Uses cmpxchg8b instruction" ],
71
+ 0x00000008 => [ :SEP, "Uses sysenter/sysexit instructions" ],
72
+ 0x00000010 => [ :AmdSycC, "Uses AMD's syscall/sysret instructions" ],
73
+ 0x00000020 => [ :CMov, "Uses conditional move instructions" ],
74
+ 0x00000040 => [ :MMX, "Uses MMX instruction set" ],
75
+ 0x00000080 => [ :AmdMMX, "Uses AMD's MMX instruction set" ],
76
+ 0x00000100 => [ :Amd3DNow, "Uses AMD's 3DNow! instruction set" ],
77
+ 0x00000200 => [ :Amd3DNowX, "Uses AMD's 3DNow! extended instruction set" ],
78
+ 0x00000400 => [ :FXSR, "Uses fxsave/fxrstor instructions" ],
79
+ 0x00000800 => [ :SSE, "Uses SSE instruction set and registers" ],
80
+ 0x00001000 => [ :SSE2, "Uses SSE2 instruction set and registers" ],
81
+ 0x00002000 => [ :Pause, "Uses pause instruction" ],
82
+ 0x00004000 => [ :SSE3, "Uses SSE3 instruction set and registers" ],
83
+ 0x00008000 => [ :Mon, "Uses monitor/mwait instructions" ],
84
+ 0x00010000 => [ :CX16, "Uses cmpxchg16b instruction" ],
85
+ 0x00020000 => [ :AHF, "Uses lahf/sahf instructions" ],
86
+ 0x00040000 => [ :TSCP, "Uses rdtscp instruction" ],
87
+ 0x00080000 => [ :AmdSSE4a, "Uses AMD's SSEA4a instructions" ],
88
+ 0x00100000 => [ :PopCnt, "Uses popcnt instruction" ],
89
+ 0x00200000 => [ :AmdLzcnt, "Uses AMD's lzcnt instructon" ],
90
+ 0x00400000 => [ :SSSE3, "Uses Intel's SSSE3 instruction set" ],
91
+ 0x00800000 => [ :SSE4_1, "Uses Intel's SSE4.1 instruction set" ],
92
+ 0x01000000 => [ :SSE4_2, "uses Intel's SSE4.2 instruction set" ]
93
+ )
94
+ end
95
+ end
96
+
97
+ def load_internal
98
+ elf32 = @file.elf_class == Class::Elf32
99
+
100
+ @entries = []
101
+ loop do
102
+ entry = {}
103
+ tag = elf32 ? @file.read_word : @file.read_xword
104
+ entry[:tag] = Tag[tag]
105
+
106
+ # This marks the end of the array.
107
+ break if entry[:tag] == Tag::Null
108
+
109
+ # Right now the only two types used make only use of c_val,
110
+ # but in the future there might be capabilities using c_ptr,
111
+ # so prepare for that.
112
+ case entry[:tag]
113
+ when Tag::SF1
114
+ val = elf32 ? @file.read_word : @file.read_xword
115
+ entry[:flags] = Set.new
116
+
117
+ Software1.each do |flag|
118
+ entry[:flags].add(flag) if (val & flag.val) == flag.val
119
+ end
120
+ when Tag::HW1
121
+ val = elf32 ? @file.read_word : @file.read_xword
122
+ entry[:flags] = Set.new
123
+
124
+ case @file.machine
125
+ when Machine::Sparc
126
+ Hardware1::Sparc.each do |flag|
127
+ entry[:flags].add(flag) if (val & flag.val) == flag.val
128
+ end
129
+ when Machine::I386
130
+ Hardware1::I386.each do |flag|
131
+ entry[:flags].add(flag) if (val & flag.val) == flag.val
132
+ end
133
+ else
134
+ raise "Sun-specific extensions only support i386/Sparc!"
135
+ end
136
+
137
+ else
138
+ entry[:ptr] = @file.read_addr
139
+ end
140
+
141
+ @entries << entry
142
+ end
143
+ end
144
+
145
+ def size
146
+ load unless @entries
147
+
148
+ @entries.size
149
+ end
150
+
151
+ def [](idx)
152
+ load unless @entries
153
+
154
+ @entries[idx]
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,368 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Simple ELF parser for Ruby
3
+ #
4
+ # Copyright © 2007-2010 Diego E. "Flameeyes" Pettenò <flameeyes@gmail.com>
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 'elf/symbol/demangler_gcc3'
25
+
26
+ module Elf
27
+ class Symbol
28
+ class Binding < Value
29
+ fill(
30
+ 0 => [ :Local, 'Local symbol' ],
31
+ 1 => [ :Global, 'Global symbol' ],
32
+ 2 => [ :Weak, 'Weak symbol' ],
33
+ # This one is inferred out of libpam.so
34
+ 3 => [ :Number, 'Number of defined type' ]
35
+ )
36
+
37
+ class GNU < Value
38
+ fill(
39
+ 10 => [ :Unique, 'Unique symbol' ]
40
+ )
41
+ end
42
+
43
+ OsSpecific = 10..12
44
+ ProcSpecific = 13..15
45
+
46
+ SpecialRanges = {
47
+ "STB_LOOS" => OsSpecific,
48
+ "STB_LOPROC" => ProcSpecific
49
+ }
50
+ end
51
+
52
+ class Type < Value
53
+ fill(
54
+ 0 => [ :None, 'Unspecified' ],
55
+ 1 => [ :Object, 'Data object' ],
56
+ 2 => [ :Func, 'Code object' ],
57
+ 3 => [ :Section, 'Associated with a section' ],
58
+ 4 => [ :File, 'File name' ],
59
+ 5 => [ :Common, 'Common data object' ],
60
+ 6 => [ :TLS, 'Thread-local data object' ]
61
+ )
62
+
63
+ class GNU < Value
64
+ fill(
65
+ 10 => [ :IFunc, 'Indirect function' ]
66
+ )
67
+ end
68
+
69
+ OsSpecific = 10..12
70
+ ProcSpecific = 13..15
71
+ SpecialRanges = {
72
+ "STT_LOOS" => OsSpecific,
73
+ "STT_LOPROC" => ProcSpecific
74
+ }
75
+ end
76
+
77
+ class Visibility < Value
78
+ fill(
79
+ 0 => [ :Default, 'Default visibility' ],
80
+ 1 => [ :Internal, 'Processor-specific hidden visibility' ],
81
+ 2 => [ :Hidden, 'Hidden visibility' ],
82
+ 3 => [ :Protected, 'Protected visibility' ],
83
+ 4 => [ :Exported, 'Exported symbol' ],
84
+ 5 => [ :Singleton, 'Singleton symbol' ],
85
+ 6 => [ :Eliminate, 'Symbol to be eliminated' ]
86
+ )
87
+ end
88
+
89
+ attr_reader :value, :size, :other, :bind, :type, :idx, :visibility, :file
90
+
91
+ # Create a new Symbol object reading the symbol structure from the file.
92
+ # This function assumes that the elf file is aligned ad the
93
+ # start of a symbol structure, and returns the file moved at the
94
+ # start of the symbol.
95
+ def initialize(elf, symsect, idx)
96
+ @symsect = symsect
97
+ @idx = idx
98
+
99
+ case elf.elf_class
100
+ when Class::Elf32
101
+ @name = elf.read_word
102
+ @value = elf.read_addr
103
+ @size = elf.read_word
104
+ info = elf.read_u8
105
+ @other = elf.read_u8
106
+ @section = elf.read_section
107
+ when Class::Elf64
108
+ @name = elf.read_word
109
+ info = elf.read_u8
110
+ @other = elf.read_u8
111
+ @section = elf.read_section
112
+ @value = elf.read_addr
113
+ @size = elf.read_xword
114
+ end
115
+
116
+ begin
117
+ type_value = info & 0xF
118
+ @type = case
119
+ when Type::OsSpecific.include?(type_value)
120
+ # Assume always GNU for now, but it's wrong
121
+ Type::GNU[type_value]
122
+ else
123
+ Type[type_value]
124
+ end
125
+
126
+ binding_value = info >> 4
127
+ @bind = case
128
+ when Binding::OsSpecific.include?(binding_value)
129
+ # Assume always GNU for now, but it's wrong
130
+ Binding::GNU[binding_value]
131
+ else
132
+ Binding[binding_value]
133
+ end
134
+
135
+ rescue Elf::Value::OutOfBound => e
136
+ e.append_message("While processing symbol #{@idx}. Symbol info: 0x#{info.hex}")
137
+ raise e
138
+ end
139
+
140
+ begin
141
+ @visibility = Visibility[@other & 0x03]
142
+ rescue Elf::Value::OutOfBound => e
143
+ e.append_message("While procesing symbol #{@idx}. Symbol other info: 0x#{@other.hex}")
144
+ raise e
145
+ end
146
+
147
+ @file = elf
148
+ end
149
+
150
+ class InvalidName < Exception
151
+ def initialize(name_idx, sym, symsect)
152
+ super("Invalid name index in #{symsect.link.name} #{name_idx} for symbol #{sym.idx}")
153
+ end
154
+ end
155
+
156
+ def name
157
+ # We didn't read the name in form of string yet;
158
+ if @name.is_a? Integer and @symsect.link
159
+ begin
160
+ name = @symsect.link[@name]
161
+ rescue StringTable::InvalidIndex
162
+ raise InvalidName.new(@name, self, @symsect)
163
+ end
164
+ @name = name if name
165
+ end
166
+
167
+ @name
168
+ end
169
+
170
+ # Alias to_s to name so that using this in a string will report the name
171
+ alias :to_s :name
172
+
173
+ def section
174
+ # We didn't read the section yet.
175
+ @section = nil if @section.is_a? Integer and @section == 0
176
+
177
+ if @section.is_a? Integer and
178
+ not Section::Reserved.include?(@section) and
179
+ @file.has_section?(@section)
180
+
181
+ @section = @file[@section]
182
+ end
183
+
184
+ @section
185
+ end
186
+
187
+ def version
188
+ # bit 15 is meant to say that this symbol is _not_ the default
189
+ # version to link to; we don't care about that here so we simply
190
+ # ignore its presence.
191
+ version_idx = version_index & ~(1 << 15)
192
+
193
+ return nil unless version_idx && version_idx >= 1
194
+
195
+ return '' if version_idx == 1
196
+
197
+ if section.nil? or @file['.gnu.version_d'][version_idx].nil?
198
+ return @file['.gnu.version_r'][version_idx][:name]
199
+ else
200
+ return @file['.gnu.version_d'][version_idx][:names][0]
201
+ end
202
+ end
203
+
204
+ # the default symbol version is the one that the link editor will
205
+ # use when linking against the library; any symbol is the default
206
+ # symbol unless bit 15 of its version index is set.
207
+ #
208
+ # An undefined symbol cannot be the default symbol
209
+ def version_default?
210
+ !section.nil? and (version_index & (1 << 15) == 0)
211
+ end
212
+
213
+ def defined?
214
+ return false if section.nil?
215
+ return false if section.is_a?(Integer)
216
+ return false if bind == Binding::Weak and value == 0
217
+ return true
218
+ end
219
+
220
+ # Exception raised when the NM code for a given symbol is unknown.
221
+ class UnknownNMCode < Exception
222
+ def initialize(symbol)
223
+ section = if symbol.section.nil?
224
+ nil
225
+ elsif symbol.section.is_a?(Integer)
226
+ symbol.section.hex
227
+ else
228
+ symbol.section.name
229
+ end
230
+
231
+ super("Unknown NM code for symbol #{symbol.name} in section #{section}")
232
+ end
233
+ end
234
+
235
+ # Show the letter code as compatible with GNU nm
236
+ #
237
+ # This function has been moved inside the library since multiple
238
+ # tools based on ruby-elf would be using these code to report
239
+ # symbols, and since the code is complex it's easier to have it
240
+ # here.
241
+ #
242
+ # The resturned value is a one-letter string. The function may
243
+ # raise an UnknownNMCode exception.
244
+ def nm_code
245
+ @nmflag ||= nm_code_internal
246
+ end
247
+
248
+ # Convenience function for the first tile the nm code is requested.
249
+ def nm_code_internal
250
+ nmflag = nil
251
+
252
+ case
253
+ when idx == 0
254
+ return " "
255
+
256
+ # When the section is nil, it means it goes into the Undef
257
+ # section, and the symbol is not defined.
258
+ when section.nil?
259
+ nmflag = "U"
260
+
261
+ when bind == Binding::Weak
262
+ nmflag = case type
263
+ when Type::Object then "V"
264
+ else "W"
265
+ end
266
+
267
+ nmflag.downcase! if value == 0
268
+
269
+ when section.is_a?(Integer)
270
+ nmflag = case section
271
+ when Elf::Section::Abs then "A"
272
+ when Elf::Section::Common then "C"
273
+ else nil
274
+ end
275
+
276
+ else
277
+ # Find the nm(1) code for the section.
278
+ nmflag = section.nm_code
279
+ end
280
+
281
+ # If we haven't found the flag with the above code, we don't
282
+ # know what to use, so raise exception.
283
+ raise UnknownNMCode.new(self) if nmflag.nil?
284
+
285
+ nmflag = nmflag.dup
286
+
287
+ nmflag.downcase! if bind == Binding::Local
288
+
289
+ return nmflag
290
+ end
291
+
292
+ # Reports the symbol's address as a string, if any is provided
293
+ #
294
+ # Reports a string full of whitespace if the symbols is not
295
+ # defined (as there is no address)
296
+ def address_string
297
+ addrsize = (@file.elf_class == Elf::Class::Elf32 ? 8 : 16)
298
+
299
+ if section
300
+ sprintf("%0#{addrsize}x", value)
301
+ else
302
+ ' ' * addrsize
303
+ end
304
+ end
305
+
306
+ # Check whether two symbols are the same
307
+ #
308
+ # This function compares the name, version and section of two
309
+ # symbols to tell if they are the same symbol.
310
+ def ==(other)
311
+ return false unless other.is_a? Symbol
312
+
313
+ return false unless name == other.name
314
+ return false unless version == other.version
315
+
316
+ return false if section == nil and other.section != nil
317
+ return false if section != nil and other.section == nil
318
+
319
+ return true
320
+ end
321
+
322
+ def eql?(other)
323
+ return self == other
324
+ end
325
+
326
+ # Check whether one symbol is compatible with the other
327
+ #
328
+ # This function compares the name and version of two symbols, and
329
+ # ensures that only one of them is undefined; this allows to
330
+ # establish whether one symbol might be satisfied by another.
331
+ def =~(other)
332
+ return false unless other.is_a? Symbol
333
+
334
+ return false unless name == other.name
335
+ return false unless version == other.version
336
+
337
+ return false if section == nil and other.section == nil
338
+ return false if section != nil and other.section != nil
339
+
340
+ return true
341
+ end
342
+
343
+ Demanglers = [ Elf::Symbol::Demangler::GCC3 ]
344
+ def demangle
345
+ return @demangled if @demangled
346
+
347
+ Demanglers.each do |demangler|
348
+ break if (@demangled ||= demangler.demangle(name))
349
+ end
350
+
351
+ # We're going to remove top-namespace specifications as we don't
352
+ # need them, but it's easier for the demangler to still emit
353
+ # them.
354
+ @demangled.gsub!(/(^| |\()::/, '\1') if @demangled
355
+
356
+ return @demangled ||= name
357
+ end
358
+
359
+ private
360
+ def version_index
361
+ return nil if (!@file.has_section?('.gnu.version')) or
362
+ ( section.is_a?(Integer) and section == Elf::Section::Abs ) or
363
+ ( section.is_a? Elf::Section and section.name == ".bss" )
364
+
365
+ @file['.gnu.version'][@idx]
366
+ end
367
+ end
368
+ end