ruby-elf 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +339 -0
- data/DONATING +42 -0
- data/bin/cowstats +264 -0
- data/bin/elfgrep +185 -0
- data/bin/missingstatic +112 -0
- data/bin/rbelf-size +123 -0
- data/bin/verify-lfs +120 -0
- data/extras/README.extras +5 -0
- data/extras/bindings-parsers.rb +157 -0
- data/lib/bytestream-reader.rb +271 -0
- data/lib/elf.rb +248 -0
- data/lib/elf/dynamic.rb +392 -0
- data/lib/elf/file.rb +366 -0
- data/lib/elf/gnu.rb +174 -0
- data/lib/elf/section.rb +321 -0
- data/lib/elf/stringtable.rb +49 -0
- data/lib/elf/sunw.rb +158 -0
- data/lib/elf/symbol.rb +368 -0
- data/lib/elf/symbol/demangler_gcc3.rb +1952 -0
- data/lib/elf/symboltable.rb +90 -0
- data/lib/elf/tools.rb +228 -0
- data/lib/elf/utils/loader.rb +112 -0
- data/lib/elf/utils/pool.rb +37 -0
- data/lib/elf/value.rb +128 -0
- data/manpages/cowstats.1 +180 -0
- data/manpages/elfgrep.1 +188 -0
- data/manpages/missingstatic.1 +176 -0
- data/manpages/rbelf-size.1 +186 -0
- data/manpages/verify-lfs.1 +95 -0
- data/tools/assess_duplicate_save.rb +105 -0
- data/tools/link-collisions/analyse.rb +57 -0
- data/tools/link-collisions/harvest.rb +367 -0
- data/tools/link-collisions/known-broken +165 -0
- data/tools/link-collisions/multimplementations +125 -0
- data/tools/link-collisions/suppress.rb +84 -0
- data/tools/link-collisions/suppressions +279 -0
- data/tools/nm.rb +78 -0
- data/tools/rbelf-lddtree.rb +49 -0
- data/tools/readelf-d.rb +76 -0
- metadata +114 -0
data/lib/elf/dynamic.rb
ADDED
@@ -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
|
data/lib/elf/file.rb
ADDED
@@ -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
|