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,392 @@
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/utils/loader'
25
+
26
+ module Elf
27
+ class Dynamic < Section
28
+ class Type < Value
29
+ class Unknown < Elf::Value::Unknown
30
+ def initialize(val, desc)
31
+ super(val, desc)
32
+ end
33
+
34
+ # For unknown types, guess Value is the key.
35
+ def attribute
36
+ :Value
37
+ end
38
+ end
39
+
40
+ attr_reader :attribute
41
+
42
+ def initialize(val, params)
43
+ super(val, params)
44
+
45
+ @attribute = params[2]
46
+ end
47
+
48
+ fill(
49
+ 0 => [ :Null, "NULL", :Ignore ],
50
+ 1 => [ :Needed, "NEEDED", :Value ],
51
+ 2 => [ :PltRelSz, "PLTRELSZ", :Value ],
52
+ 3 => [ :PltGot, "PLTGOT", :Ignore ],
53
+ 4 => [ :Hash, "HASH", :Address ],
54
+ 5 => [ :StrTab, "STRTAB", :Address ],
55
+ 6 => [ :SymTab, "SYMTAB", :Address ],
56
+ 7 => [ :RelA, "RELA", :Address ],
57
+ 8 => [ :RelASz, "RELASZ", :Value ],
58
+ 9 => [ :RelAEnt, "RELAENT", :Value ],
59
+ 10 => [ :StrSz, "STRSZ", :Value ],
60
+ 11 => [ :SymEnt, "SYMENT", :Value ],
61
+ 12 => [ :Init, "INIT", :Address ],
62
+ 13 => [ :Fini, "FINI", :Address ],
63
+ 14 => [ :SoName, "SONAME", :Value ],
64
+ 15 => [ :RPath, "RPATH", :Value ],
65
+ 16 => [ :Symbolic, "SYMBOLIC", :Ignore ],
66
+ 17 => [ :Rel, "REL", :Address ],
67
+ 18 => [ :RelSz, "RELSZ", :Value ],
68
+ 19 => [ :RelEnt, "RELENT", :Value ],
69
+ 20 => [ :PltRel, "PLTREL", :Value ],
70
+ 21 => [ :Debug, "DEBUG", :Ignore ],
71
+ 22 => [ :TextRel, "TEXTREL", :Ignore ],
72
+ 23 => [ :JmpRel, "JMPREL", :Address ],
73
+ 24 => [ :BindNow, "BINDNOW", :Ignore ],
74
+ 25 => [ :InitArray, "INIT_ARRAY", :Address ],
75
+ 26 => [ :FiniArray, "FINI_ARRAY", :Address ],
76
+ 27 => [ :InitArraySz, "INIT_ARRAYSZ", :Value ],
77
+ 28 => [ :FiniArraySz, "FINI_ARRAYSZ", :Value ],
78
+ 29 => [ :RunPath, "RUNPATH", :Value ],
79
+ 30 => [ :Flags, "FLAGS", :Value ],
80
+ 32 => [ :PreinitArray, "PREINIT_ARRAY", :Address ],
81
+ 33 => [ :PreinitArraySz, "PREINIT_ARRAYSZ", :Value ],
82
+
83
+ # DT_VAL* constants mapping
84
+ 0x6ffffdf5 => [ :GNUPrelinked, "GNU_PRELINKED", :Value ],
85
+ 0x6ffffdf6 => [ :GNUConflictSz, "GNU_CONFLICTSZ", :Value ],
86
+ 0x6ffffdf7 => [ :GNULibListSz, "GNU_LIBLISTSZ", :Value ],
87
+ 0x6ffffdf8 => [ :CheckSum, "CHECKSUM", :Value ],
88
+ 0x6ffffdf9 => [ :PltPadSz, "PLTPADSZ", :Value ],
89
+ 0x6ffffdfa => [ :MoveEnt, "MOVENT", :Value ],
90
+ 0x6ffffdfb => [ :MoveSz, "MOVESZ", :Value ],
91
+ 0x6ffffdfc => [ :Feature1, "FEATURE_1", :Value ],
92
+ 0x6ffffdfd => [ :PosFlag1, "POSFLAG_1", :Value ],
93
+ 0x6ffffdfe => [ :SymInSz, "SYMINSZ", :Value ],
94
+ 0x6ffffdff => [ :SymInEnt, "SYMINENT", :Value ],
95
+
96
+ # DT_ADDR* constants mapping
97
+ 0x6ffffef5 => [ :GNUHash, "GNU_HASH", :Address ],
98
+ 0x6ffffef6 => [ :TLSDescPlt, "TLSDESC_PLT", :Address ],
99
+ 0x6ffffef7 => [ :TLSDescGot, "TLSDESC_GOT", :Address ],
100
+ 0x6ffffef8 => [ :GNUConflict, "GNU_CONFLICT", :Address ],
101
+ 0x6ffffef9 => [ :GNULibList, "GNU_LIBLIST", :Address ],
102
+ 0x6ffffefa => [ :Config, "CONFIG", :Address ],
103
+ 0x6ffffefb => [ :DepAudit, "DEPAUDIT", :Address ],
104
+ 0x6ffffefc => [ :PltPad, "PLTPAD", :Address ],
105
+ 0x6ffffefd => [ :MoveTab, "MOVETAB", :Address ],
106
+ 0x6ffffeff => [ :SymInfo, "SYMINFO", :Address ],
107
+
108
+ # GNU extension, should be named :GNUVerSym?
109
+ 0x6ffffff0 => [ :VerSym, "VERSYM", :Ignore ],
110
+
111
+ 0x6ffffff9 => [ :RelACount, "RELACOUNT", :Value ],
112
+ 0x6ffffffa => [ :RelCount, "RELCOUNT", :Value ],
113
+
114
+ # Sun extensions, should be named :Sun*?
115
+ 0x6ffffffb => [ :Flags1, "FLAGS_1", :Value ],
116
+ 0x6ffffffc => [ :VerDef, "VERDEF", :Address ],
117
+ 0x6ffffffd => [ :VerDefNum, "VERDEFNUM", :Value ],
118
+ 0x6ffffffe => [ :VerNeed, "VERNEED", :Address ],
119
+ 0x6fffffff => [ :VerNeedNum, "VERNEEDNUM", :Value ],
120
+
121
+ # Unknown CPU-specific extensions
122
+ 0x7ffffffd => [ :Auxiliary, "AUXILIARY", :Value ]
123
+ )
124
+
125
+ OsSpecific = 0x6000000d..0x6ffff000
126
+ ProcSpecific = 0x70000000..0x7fffffff
127
+ SpecialRanges = {
128
+ "DT_LOOS" => OsSpecific,
129
+ "DT_LOPROC" => ProcSpecific
130
+ }
131
+ end
132
+
133
+ module Flags
134
+ Origin = 0x00000001
135
+ Symbolic = 0x00000002
136
+ Textrel = 0x00000004
137
+ BindNow = 0x00000008
138
+ StaticTLS = 0x00000010
139
+ end
140
+
141
+ module Flags1
142
+ Now = 0x00000001
143
+ Global = 0x00000002
144
+ Group = 0x00000004
145
+ NoDelete = 0x00000008
146
+ LoadFltr = 0x00000010
147
+ InitFirst = 0x00000020
148
+ NoOpen = 0x00000040
149
+ Origin = 0x00000080
150
+ Direct = 0x00000100
151
+ Trans = 0x00000200
152
+ Interpose = 0x00000400
153
+ NoDefLib = 0x00000800
154
+ NoDump = 0x00001000
155
+ ConfAlt = 0x00002000
156
+ EndFiltee = 0x00004000
157
+ DispRelDNE = 0x00008000
158
+ DispRelPND = 0x00010000
159
+ end
160
+
161
+ module Features1
162
+ ParInit = 0x00000001
163
+ ConfExp = 0x00000002
164
+ end
165
+
166
+ module PosFlags1
167
+ LazyLoad = 0x00000001
168
+ GroupPerm = 0x00000002
169
+ end
170
+
171
+ # A .dynamic section entry
172
+ class Entry
173
+ attr_reader :type, :value
174
+
175
+ def initialize(type, file)
176
+ @file = file
177
+ @type = type
178
+ @value = case type.attribute
179
+ when :Address then @file.read_addr
180
+ when :Value then @file.elf_class == Class::Elf32 ? @file.read_word : @file.read_xword
181
+ else @file.read_addr
182
+ end
183
+ end
184
+ end
185
+
186
+ # An entry for a string value
187
+ class StringEntry < Entry
188
+ def parsed
189
+ @parsed = @file['.dynstr'][@value] unless @parsed
190
+ return @parsed
191
+ end
192
+ end
193
+
194
+ # An entry for a timestamp value
195
+ class TimestampEntry < Entry
196
+ def parsed
197
+ @parsed = Time.at(@value) unless @parsed
198
+ return @parsed
199
+ end
200
+ end
201
+
202
+ # An entry for the address of another section
203
+ class SectionLink < Entry
204
+ def parsed
205
+ @parsed = @file.find_section_by_addr(@value) unless @parsed
206
+ return @parsed
207
+ end
208
+ end
209
+
210
+ ClassMap = {
211
+ Type::Needed => StringEntry,
212
+ Type::Hash => SectionLink,
213
+ Type::StrTab => SectionLink,
214
+ Type::SymTab => SectionLink,
215
+ Type::Init => SectionLink,
216
+ Type::Fini => SectionLink,
217
+ Type::SoName => StringEntry,
218
+ Type::RPath => StringEntry,
219
+ Type::RunPath => StringEntry,
220
+ Type::GNUPrelinked => TimestampEntry,
221
+ Type::GNUHash => SectionLink,
222
+ Type::Auxiliary => StringEntry
223
+ }
224
+
225
+ def load_internal
226
+ elf32 = @file.elf_class == Class::Elf32
227
+
228
+ @entries = []
229
+
230
+ for i in 1..@numentries
231
+ type = Type[elf32 ? @file.read_sword : @file.read_sxword]
232
+
233
+ @entries << if ClassMap.has_key? type
234
+ ClassMap[type].new(type, @file)
235
+ else
236
+ Entry.new(type, @file)
237
+ end
238
+
239
+ break if type == Type::Null
240
+ end
241
+ end
242
+
243
+ # Iterate over all the entries in the .dynamic section.
244
+ def each_entry(&block)
245
+ load unless @entries
246
+
247
+ @entries.each(&block)
248
+ end
249
+
250
+ # Return the amount of entries in the .dynamic section.
251
+ def size
252
+ load unless @entries
253
+
254
+ @entries.size
255
+ end
256
+
257
+ def soname
258
+ each_entry do |entry|
259
+ return entry.parsed if entry.type == Type::SoName
260
+ end
261
+
262
+ return nil
263
+ end
264
+
265
+ # Returns the value of DT_RPATH entries in the file
266
+ def rpath
267
+ @rpath ||= auxiliary_library_path(Type::RPath)
268
+ end
269
+
270
+ # Returns the value of DT_RUNPATH entries in the file
271
+ def runpath
272
+ @runpath ||= auxiliary_library_path(Type::RunPath)
273
+ end
274
+
275
+ # Returns the auxiliary path specified by the given type
276
+ #
277
+ # Please never use this function because it does not caches its
278
+ # values, use Dynamic#rpath or Dynamic#runpath instead.
279
+ def auxiliary_library_path(type)
280
+ retval = Array.new
281
+
282
+ each_entry do |entry|
283
+ next unless entry.type == type
284
+ retval.concat entry.parsed.split(":")
285
+ end
286
+
287
+ return retval.uniq.collect do |path|
288
+ if path == "$ORIGIN" or path == "${ORIGIN}"
289
+ Pathname.new(@file.path).dirname
290
+ else
291
+ begin
292
+ Pathname.new(path).realpath
293
+ rescue Errno::ENOENT
294
+ path
295
+ end
296
+ end.to_s
297
+ end
298
+ end
299
+
300
+ # Returns the complete library path for the current ELF file.
301
+ #
302
+ # Since the ELF loaders have somewhat complex rules to identify
303
+ # the path to load dependencies from, we evalute it on a per-file
304
+ # basis.
305
+ def complete_library_path
306
+ if @complete_library_path.nil?
307
+ @complete_library_path = Array.new
308
+
309
+ # If there is no DT_RUNPATH. RPATH wins over the LD_LIBRARY_PATH
310
+ @complete_library_path.concat rpath unless runpath.empty?
311
+
312
+ @complete_library_path.concat Elf::Utilities.environment_library_path
313
+
314
+ # If there is a DT_RUNPATH it wins over the system library path
315
+ @complete_library_path.concat runpath
316
+
317
+ @complete_library_path.concat Elf::Utilities.system_library_path
318
+
319
+ @complete_library_path.uniq!
320
+ end
321
+
322
+ return @complete_library_path
323
+ end
324
+
325
+ # Return the ELF library corresponding to the given soname.
326
+ #
327
+ # This function gets the system library paths and eventually adds
328
+ # the rpaths as expressed by the file itself, then look them up to
329
+ # find the proper library, just like the loader would.
330
+ def find_library(soname)
331
+ complete_library_path.each do |path|
332
+ # For the ELF linker an empty entry in the library path is the
333
+ # same as "." and means the current working directory, so
334
+ # replace it.
335
+ path = "." if path == ""
336
+
337
+ if FileTest.exist? "#{path}/#{soname}"
338
+ begin
339
+ possible_library = Elf::Utilities::FilePool["#{path}/#{soname}"]
340
+
341
+ return possible_library if @file.is_compatible(possible_library)
342
+ rescue Errno::ENOENT, Errno::EACCES, Errno::EISDIR, Elf::File::NotAnELF
343
+ # we don't care if the file does not exist and similar.
344
+ end
345
+ end
346
+ end
347
+
348
+ return nil
349
+ end
350
+
351
+ # Returns an array of needed sonames from .dynamic section
352
+ #
353
+ # This function reads the .dynamic section of the file for
354
+ # DT_NEEDED entries, and fills an array with them.
355
+ def needed_sonames
356
+ if @needed_sonames.nil?
357
+ @needed_sonames = Array.new
358
+
359
+ each_entry do |entry|
360
+ next unless entry.type == Elf::Dynamic::Type::Needed
361
+
362
+ @needed_sonames << entry.parsed
363
+ end
364
+ end
365
+
366
+ return @needed_sonames
367
+ end
368
+
369
+ # Returns an hash representing the dependencies of the ELF file.
370
+ #
371
+ # This function reads the .dynamic section of the file for
372
+ # DT_NEEDED entries, then looks for them and add them to an hash.
373
+ #
374
+ # Note that nil values in the hash means that the library couldn't
375
+ # be found on either the runpath of the file or the system library
376
+ # path.
377
+ def needed_libraries
378
+ # Make sure to cache the thing, we don't want to have to parse
379
+ # this multiple times since we might access it over and over to
380
+ # check the dependencies.
381
+ if @needed_libraries.nil?
382
+ @needed_libraries = Hash.new
383
+
384
+ needed_sonames.each do |soname|
385
+ @needed_libraries[soname] = find_library(soname)
386
+ end
387
+ end
388
+
389
+ return @needed_libraries
390
+ end
391
+ end
392
+ end
@@ -0,0 +1,366 @@
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 'bytestream-reader'
25
+ require 'pathname'
26
+
27
+ module Elf
28
+ class File < ::File
29
+ include BytestreamReader
30
+
31
+ class NotAnELF < Exception
32
+ def initialize
33
+ super("not a valid ELF file")
34
+ end
35
+ end
36
+
37
+ class InvalidElfClass < Exception
38
+ def initialize(klass)
39
+ super("Invalid Elf Class #{klass}")
40
+ end
41
+ end
42
+
43
+ class InvalidDataEncoding < Exception
44
+ def initialize(encoding)
45
+ super("Invalid Elf Data Encoding #{encoding}")
46
+ end
47
+ end
48
+
49
+ class UnsupportedElfVersion < Exception
50
+ def initialize(version)
51
+ super("Unsupported Elf version #{version}")
52
+ end
53
+ end
54
+
55
+ class InvalidOsAbi < Exception
56
+ def initialize(abi)
57
+ super("Invalid Elf ABI #{abi}")
58
+ end
59
+ end
60
+
61
+ class InvalidElfType < Exception
62
+ def initialize(type)
63
+ super("Invalid Elf type #{type}")
64
+ end
65
+ end
66
+
67
+ class InvalidMachine < Exception
68
+ def initialize(machine)
69
+ super("Invalid Elf machine #{machine}")
70
+ end
71
+ end
72
+
73
+ class Type < Value
74
+ fill(
75
+ 0 => [ :None, 'No file type' ],
76
+ 1 => [ :Rel, 'Relocatable file' ],
77
+ 2 => [ :Exec, 'Executable file' ],
78
+ 3 => [ :Dyn, 'Shared object file' ],
79
+ 4 => [ :Core, 'Core file' ]
80
+ )
81
+
82
+ OsSpecific = 0xfe00..0xfeff
83
+ ProcSpecific = 0xff00..0xffff
84
+ end
85
+
86
+ attr_reader :elf_class, :data_encoding, :type, :version, :abi,
87
+ :abi_version, :machine
88
+ attr_reader :string_table
89
+
90
+ def read_addr
91
+ case @elf_class
92
+ when Class::Elf32 then read_u32
93
+ when Class::Elf64 then read_u64
94
+ end
95
+ end
96
+
97
+ def read_off
98
+ case @elf_class
99
+ when Class::Elf32 then read_u32
100
+ when Class::Elf64 then read_u64
101
+ end
102
+ end
103
+
104
+ alias :read_half :read_u16
105
+
106
+ alias :read_word :read_u32
107
+ alias :read_sword :read_s32
108
+
109
+ alias :read_xword :read_u64
110
+ alias :read_sxword :read_s64
111
+
112
+ alias :read_section :read_u16
113
+ alias :read_versym :read_half
114
+
115
+ def _checkvalidpath(path)
116
+ # We're going to check the path we're given for a few reasons,
117
+ # the first of which is that we do not want to open a pipe or
118
+ # something like that. If we were just to leave it to File.open,
119
+ # we would end up stuck waiting for data on a named pipe, for
120
+ # instance.
121
+ #
122
+ # We cannot just use File.file? either because it'll be ignoring
123
+ # ENOENT by default (which would be bad for us).
124
+ #
125
+ # If we were just to use File.ftype we would have to handle
126
+ # manually the links... since Pathname will properly report
127
+ # ENOENT for broken links, we're going to keep it this way.
128
+ path = Pathname.new(path) unless path.is_a? Pathname
129
+
130
+ case path.ftype
131
+ when "directory" then raise Errno::EISDIR
132
+ when "file" then # do nothing
133
+ when "link"
134
+ # we use path.realpath here; the reason is that if
135
+ # we're to use readlink we're going to have a lot of
136
+ # trouble to find the correct path. We cannot just
137
+ # always use realpath as that will run too many stat
138
+ # calls and have a huge hit on performance.
139
+ _checkvalidpath(path.realpath)
140
+ else
141
+ raise Errno::EINVAL
142
+ end
143
+ end
144
+
145
+ private :_checkvalidpath
146
+
147
+ def initialize(path)
148
+ _checkvalidpath(path)
149
+
150
+ super(path, "rb")
151
+
152
+ begin
153
+ begin
154
+ raise NotAnELF unless readexactly(4) == MagicString
155
+ rescue EOFError
156
+ raise NotAnELF
157
+ end
158
+
159
+ begin
160
+ @elf_class = Class[read_u8]
161
+ rescue Value::OutOfBound => e
162
+ raise InvalidElfClass.new(e.val)
163
+ end
164
+
165
+ begin
166
+ @data_encoding = DataEncoding[read_u8]
167
+ rescue Value::OutOfBound => e
168
+ raise InvalidDataEncoding.new(e.val)
169
+ end
170
+
171
+ @version = read_u8
172
+ raise UnsupportedElfVersion.new(@version) if @version > 1
173
+
174
+ begin
175
+ @abi = OsAbi[read_u8]
176
+ rescue Value::OutOfBound => e
177
+ raise InvalidOsAbi.new(e.val)
178
+ end
179
+ @abi_version = read_u8
180
+
181
+ seek(16, IO::SEEK_SET)
182
+ set_endian(DataEncoding::BytestreamMapping[@data_encoding])
183
+
184
+ begin
185
+ @type = Type[read_half]
186
+ rescue Value::OutOfBound => e
187
+ raise InvalidElfType.new(e.val)
188
+ end
189
+
190
+ begin
191
+ @machine = Machine[read_half]
192
+ rescue Value::OutOfBound => e
193
+ raise InvalidMachine.new(e.val)
194
+ end
195
+
196
+ @version = read_word
197
+ @entry = read_addr
198
+ @phoff = read_off
199
+ shoff = read_off
200
+ @flags = read_word
201
+ @ehsize = read_half
202
+ @phentsize = read_half
203
+ @phnum = read_half
204
+ @shentsize = read_half
205
+ shnum = read_half
206
+ shstrndx = read_half
207
+
208
+ elf32 = elf_class == Class::Elf32
209
+ @sections = {}
210
+
211
+ @sections_data = []
212
+ seek(shoff)
213
+ for i in 1..shnum
214
+ sectdata = {}
215
+ sectdata[:idx] = i-1
216
+ sectdata[:name_idx] = read_word
217
+ sectdata[:type_id] = read_word
218
+ sectdata[:flags_val] = elf32 ? read_word : read_xword
219
+ sectdata[:addr] = read_addr
220
+ sectdata[:offset] = read_off
221
+ sectdata[:size] = elf32 ? read_word : read_xword
222
+ sectdata[:link] = read_word
223
+ sectdata[:info] = read_word
224
+ sectdata[:addralign] = elf32 ? read_word : read_xword
225
+ sectdata[:entsize] = elf32 ? read_word : read_xword
226
+
227
+ @sections_data << sectdata
228
+ end
229
+
230
+ # When the section header string table index is set to zero,
231
+ # there is not going to be a string table in the file, this
232
+ # happens usually when the file is a static ELF file built
233
+ # directly with an assembler.
234
+ #
235
+ # To handle this specific case, set the @string_table attribute
236
+ # to false, that is distinct from nil, and raise
237
+ # MissingStringTable on request. If the string table is not yet
238
+ # loaded raise instead StringTableNotLoaded.
239
+ if shstrndx == 0 or not self[shstrndx].is_a? StringTable
240
+ @string_table = false
241
+ else
242
+ @string_table = self[shstrndx]
243
+
244
+ @sections_names = {}
245
+ @sections_data.each do |sectdata|
246
+ @sections_names[@string_table[sectdata[:name_idx]]] = sectdata[:idx]
247
+ end
248
+ end
249
+ rescue ::Exception => e
250
+ close
251
+ raise e
252
+ end
253
+ end
254
+
255
+ class MissingSection < Exception
256
+ def initialize(sect_identifier)
257
+ super("Requested section #{sect_identifier} not found in the file")
258
+ end
259
+ end
260
+
261
+ def load_section(sect_idx_or_name)
262
+ if sect_idx_or_name.is_a? Integer
263
+ raise MissingSection.new(sect_idx_or_name) unless
264
+ @sections_data[sect_idx_or_name]
265
+
266
+ @sections[sect_idx_or_name] = Section.read(self, @sections_data[sect_idx_or_name])
267
+ else
268
+ raise MissingSection.new(sect_idx_or_name) unless
269
+ @sections_names[sect_idx_or_name]
270
+
271
+ load_section @sections_names[sect_idx_or_name]
272
+
273
+ @sections[sect_idx_or_name] = @sections[@sections_names[sect_idx_or_name]]
274
+ end
275
+ end
276
+
277
+ class StringTableNotLoaded < Exception
278
+ def initialize(sect_name)
279
+ super("Requested section '#{sect_name}' but there is no string table yet.")
280
+ end
281
+ end
282
+
283
+ class MissingStringTable < Exception
284
+ def initialize(sect_name)
285
+ super("Requested section '#{sect_name}' but the file has no string table.")
286
+ end
287
+ end
288
+
289
+ def [](sect_idx_or_name)
290
+ if sect_idx_or_name.is_a? String and not @string_table.is_a? Elf::Section
291
+ raise MissingStringTable.new(sect_idx_or_name) if @string_table == false
292
+ raise StringTableNotLoaded.new(sect_idx_or_name) if @string_table.nil?
293
+ end
294
+
295
+ load_section(sect_idx_or_name) unless
296
+ @sections.has_key? sect_idx_or_name
297
+
298
+ return @sections[sect_idx_or_name]
299
+ end
300
+
301
+ def each_section
302
+ @sections_data.each do |sectdata|
303
+ load_section(sectdata[:idx])
304
+ yield @sections[sectdata[:idx]]
305
+ end
306
+ end
307
+
308
+ def find_section_by_addr(addr)
309
+ @sections_data.each do |sectdata|
310
+ next unless sectdata[:addr] == addr
311
+ load_section(sectdata[:idx])
312
+ return @sections[sectdata[:idx]]
313
+ end
314
+ end
315
+
316
+ def has_section?(sect_idx_or_name)
317
+
318
+ if sect_idx_or_name.is_a? String and not @string_table.is_a? Elf::Section
319
+ return false if @string_table == false
320
+ raise StringTableNotLoaded.new(sect_idx_or_name) if @string_table.nil?
321
+ end
322
+
323
+ if sect_idx_or_name.is_a? Integer
324
+ return @sections_data[sect_idx_or_name] != nil
325
+ elsif sect_idx_or_name.is_a? String
326
+ return @sections_names.has_key?(sect_idx_or_name)
327
+ else
328
+ raise TypeError.new("wrong argument type #{sect_idx_or_name.class} (expected String or Integer)")
329
+ end
330
+ end
331
+
332
+ def summary
333
+ $stdout.puts "ELF file #{path}"
334
+ $stdout.puts "ELF class: #{@elf_class} #{@data_encoding} ver. #{@version}"
335
+ $stdout.puts "ELF ABI: #{@abi} ver. #{@abi_version}"
336
+ $stdout.puts "ELF type: #{@type} machine: #{@machine}"
337
+ $stdout.puts "Sections:"
338
+ @sections.values.uniq.each do |sh|
339
+ sh.summary
340
+ end
341
+
342
+ return nil
343
+ end
344
+
345
+ # Checks whether two ELF files are compatible one with the other for linking
346
+ #
347
+ # This function has to check whether two ELF files can be linked
348
+ # together (either at build time or at load time), and thus checks
349
+ # for class, encoding, versioning, ABI and machine type.
350
+ #
351
+ # Note that it explicitly does not check for ELF file type since
352
+ # you can link different type of files together, like an
353
+ # Executable with a Dynamic library.
354
+ def is_compatible(other)
355
+ raise TypeError.new("wrong argument type #{other.class} (expected Elf::File)") unless
356
+ other.is_a? Elf::File
357
+
358
+ @elf_class == other.elf_class and
359
+ @data_encoding == other.data_encoding and
360
+ @version == other.version and
361
+ @abi == other.abi and
362
+ @abi_version == other.abi_version and
363
+ @machine == other.machine
364
+ end
365
+ end
366
+ end