ruby-elf 1.0.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.
@@ -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