ony-ruby-elf 1.1.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 Elio Pettenò <flameeyes@flameeyes.eu>
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 = 0x60000000..0x6fffffff
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 count
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
data/lib/elf/file.rb ADDED
@@ -0,0 +1,407 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Simple ELF parser for Ruby
3
+ #
4
+ # Copyright © 2007-2010 Diego Elio Pettenò <flameeyes@flameeyes.eu>
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, :entry_address, :phoff, :shoff,
88
+ :flags, :ehsize, :phentsize, :phnum, :shentsize, :shnum,
89
+ :shstrndx
90
+ attr_reader :string_table
91
+
92
+ # raw data access
93
+ attr_reader :shoff
94
+
95
+ def read_addr
96
+ case @elf_class
97
+ when Class::Elf32 then read_u32
98
+ when Class::Elf64 then read_u64
99
+ end
100
+ end
101
+
102
+ def read_off
103
+ case @elf_class
104
+ when Class::Elf32 then read_u32
105
+ when Class::Elf64 then read_u64
106
+ end
107
+ end
108
+
109
+ alias :read_half :read_u16
110
+
111
+ alias :read_word :read_u32
112
+ alias :read_sword :read_s32
113
+
114
+ alias :read_xword :read_u64
115
+ alias :read_sxword :read_s64
116
+
117
+ alias :read_section :read_u16
118
+ alias :read_versym :read_half
119
+
120
+ def _checkvalidpath(path)
121
+ # We're going to check the path we're given for a few reasons,
122
+ # the first of which is that we do not want to open a pipe or
123
+ # something like that. If we were just to leave it to File.open,
124
+ # we would end up stuck waiting for data on a named pipe, for
125
+ # instance.
126
+ #
127
+ # We cannot just use File.file? either because it'll be ignoring
128
+ # ENOENT by default (which would be bad for us).
129
+ #
130
+ # If we were just to use File.ftype we would have to handle
131
+ # manually the links... since Pathname will properly report
132
+ # ENOENT for broken links, we're going to keep it this way.
133
+ path = Pathname.new(path) unless path.is_a? Pathname
134
+
135
+ case path.ftype
136
+ when "directory" then raise Errno::EISDIR
137
+ when "file" then # do nothing
138
+ when "link"
139
+ # we use path.realpath here; the reason is that if
140
+ # we're to use readlink we're going to have a lot of
141
+ # trouble to find the correct path. We cannot just
142
+ # always use realpath as that will run too many stat
143
+ # calls and have a huge hit on performance.
144
+ _checkvalidpath(path.realpath)
145
+ else
146
+ raise Errno::EINVAL
147
+ end
148
+ end
149
+
150
+ private :_checkvalidpath
151
+
152
+ def initialize(path)
153
+ _checkvalidpath(path)
154
+
155
+ super(path, "rb")
156
+
157
+ begin
158
+ begin
159
+ raise NotAnELF unless readexactly(4) == MagicString
160
+ rescue EOFError
161
+ raise NotAnELF
162
+ end
163
+
164
+ begin
165
+ @elf_class = Class[read_u8]
166
+ rescue Value::OutOfBound => e
167
+ raise InvalidElfClass.new(e.val)
168
+ end
169
+
170
+ begin
171
+ @data_encoding = DataEncoding[read_u8]
172
+ rescue Value::OutOfBound => e
173
+ raise InvalidDataEncoding.new(e.val)
174
+ end
175
+
176
+ @version = read_u8
177
+ raise UnsupportedElfVersion.new(@version) if @version > 1
178
+
179
+ begin
180
+ @abi = OsAbi[read_u8]
181
+ rescue Value::OutOfBound => e
182
+ raise InvalidOsAbi.new(e.val)
183
+ end
184
+ @abi_version = read_u8
185
+
186
+ seek(16, IO::SEEK_SET)
187
+ set_endian(DataEncoding::BytestreamMapping[@data_encoding])
188
+
189
+ begin
190
+ @type = Type[read_half]
191
+ rescue Value::OutOfBound => e
192
+ raise InvalidElfType.new(e.val)
193
+ end
194
+
195
+ begin
196
+ @machine = Machine[read_half]
197
+ rescue Value::OutOfBound => e
198
+ raise InvalidMachine.new(e.val)
199
+ end
200
+
201
+ @version = read_word
202
+ @entry_address = read_addr
203
+ @phoff = read_off
204
+ @shoff = read_off
205
+ @flags = read_word
206
+ @ehsize = read_half
207
+ @phentsize = read_half
208
+ @phnum = read_half
209
+ @shentsize = read_half
210
+ @shnum = read_half
211
+ @shstrndx = read_half
212
+
213
+ elf32 = elf_class == Class::Elf32
214
+ @sections = {}
215
+
216
+ @sections_data = []
217
+ seek(@shoff)
218
+ for i in 1..@shnum
219
+ sectdata = {}
220
+ sectdata[:idx] = i-1
221
+ sectdata[:name_idx] = read_word
222
+ sectdata[:type_id] = read_word
223
+ sectdata[:flags_val] = elf32 ? read_word : read_xword
224
+ sectdata[:addr] = read_addr
225
+ sectdata[:offset] = read_off
226
+ sectdata[:size] = elf32 ? read_word : read_xword
227
+ sectdata[:link] = read_word
228
+ sectdata[:info] = read_word
229
+ sectdata[:addralign] = elf32 ? read_word : read_xword
230
+ sectdata[:entsize] = elf32 ? read_word : read_xword
231
+
232
+ @sections_data << sectdata
233
+ end
234
+
235
+ # When the section header string table index is set to zero,
236
+ # there is not going to be a string table in the file, this
237
+ # happens usually when the file is a static ELF file built
238
+ # directly with an assembler, or when it was passed through
239
+ # the elfkickers' sstrip utility.
240
+ #
241
+ # To handle this specific case, set the @string_table attribute
242
+ # to false, that is distinct from nil, and raise
243
+ # MissingStringTable on request. If the string table is not yet
244
+ # loaded raise instead StringTableNotLoaded.
245
+ if @shstrndx == 0 or not self[@shstrndx].is_a? StringTable
246
+ @string_table = false
247
+ else
248
+ @string_table = self[@shstrndx]
249
+
250
+ @sections_names = {}
251
+ @sections_data.each do |sectdata|
252
+ @sections_names[@string_table[sectdata[:name_idx]]] = sectdata[:idx]
253
+ end
254
+ end
255
+ rescue ::Exception => e
256
+ close
257
+ raise e
258
+ end
259
+ end
260
+
261
+ class MissingSection < Exception
262
+ def initialize(sect_identifier)
263
+ super("Requested section #{sect_identifier} not found in the file")
264
+ end
265
+ end
266
+
267
+ def load_section(sect_idx_or_name)
268
+ if sect_idx_or_name.is_a? Integer
269
+ raise MissingSection.new(sect_idx_or_name) unless
270
+ @sections_data[sect_idx_or_name]
271
+
272
+ @sections[sect_idx_or_name] = Section.read(self, sect_idx_or_name, @sections_data[sect_idx_or_name])
273
+ else
274
+ raise MissingSection.new(sect_idx_or_name) unless
275
+ @sections_names[sect_idx_or_name]
276
+
277
+ load_section @sections_names[sect_idx_or_name]
278
+
279
+ @sections[sect_idx_or_name] = @sections[@sections_names[sect_idx_or_name]]
280
+ end
281
+ end
282
+
283
+ class StringTableNotLoaded < Exception
284
+ def initialize(sect_name)
285
+ super("Requested section '#{sect_name}' but there is no string table yet.")
286
+ end
287
+ end
288
+
289
+ class MissingStringTable < Exception
290
+ def initialize(sect_name)
291
+ super("Requested section '#{sect_name}' but the file has no string table.")
292
+ end
293
+ end
294
+
295
+ def sections
296
+ return @sections_data.size
297
+ end
298
+
299
+ def [](sect_idx_or_name)
300
+ if sect_idx_or_name.is_a? String and not @string_table.is_a? Elf::Section
301
+ raise MissingStringTable.new(sect_idx_or_name) if @string_table == false
302
+ raise StringTableNotLoaded.new(sect_idx_or_name) if @string_table.nil?
303
+ end
304
+
305
+ load_section(sect_idx_or_name) unless
306
+ @sections.has_key? sect_idx_or_name
307
+
308
+ return @sections[sect_idx_or_name]
309
+ end
310
+
311
+ def each_section
312
+ @sections_data.each do |sectdata|
313
+ load_section(sectdata[:idx])
314
+ yield @sections[sectdata[:idx]]
315
+ end
316
+ end
317
+
318
+ def find_section_by_addr(addr)
319
+ @sections_data.each do |sectdata|
320
+ next unless sectdata[:addr] == addr
321
+ load_section(sectdata[:idx])
322
+ return @sections[sectdata[:idx]]
323
+ end
324
+ end
325
+
326
+ def has_section?(sect_idx_or_name)
327
+
328
+ if sect_idx_or_name.is_a? String and not @string_table.is_a? Elf::Section
329
+ raise MissingStringTable.new(sect_idx_or_name) if @string_table == false
330
+ raise StringTableNotLoaded.new(sect_idx_or_name) if @string_table.nil?
331
+ end
332
+
333
+ if sect_idx_or_name.is_a? Integer
334
+ return @sections_data[sect_idx_or_name] != nil
335
+ elsif sect_idx_or_name.is_a? String
336
+ return @sections_names.has_key?(sect_idx_or_name)
337
+ else
338
+ raise TypeError.new("wrong argument type #{sect_idx_or_name.class} (expected String or Integer)")
339
+ end
340
+ end
341
+
342
+ # Returns the hex address size for the file.
343
+ #
344
+ # Since each ELF file uses either 32- or 64-bit addresses, it is
345
+ # important to know how many characters a file's address would
346
+ # require when printed as an hexadecimal string.
347
+ def address_print_size
348
+ (@elf_class == Elf::Class::Elf32 ? 8 : 16)
349
+ end
350
+
351
+ def summary
352
+ $stdout.puts "ELF file #{path}"
353
+ $stdout.puts "ELF class: #{@elf_class} #{@data_encoding} ver. #{@version}"
354
+ $stdout.puts "ELF ABI: #{@abi} ver. #{@abi_version}"
355
+ $stdout.puts "ELF type: #{@type} machine: #{@machine}"
356
+ $stdout.puts "Sections:"
357
+ @sections.values.uniq.each do |sh|
358
+ sh.summary
359
+ end
360
+
361
+ return nil
362
+ end
363
+
364
+ # Checks whether two ELF files are compatible one with the other for linking
365
+ #
366
+ # This function has to check whether two ELF files can be linked
367
+ # together (either at build time or at load time), and thus checks
368
+ # for class, encoding, versioning, ABI and machine type.
369
+ #
370
+ # Note that it explicitly does not check for ELF file type since
371
+ # you can link different type of files together, like an
372
+ # Executable with a Dynamic library.
373
+ def is_compatible(other)
374
+ raise TypeError.new("wrong argument type #{other.class} (expected Elf::File)") unless
375
+ other.is_a? Elf::File
376
+
377
+ compatible_abi = (@abi.linux_compatible? && other.abi.linux_compatible?) \
378
+ || ([@abi, @abi_version] == [other.abi, other.abi_version])
379
+
380
+ @elf_class == other.elf_class and
381
+ @data_encoding == other.data_encoding and
382
+ @version == other.version and
383
+ @machine == other.machine and
384
+ compatible_abi
385
+ end
386
+
387
+ # Constants used for ARM-architecture files, as described by the
388
+ # official documentation, "ELF for the ARM® Architecture", 28
389
+ # October 2009.
390
+ module ARM
391
+ EFlags_EABI_Mask = 0xFF000000
392
+ EFlags_BE8 = 0x00800000
393
+ end
394
+
395
+ def arm_eabi_version
396
+ return nil if machine != Elf::Machine::ARM
397
+
398
+ return (@flags & ARM::EFlags_EABI_Mask) >> 24
399
+ end
400
+
401
+ def arm_be8?
402
+ return nil if machine != Elf::Machine::ARM
403
+
404
+ return (@flags & ARM::EFlags_BE8) == ARM::EFlags_BE8
405
+ end
406
+ end
407
+ end