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,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