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,174 @@
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
+ 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 size
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 size
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 size
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,321 @@
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 '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, sectdata)
59
+ begin
60
+ if Type::ProcSpecific.include?(sectdata[:type_id])
61
+ case elf.machine
62
+ when Elf::Machine::ARM
63
+ type = Type::ProcARM[sectdata[:type_id]]
64
+ else
65
+ type = Type[sectdata[:type_id]]
66
+ end
67
+ elsif Type::OsSpecific.include?(sectdata[:type_id])
68
+ # Unfortunately, even though OS ABIs are well-defined for both
69
+ # GNU/Linux and Solaris, they don't seem to get used at all.
70
+ #
71
+ # For this reason, instead of basing ourselves on (just) the
72
+ # OS ABI, the name of the section is used to identify the type
73
+ # of section to use
74
+
75
+ # Don't set the name if there is no string table loaded
76
+ name = elf.string_table ? elf.string_table[sectdata[:name_idx]] : ""
77
+ if elf.abi == Elf::OsAbi::Solaris or
78
+ name =~ /^\.SUNW_/
79
+ type = Type::SunW[sectdata[:type_id]]
80
+ elsif elf.abi == Elf::OsAbi::Linux or
81
+ name =~ /^\.gnu\./
82
+ type = Type::GNU[sectdata[:type_id]]
83
+ else
84
+ type = Type[sectdata[:type_id]]
85
+ end
86
+ else
87
+ type = Type[sectdata[:type_id]]
88
+ end
89
+ type = nil if Type.is_a? Value::Unknown
90
+ rescue Value::OutOfBound
91
+ type = nil
92
+ end
93
+
94
+ raise UnknownType.new(sectdata[:type_id],
95
+ elf.string_table ? elf.string_table[sectdata[:name_idx]] : sectdata[:name_idx]
96
+ ) if type.nil?
97
+
98
+ if Type::Class[type]
99
+ return Type::Class[type].new(elf, sectdata, type)
100
+ else
101
+ return Section.new(elf, sectdata, type)
102
+ end
103
+ end
104
+
105
+ attr_reader :offset, :addr, :type, :size, :file
106
+
107
+ def initialize(elf, sectdata, type)
108
+ @file = elf
109
+ @type = type
110
+ @name = sectdata[:name_idx]
111
+ @flags_val = sectdata[:flags_val]
112
+ @addr = sectdata[:addr]
113
+ @offset = sectdata[:offset]
114
+ @size = sectdata[:size]
115
+ @link = sectdata[:link]
116
+ @info = sectdata[:info]
117
+ @addralign = sectdata[:addralign]
118
+ @entsize = sectdata[:entsize]
119
+
120
+ @numentries = @size/@entsize unless @entsize == 0
121
+ end
122
+
123
+ def ==(other)
124
+ # For the sake of retrocompatibility and code readability,
125
+ # accept these two types as a valid (albeit false) comparison.
126
+ return false if other.nil? or other.is_a? Integer
127
+
128
+ raise TypeError.new("wrong argument type #{other.class} (expected Elf::Section)") unless
129
+ other.is_a? Section
130
+
131
+ other.file == @file and other.addr == @addr
132
+ end
133
+
134
+ def name
135
+ # We didn't read the name in form of string yet;
136
+ # Check if the file has loaded a string table yet
137
+ if @name.is_a? Integer and @file.string_table
138
+ @name = @file.string_table[@name]
139
+ end
140
+
141
+ @name
142
+ end
143
+
144
+ # Alias to_s to name so that using this in a string will report the name
145
+ alias :to_s :name
146
+
147
+ def link
148
+ # We didn't get the linked section header yet
149
+ if @link.is_a? Integer
150
+ @link = @file[@link]
151
+ end
152
+
153
+ @link
154
+ end
155
+
156
+ # Return a set of flag items, easier to check for single elements.
157
+ def flags
158
+ return @flags if @flags
159
+
160
+ @flags = Set.new
161
+ Flags.each do |flag|
162
+ flags.add(flag) if (@flags_val & flag.val) == flag.val
163
+ end
164
+
165
+ @flags
166
+ end
167
+
168
+ def load
169
+ oldpos = @file.tell
170
+ @file.seek(@offset, IO::SEEK_SET)
171
+
172
+ load_internal
173
+
174
+ @file.seek(oldpos, IO::SEEK_SET)
175
+ end
176
+
177
+ def summary
178
+ $stdout.puts "#{name}\t\t#{@type}\t#{@flags_val}\t#{@addr}\t#{@offset}\t#{@size}\t#{@link}\t#{@info}\t#{@addralign}\t#{@entsize}"
179
+ end
180
+
181
+ class Flags < Value
182
+ fill(
183
+ 0x00000001 => [ :Write, 'Writable' ],
184
+ 0x00000002 => [ :Alloc, 'Allocated' ],
185
+ 0x00000004 => [ :ExecInstr, 'Executable' ],
186
+ 0x00000010 => [ :Merge, 'Mergeable' ],
187
+ 0x00000020 => [ :Strings, 'Contains null-terminated strings' ],
188
+ 0x00000040 => [ :InfoLink, 'sh_info contains SHT index' ],
189
+ 0x00000080 => [ :LinkOrder, 'Preserve order after combining' ],
190
+ 0x00000100 => [ :OsNonConforming, 'Non-standard OS specific handling required' ],
191
+ 0x00000200 => [ :Group, 'Section is member of a group' ],
192
+ 0x00000400 => [ :TLS, 'Section hold thread-local data' ],
193
+ 0x40000000 => [ :Ordered, 'Special ordering requirement' ],
194
+ 0x80000000 => [ :Exclude, 'Section is excluded unless referenced or allocated' ]
195
+ )
196
+
197
+ # OS-specific flags mask
198
+ MaskOS = 0x0ff00000
199
+ # Processor-specific flags mask
200
+ MaskProc = 0xf0000000
201
+ end
202
+
203
+ # Return the nm(1) code for the section.
204
+ #
205
+ # This function is usually mostly used by Elf::Symbol#nm_code. It
206
+ # moves the parts of the logic that have to deal with section
207
+ # flags and similar here, to stay closer with the section's data
208
+ def nm_code
209
+ @nmflag ||= case
210
+ when flags.include?(Flags::ExecInstr)
211
+ "T"
212
+ when type == Type::NoBits then "B"
213
+ when type == Type::Note then "N"
214
+ when name =~ /\.rodata.*/ then "R"
215
+ when name =~ /\.(t|pic)?data.*/ then "D"
216
+ else
217
+ nil
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ require 'elf/stringtable'
224
+ require 'elf/symboltable'
225
+ require 'elf/dynamic'
226
+ require 'elf/sunw'
227
+ require 'elf/gnu'
228
+
229
+ module Elf
230
+ class Section
231
+ class Type < Value
232
+ fill(
233
+ 0 => [ :Null, 'Unused' ],
234
+ 1 => [ :ProgBits, 'Program data' ],
235
+ 2 => [ :SymTab, 'Symbol table' ],
236
+ 3 => [ :StrTab, 'String table' ],
237
+ 4 => [ :RelA, 'Relocation entries with addends' ],
238
+ 5 => [ :Hash, 'Symbol hash table' ],
239
+ 6 => [ :Dynamic, 'Dynamic linking information' ],
240
+ 7 => [ :Note, 'Notes' ],
241
+ 8 => [ :NoBits, 'Program space with no data (bss)' ],
242
+ 9 => [ :Rel, 'Relocation entries, no addends' ],
243
+ 10 => [ :ShLib, 'Reserved' ],
244
+ 11 => [ :DynSym, 'Dynamic linker symbol table' ],
245
+ 14 => [ :InitArray, 'Array of constructors' ],
246
+ 15 => [ :FiniArray, 'Array of destructors' ],
247
+ 16 => [ :PreinitArray, 'Array of pre-constructors' ],
248
+ 17 => [ :Group, 'Section group' ],
249
+ 18 => [ :SymTabShndx, 'Extended section indeces' ],
250
+ # OS-specific range start
251
+ 0x6ffffff8 => [ :Checksum, 'Checksum for DSO content' ]
252
+ # OS-specific range end
253
+ )
254
+
255
+ # Sun-specific range
256
+ SunWSpecific = 0x6ffffff1..0x6fffffff
257
+
258
+ class SunW < Value
259
+ fill(
260
+ SunWSpecific.min+0x0 => [ :SymSort, nil ],
261
+ SunWSpecific.min+0x1 => [ :TLSSort, nil ],
262
+ SunWSpecific.min+0x2 => [ :LDynSym, nil ],
263
+ SunWSpecific.min+0x3 => [ :DOF, nil ],
264
+ SunWSpecific.min+0x4 => [ :Cap, "Software/Hardware Capabilities" ],
265
+ SunWSpecific.min+0x5 => [ :Signature, nil ],
266
+ SunWSpecific.min+0x6 => [ :Annotate, nil ],
267
+ SunWSpecific.min+0x7 => [ :DebugStr, nil ],
268
+ SunWSpecific.min+0x8 => [ :Debug, nil ],
269
+ SunWSpecific.min+0x9 => [ :Move, nil ],
270
+ SunWSpecific.min+0xa => [ :ComDat, nil ],
271
+ SunWSpecific.min+0xb => [ :SymInfo, nil ],
272
+ SunWSpecific.min+0xc => [ :VerDef, nil ],
273
+ SunWSpecific.min+0xd => [ :VerNeed, nil ],
274
+ SunWSpecific.min+0xe => [ :VerSym, nil ]
275
+ )
276
+ end
277
+
278
+ # Type values for GNU-specific sections. These sections are
279
+ # generally available just for glibc-based systems using GNU
280
+ # binutils, but might be used by other Operating Systems too.
281
+ class GNU < Value
282
+ fill(
283
+ 0x6ffffff6 => [ :Hash, 'GNU-style hash table' ],
284
+ 0x6ffffff7 => [ :Liblist, 'Prelink library list' ],
285
+ 0x6ffffffd => [ :VerDef, 'Version definition section' ],
286
+ 0x6ffffffe => [ :VerNeed, 'Version needs section' ],
287
+ 0x6fffffff => [ :VerSym, 'Version symbol table' ]
288
+ )
289
+ end
290
+
291
+ class ProcARM < Value
292
+ fill(
293
+ 0x70000003 => [ :ARMAttributes, 'ARM Attributes' ]
294
+ )
295
+ end
296
+
297
+ OsSpecific = 0x60000000..0x6fffffff
298
+ ProcSpecific = 0x70000000..0x7fffffff
299
+ # Application-specific range
300
+ UserSpecific = 0x80000000..0x8fffffff
301
+
302
+ SpecialRanges = {
303
+ "SHT_LOOS" => OsSpecific,
304
+ "SHT_LOPROC" => ProcSpecific,
305
+ "SHT_LOUSER" => UserSpecific
306
+ }
307
+
308
+ Class = {
309
+ StrTab => Elf::StringTable,
310
+ SymTab => Elf::SymbolTable,
311
+ DynSym => Elf::SymbolTable,
312
+ Dynamic => Elf::Dynamic,
313
+ GNU::VerSym => Elf::GNU::SymbolVersionTable,
314
+ GNU::VerDef => Elf::GNU::SymbolVersionDef,
315
+ GNU::VerNeed => Elf::GNU::SymbolVersionNeed,
316
+ SunW::Cap => Elf::SunW::Capabilities
317
+ }
318
+
319
+ end
320
+ end
321
+ end