ruby-elf 1.0.3 → 1.0.4
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.
- data/bin/cowstats +217 -215
- data/bin/elfgrep +165 -144
- data/bin/missingstatic +74 -73
- data/bin/rbelf-read +111 -0
- data/bin/rbelf-size +144 -107
- data/bin/verify-lfs +92 -87
- data/lib/elf.rb +1 -1
- data/lib/elf/dynamic.rb +1 -1
- data/lib/elf/file.rb +30 -3
- data/lib/elf/gnu.rb +3 -3
- data/lib/elf/section.rb +85 -40
- data/lib/elf/symbol.rb +19 -13
- data/lib/elf/symboltable.rb +1 -1
- data/lib/elf/tools.rb +198 -164
- data/manpages/cowstats.1 +1 -4
- data/manpages/elfgrep.1 +10 -9
- data/manpages/elfgrep.1.xml +9 -7
- data/manpages/missingstatic.1 +1 -4
- data/manpages/rbelf-size.1 +13 -15
- data/manpages/rbelf-size.1.xml +25 -14
- data/manpages/verify-lfs.1 +1 -4
- data/tools/link-collisions/harvest.rb +262 -297
- data/tools/link-collisions/multimplementations +17 -3
- data/tools/link-collisions/suppressions +30 -26
- metadata +6 -6
- data/tools/link-collisions/analyse.rb +0 -57
- data/tools/readelf-d.rb +0 -79
data/lib/elf.rb
CHANGED
data/lib/elf/dynamic.rb
CHANGED
data/lib/elf/file.rb
CHANGED
@@ -87,6 +87,9 @@ module Elf
|
|
87
87
|
:abi_version, :machine
|
88
88
|
attr_reader :string_table
|
89
89
|
|
90
|
+
# raw data access
|
91
|
+
attr_reader :shoff
|
92
|
+
|
90
93
|
def read_addr
|
91
94
|
case @elf_class
|
92
95
|
when Class::Elf32 then read_u32
|
@@ -196,7 +199,7 @@ module Elf
|
|
196
199
|
@version = read_word
|
197
200
|
@entry = read_addr
|
198
201
|
@phoff = read_off
|
199
|
-
shoff = read_off
|
202
|
+
@shoff = read_off
|
200
203
|
@flags = read_word
|
201
204
|
@ehsize = read_half
|
202
205
|
@phentsize = read_half
|
@@ -209,7 +212,7 @@ module Elf
|
|
209
212
|
@sections = {}
|
210
213
|
|
211
214
|
@sections_data = []
|
212
|
-
seek(shoff)
|
215
|
+
seek(@shoff)
|
213
216
|
for i in 1..shnum
|
214
217
|
sectdata = {}
|
215
218
|
sectdata[:idx] = i-1
|
@@ -264,7 +267,7 @@ module Elf
|
|
264
267
|
raise MissingSection.new(sect_idx_or_name) unless
|
265
268
|
@sections_data[sect_idx_or_name]
|
266
269
|
|
267
|
-
@sections[sect_idx_or_name] = Section.read(self, @sections_data[sect_idx_or_name])
|
270
|
+
@sections[sect_idx_or_name] = Section.read(self, sect_idx_or_name, @sections_data[sect_idx_or_name])
|
268
271
|
else
|
269
272
|
raise MissingSection.new(sect_idx_or_name) unless
|
270
273
|
@sections_names[sect_idx_or_name]
|
@@ -287,6 +290,10 @@ module Elf
|
|
287
290
|
end
|
288
291
|
end
|
289
292
|
|
293
|
+
def sections
|
294
|
+
return @sections_data.size
|
295
|
+
end
|
296
|
+
|
290
297
|
def [](sect_idx_or_name)
|
291
298
|
if sect_idx_or_name.is_a? String and not @string_table.is_a? Elf::Section
|
292
299
|
raise MissingStringTable.new(sect_idx_or_name) if @string_table == false
|
@@ -363,5 +370,25 @@ module Elf
|
|
363
370
|
@abi_version == other.abi_version and
|
364
371
|
@machine == other.machine
|
365
372
|
end
|
373
|
+
|
374
|
+
# Constants used for ARM-architecture files, as described by the
|
375
|
+
# official documentation, "ELF for the ARM® Architecture", 28
|
376
|
+
# October 2009.
|
377
|
+
module ARM
|
378
|
+
EFlags_EABI_Mask = 0xFF000000
|
379
|
+
EFlags_BE8 = 0x00800000
|
380
|
+
end
|
381
|
+
|
382
|
+
def arm_eabi_version
|
383
|
+
return nil if machine != Elf::Machine::ARM
|
384
|
+
|
385
|
+
return (flags & ARM::EFlags_EABI_Mask) >> 24
|
386
|
+
end
|
387
|
+
|
388
|
+
def arm_be8?
|
389
|
+
return nil if machine != Elf::Machine::ARM
|
390
|
+
|
391
|
+
return (flags & ARM::EFlags_EB8) == ARM::EFlags_EB8
|
392
|
+
end
|
366
393
|
end
|
367
394
|
end
|
data/lib/elf/gnu.rb
CHANGED
@@ -39,7 +39,7 @@ module Elf
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
42
|
+
def count
|
43
43
|
load unless @versions
|
44
44
|
|
45
45
|
@versions.size
|
@@ -91,7 +91,7 @@ module Elf
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
-
def
|
94
|
+
def count
|
95
95
|
load unless @defined_versions
|
96
96
|
|
97
97
|
@defined_versions.size
|
@@ -150,7 +150,7 @@ module Elf
|
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
153
|
-
def
|
153
|
+
def count
|
154
154
|
load unless @needed_versions
|
155
155
|
|
156
156
|
@needed_versions.size
|
data/lib/elf/section.rb
CHANGED
@@ -39,7 +39,7 @@ module Elf
|
|
39
39
|
Abs = 0xfff1 # Absolute symbols
|
40
40
|
Common = 0xfff2 # Common symbols
|
41
41
|
XIndex = 0xffff
|
42
|
-
|
42
|
+
|
43
43
|
class UnknownType < Exception
|
44
44
|
def initialize(type_id, section_name)
|
45
45
|
@type_id = type_id
|
@@ -55,32 +55,40 @@ module Elf
|
|
55
55
|
# This function assumes that the elf file is aligned ad the
|
56
56
|
# start of a section header, and returns the file moved at the
|
57
57
|
# start of the next header.
|
58
|
-
def Section.read(elf, sectdata)
|
58
|
+
def Section.read(elf, index, sectdata)
|
59
59
|
begin
|
60
60
|
if Type::ProcSpecific.include?(sectdata[:type_id])
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
61
|
+
begin
|
62
|
+
case elf.machine
|
63
|
+
when Elf::Machine::ARM
|
64
|
+
type = Type::ProcARM[sectdata[:type_id]]
|
65
|
+
else
|
66
|
+
type = Type[sectdata[:type_id]]
|
67
|
+
end
|
68
|
+
rescue Value::OutOfBound
|
65
69
|
type = Type[sectdata[:type_id]]
|
66
70
|
end
|
67
71
|
elsif Type::OsSpecific.include?(sectdata[:type_id])
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
72
|
+
begin
|
73
|
+
# Unfortunately, even though OS ABIs are well-defined for both
|
74
|
+
# GNU/Linux and Solaris, they don't seem to get used at all.
|
75
|
+
#
|
76
|
+
# For this reason, instead of basing ourselves on (just) the
|
77
|
+
# OS ABI, the name of the section is used to identify the type
|
78
|
+
# of section to use
|
79
|
+
|
80
|
+
# Don't set the name if there is no string table loaded
|
81
|
+
name = elf.string_table ? elf.string_table[sectdata[:name_idx]] : ""
|
82
|
+
if elf.abi == Elf::OsAbi::Solaris or
|
83
|
+
name =~ /^\.SUNW_/
|
84
|
+
type = Type::SunW[sectdata[:type_id]]
|
85
|
+
elsif elf.abi == Elf::OsAbi::Linux or
|
86
|
+
name =~ /^\.gnu\./
|
87
|
+
type = Type::GNU[sectdata[:type_id]]
|
88
|
+
else
|
89
|
+
type = Type[sectdata[:type_id]]
|
90
|
+
end
|
91
|
+
rescue Value::OutOfBound
|
84
92
|
type = Type[sectdata[:type_id]]
|
85
93
|
end
|
86
94
|
else
|
@@ -96,16 +104,18 @@ module Elf
|
|
96
104
|
) if type.nil?
|
97
105
|
|
98
106
|
if Type::Class[type]
|
99
|
-
return Type::Class[type].new(elf, sectdata, type)
|
107
|
+
return Type::Class[type].new(elf, index, sectdata, type)
|
100
108
|
else
|
101
|
-
return Section.new(elf, sectdata, type)
|
109
|
+
return Section.new(elf, index, sectdata, type)
|
102
110
|
end
|
103
111
|
end
|
104
112
|
|
105
|
-
attr_reader :
|
113
|
+
attr_reader :file, :index, :type, :addr, :offset, :size
|
114
|
+
attr_reader :addralign, :entsize, :info
|
106
115
|
|
107
|
-
def initialize(elf, sectdata, type)
|
116
|
+
def initialize(elf, index, sectdata, type)
|
108
117
|
@file = elf
|
118
|
+
@index = index
|
109
119
|
@type = type
|
110
120
|
@name = sectdata[:name_idx]
|
111
121
|
@flags_val = sectdata[:flags_val]
|
@@ -143,6 +153,7 @@ module Elf
|
|
143
153
|
|
144
154
|
# Alias to_s to name so that using this in a string will report the name
|
145
155
|
alias :to_s :name
|
156
|
+
alias :to_i :index
|
146
157
|
|
147
158
|
def link
|
148
159
|
# We didn't get the linked section header yet
|
@@ -153,18 +164,6 @@ module Elf
|
|
153
164
|
@link
|
154
165
|
end
|
155
166
|
|
156
|
-
# Return a set of flag items, easier to check for single elements.
|
157
|
-
def flags
|
158
|
-
return @flags if @flags
|
159
|
-
|
160
|
-
@flags = Set.new
|
161
|
-
Flags.each do |flag|
|
162
|
-
flags.add(flag) if (@flags_val & flag.val) == flag.val
|
163
|
-
end
|
164
|
-
|
165
|
-
@flags
|
166
|
-
end
|
167
|
-
|
168
167
|
def load
|
169
168
|
oldpos = @file.tell
|
170
169
|
@file.seek(@offset, IO::SEEK_SET)
|
@@ -193,13 +192,55 @@ module Elf
|
|
193
192
|
0x40000000 => [ :Ordered, 'Special ordering requirement' ],
|
194
193
|
0x80000000 => [ :Exclude, 'Section is excluded unless referenced or allocated' ]
|
195
194
|
)
|
196
|
-
|
195
|
+
|
197
196
|
# OS-specific flags mask
|
198
197
|
MaskOS = 0x0ff00000
|
199
198
|
# Processor-specific flags mask
|
200
199
|
MaskProc = 0xf0000000
|
201
200
|
end
|
202
201
|
|
202
|
+
# Return a set of flag items, easier to check for single elements.
|
203
|
+
def flags
|
204
|
+
return @flags if @flags
|
205
|
+
|
206
|
+
@flags = Set.new
|
207
|
+
Flags.each do |flag|
|
208
|
+
flags.add(flag) if (@flags_val & flag.val) == flag.val
|
209
|
+
end
|
210
|
+
|
211
|
+
@flags
|
212
|
+
end
|
213
|
+
|
214
|
+
FlagsToChars = [
|
215
|
+
[Flags::Write, "W"],
|
216
|
+
[Flags::Alloc, "A"],
|
217
|
+
[Flags::ExecInstr, "X"],
|
218
|
+
[Flags::Merge, "M"],
|
219
|
+
[Flags::Strings, "S"],
|
220
|
+
[Flags::InfoLink, "I"],
|
221
|
+
[Flags::LinkOrder, "L"],
|
222
|
+
[Flags::Group, "G"],
|
223
|
+
[Flags::TLS, "T" ],
|
224
|
+
[Flags::Exclude, "E"],
|
225
|
+
]
|
226
|
+
|
227
|
+
def flags_s
|
228
|
+
return @flags_s if @flags_s
|
229
|
+
|
230
|
+
@flags_s = FlagsToChars.collect { |flag, char|
|
231
|
+
flags.include?(flag) ? char : ""
|
232
|
+
}.join
|
233
|
+
|
234
|
+
@flags_s << 'o' if (@flags_val & Flags::MaskOS) != 0
|
235
|
+
@flags_s << 'p' if (@flags_val & Flags::MaskProc) != 0
|
236
|
+
|
237
|
+
return @flags_s
|
238
|
+
end
|
239
|
+
|
240
|
+
def flags_i
|
241
|
+
@flags_val
|
242
|
+
end
|
243
|
+
|
203
244
|
# Return the nm(1) code for the section.
|
204
245
|
#
|
205
246
|
# This function is usually mostly used by Elf::Symbol#nm_code. It
|
@@ -290,7 +331,11 @@ module Elf
|
|
290
331
|
|
291
332
|
class ProcARM < Value
|
292
333
|
fill(
|
293
|
-
|
334
|
+
0x70000001 => [ :ARMExIdx, 'ARM Exception Index' ],
|
335
|
+
0x70000002 => [ :ARMPreemptMap, 'ARM BDAPI DLL dynamic linking pre-emption map' ],
|
336
|
+
0x70000003 => [ :ARMAttributes, 'ARM Attributes' ],
|
337
|
+
0x70000004 => [ :ARMDebugOverlay, 'ARM Debug Overlay' ],
|
338
|
+
0x70000005 => [ :ARMOverlaySection, 'ARM Overlay Table' ]
|
294
339
|
)
|
295
340
|
end
|
296
341
|
|
data/lib/elf/symbol.rb
CHANGED
@@ -21,8 +21,6 @@
|
|
21
21
|
# along with this generator; if not, write to the Free Software
|
22
22
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
23
23
|
|
24
|
-
require 'elf/symbol/demangler_gcc3'
|
25
|
-
|
26
24
|
module Elf
|
27
25
|
class Symbol
|
28
26
|
class Binding < Value
|
@@ -344,20 +342,28 @@ module Elf
|
|
344
342
|
return true
|
345
343
|
end
|
346
344
|
|
347
|
-
|
348
|
-
|
349
|
-
return @demangled if @demangled
|
345
|
+
begin
|
346
|
+
require 'elf/symbol/demangler_gcc3'
|
350
347
|
|
351
|
-
Demanglers
|
352
|
-
|
353
|
-
|
348
|
+
Demanglers = [ Elf::Symbol::Demangler::GCC3 ]
|
349
|
+
def demangle
|
350
|
+
return @demangled if @demangled
|
351
|
+
|
352
|
+
Demanglers.each do |demangler|
|
353
|
+
break if (@demangled ||= demangler.demangle(name))
|
354
|
+
end
|
354
355
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
356
|
+
# We're going to remove top-namespace specifications as we don't
|
357
|
+
# need them, but it's easier for the demangler to still emit
|
358
|
+
# them.
|
359
|
+
@demangled.gsub!(/(^| |\()::/, '\1') if @demangled
|
359
360
|
|
360
|
-
|
361
|
+
return @demangled ||= name
|
362
|
+
end
|
363
|
+
rescue LoadError
|
364
|
+
def demangle
|
365
|
+
return name
|
366
|
+
end
|
361
367
|
end
|
362
368
|
|
363
369
|
private
|
data/lib/elf/symboltable.rb
CHANGED
data/lib/elf/tools.rb
CHANGED
@@ -28,197 +28,231 @@ require 'elf'
|
|
28
28
|
# require arguments (in that latter case they are considered on/off
|
29
29
|
# switches), and so on.
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
module Elf
|
32
|
+
class Tool
|
33
|
+
def self.inherited(klass)
|
34
|
+
raise Exception.new("Another Tool has been already defined") if @tool_defined
|
35
|
+
@tool_defined = true
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
at_exit do
|
38
|
+
unless $!
|
39
|
+
klass.main
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
39
43
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
+
# Gets the name of the tool
|
45
|
+
def self.to_s
|
46
|
+
File.basename($0)
|
47
|
+
end
|
44
48
|
|
45
|
-
# Output
|
46
|
-
|
47
|
-
|
48
|
-
return if @quiet or @recursive
|
49
|
+
# Output an error message, prefixed with the tool name.
|
50
|
+
def self.puterror(string)
|
51
|
+
return if @quiet
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
53
|
+
@output_mutex.synchronize {
|
54
|
+
$stderr.puts "#{to_s}: #{string}"
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
# Output a notice about a file, do not prefix with the tool name, do
|
59
|
+
# not print if doing recursive analysis
|
60
|
+
def self.putnotice(message)
|
61
|
+
return if @quiet or @recursive
|
54
62
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
def self.parse_arguments
|
59
|
-
opts = Options + [
|
60
|
-
["--help", "-?", GetoptLong::NO_ARGUMENT],
|
61
|
-
["--quiet", "-q", GetoptLong::NO_ARGUMENT],
|
62
|
-
["--recursive", "-R", GetoptLong::NO_ARGUMENT],
|
63
|
-
]
|
64
|
-
|
65
|
-
opts = GetoptLong.new(*opts)
|
66
|
-
opts.each do |opt, arg|
|
67
|
-
if opt == "--help"
|
68
|
-
# check if we're executing from a tarball or the git repository,
|
69
|
-
# if so we can't use the system man page.
|
70
|
-
manpage = File.expand_path("../../../manpages/#{to_s}.1", __FILE__)
|
71
|
-
manpage = to_s unless File.exist?(manpage)
|
72
|
-
exec("man #{manpage}")
|
63
|
+
@output_mutex.synchronize {
|
64
|
+
$stderr.puts message
|
65
|
+
}
|
73
66
|
end
|
74
67
|
|
75
|
-
|
76
|
-
attrval = arg.size == 0 ? true : arg
|
77
|
-
|
78
|
-
# If there is a function with the same name of the parameter
|
79
|
-
# defined (with a _cb suffix), call that, otherwise set the
|
80
|
-
# attribute with the same name to the given value.
|
81
|
-
cb = method("#{attrname}_cb") rescue nil
|
82
|
-
case
|
83
|
-
when cb.nil?
|
84
|
-
instance_variable_set("@#{attrname}", attrval)
|
85
|
-
when cb.arity == 0
|
86
|
-
raise ArgumentError("wrong number of arguments in callback (0 for 1)") unless
|
87
|
-
arg.size == 0
|
88
|
-
cb.call
|
89
|
-
when cb.arity == 1
|
90
|
-
# fallback to provide a single "true" parameter if there was no
|
91
|
-
# required argument
|
92
|
-
cb.call(attrval)
|
93
|
-
else
|
94
|
-
raise ArgumentError("wrong number of arguments in callback (#{cb.arity} for #{arg.size})")
|
68
|
+
def self.after_options
|
95
69
|
end
|
96
|
-
end
|
97
70
|
|
98
|
-
|
99
|
-
|
71
|
+
# Parse the arguments for the tool; it does not parse the @file
|
72
|
+
# options, since they are only expected to contain file names,
|
73
|
+
# rather than options.
|
74
|
+
def self.parse_arguments
|
75
|
+
opts = GetoptLong.new(*@options)
|
76
|
+
opts.each do |opt, arg|
|
77
|
+
if opt == "--help"
|
78
|
+
# check if we're executing from a tarball or the git repository,
|
79
|
+
# if so we can't use the system man page.
|
80
|
+
manpage = File.expand_path("../../../manpages/#{to_s}.1", __FILE__)
|
81
|
+
manpage = to_s unless File.exist?(manpage)
|
82
|
+
exec("man #{manpage}")
|
83
|
+
end
|
100
84
|
|
101
|
-
|
102
|
-
|
85
|
+
attrname = opt.gsub(/^--/, "").gsub("-", "_")
|
86
|
+
attrval = arg.size == 0 ? true : arg
|
103
87
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
88
|
+
# If there is a function with the same name of the parameter
|
89
|
+
# defined (with a _cb suffix), call that, otherwise set the
|
90
|
+
# attribute with the same name to the given value.
|
91
|
+
cb = method("#{attrname}_cb") rescue nil
|
92
|
+
case
|
93
|
+
when cb.nil?
|
94
|
+
instance_variable_set("@#{attrname}", attrval)
|
95
|
+
when cb.arity == 0
|
96
|
+
raise ArgumentError("wrong number of arguments in callback (0 for 1)") unless
|
97
|
+
arg.size == 0
|
98
|
+
cb.call
|
99
|
+
when cb.arity == 1
|
100
|
+
# fallback to provide a single "true" parameter if there was no
|
101
|
+
# required argument
|
102
|
+
cb.call(attrval)
|
103
|
+
else
|
104
|
+
raise ArgumentError("wrong number of arguments in callback (#{cb.arity} for #{arg.size})")
|
105
|
+
end
|
106
|
+
end
|
108
107
|
|
109
|
-
|
110
|
-
|
111
|
-
analysis(filename)
|
112
|
-
rescue Errno::ENOENT, Errno::EACCES, Errno::EISDIR, Elf::File::NotAnELF,
|
113
|
-
Elf::File::InvalidElfClass, Elf::File::InvalidDataEncoding,
|
114
|
-
Elf::File::UnsupportedElfVersion, Elf::File::InvalidOsAbi, Elf::File::InvalidElfType,
|
115
|
-
Elf::File::InvalidMachine => e
|
116
|
-
# The Errno exceptions have their message ending in " - FILENAME",
|
117
|
-
# so we take the FILENAME out and just use the one we know
|
118
|
-
# already. We also take out the final dot on the phrase so that
|
119
|
-
# we follow the output messages from other tools, like cat.
|
120
|
-
putnotice "#{filename}: #{e.message.gsub(/\.? - .*/, '')}"
|
121
|
-
rescue Exception => e
|
122
|
-
puterror "#{filename}: #{e.message} (#{e.class})\n\t#{e.backtrace.join("\n\t")}"
|
123
|
-
exit -1
|
124
|
-
end
|
125
|
-
end
|
108
|
+
@parsed_options = true
|
109
|
+
end
|
126
110
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
execute_on_file(filename[1..-1])
|
134
|
-
return
|
111
|
+
def self.single_target?
|
112
|
+
raise Exception.new("You can't call this until options are parsed") unless @parsed_options
|
113
|
+
|
114
|
+
# We consider having a single target means that we're given exactly
|
115
|
+
# one argument, and that argument is not a targets' list itself.
|
116
|
+
return @targets.size == 1
|
135
117
|
end
|
136
118
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
next if children == "." or children == ".."
|
153
|
-
try_execute(File.join(filename, children))
|
119
|
+
def self.execute(filename)
|
120
|
+
begin
|
121
|
+
analysis(filename)
|
122
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::EISDIR, Elf::File::NotAnELF,
|
123
|
+
Elf::File::InvalidElfClass, Elf::File::InvalidDataEncoding,
|
124
|
+
Elf::File::UnsupportedElfVersion, Elf::File::InvalidOsAbi, Elf::File::InvalidElfType,
|
125
|
+
Elf::File::InvalidMachine => e
|
126
|
+
# The Errno exceptions have their message ending in " - FILENAME",
|
127
|
+
# so we take the FILENAME out and just use the one we know
|
128
|
+
# already. We also take out the final dot on the phrase so that
|
129
|
+
# we follow the output messages from other tools, like cat.
|
130
|
+
putnotice "#{filename}: #{e.message.gsub(/\.? - .*/, '')}"
|
131
|
+
rescue Exception => e
|
132
|
+
puterror "#{filename}: #{e.message} (#{e.class})\n\t#{e.backtrace.join("\n\t")}"
|
133
|
+
exit -1
|
154
134
|
end
|
155
|
-
# if the path does not point to a regular file, ignore it
|
156
|
-
elsif not file_stat.file?
|
157
|
-
putnotice "#{filename}: not a regular file"
|
158
|
-
else
|
159
|
-
@execution_threads.add(Thread.new {
|
160
|
-
execute(filename)
|
161
|
-
})
|
162
135
|
end
|
163
|
-
rescue Errno::ENOENT, Errno::EACCES, Errno::EISDIR, Elf::File::NotAnELF => e
|
164
|
-
# The Errno exceptions have their message ending in " - FILENAME",
|
165
|
-
# so we take the FILENAME out and just use the one we know
|
166
|
-
# already. We also take out the final dot on the phrase so that
|
167
|
-
# we follow the output messages from other tools, like cat.
|
168
|
-
putnotice "#{filename}: #{e.message.gsub(/\.? - .*/, '')}"
|
169
|
-
rescue SystemExit => e
|
170
|
-
exit e.status
|
171
|
-
rescue Exception => e
|
172
|
-
puterror "#{filename}: #{e.message} (#{e.class})\n\t#{e.backtrace.join("\n\t")}"
|
173
|
-
exit -1
|
174
|
-
end
|
175
|
-
end
|
176
136
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
137
|
+
def self.thread_execute(filename)
|
138
|
+
# If our child set @execution_threads to nil, it doesn't really
|
139
|
+
# support running multithreaded, this is the case for instance
|
140
|
+
# of the link collision harvester script, where the db access
|
141
|
+
# and pkey generation has to be synchronous.
|
142
|
+
unless @execution_threads.nil?
|
143
|
+
@execution_threads.add(Thread.new {
|
144
|
+
execute(filename)
|
145
|
+
})
|
146
|
+
else
|
147
|
+
execute(filename)
|
148
|
+
end
|
149
|
+
end
|
183
150
|
|
184
|
-
#
|
185
|
-
def self.
|
186
|
-
|
187
|
-
|
151
|
+
# Try to execute the analysis function on a given filename argument.
|
152
|
+
def self.try_execute(filename)
|
153
|
+
begin
|
154
|
+
# find the file type so we don't have to look it up many times; if
|
155
|
+
# we're running a recursive scan, we don't want to look into
|
156
|
+
# symlinks as they might create loops or duplicate content, while
|
157
|
+
# we usually want to check them out if they are given directly in
|
158
|
+
# the list of files to analyse
|
159
|
+
file_stat = if @recursive
|
160
|
+
File.lstat(filename)
|
161
|
+
else
|
162
|
+
File.stat(filename)
|
163
|
+
end
|
188
164
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
165
|
+
# if the path references a directory, and we're going to run
|
166
|
+
# recursively, descend into that.
|
167
|
+
if @recursive and file_stat.directory?
|
168
|
+
Dir.foreach(filename) do |children|
|
169
|
+
next if children == "." or children == ".."
|
170
|
+
try_execute(File.join(filename, children))
|
171
|
+
end
|
172
|
+
# if the path does not point to a regular file, ignore it
|
173
|
+
elsif not file_stat.file?
|
174
|
+
putnotice "#{filename}: not a regular file"
|
175
|
+
else
|
176
|
+
thread_execute(filename)
|
177
|
+
end
|
178
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::EISDIR, Elf::File::NotAnELF => e
|
179
|
+
# The Errno exceptions have their message ending in " - FILENAME",
|
180
|
+
# so we take the FILENAME out and just use the one we know
|
181
|
+
# already. We also take out the final dot on the phrase so that
|
182
|
+
# we follow the output messages from other tools, like cat.
|
183
|
+
putnotice "#{filename}: #{e.message.gsub(/\.? - .*/, '')}"
|
184
|
+
rescue SystemExit => e
|
185
|
+
exit e.status
|
186
|
+
rescue Exception => e
|
187
|
+
puterror "#{filename}: #{e.message} (#{e.class})\n\t#{e.backtrace.join("\n\t")}"
|
188
|
+
exit -1
|
189
|
+
end
|
190
|
+
end
|
193
191
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
192
|
+
# Execute the analysis function on all the elements of an array.
|
193
|
+
def self.execute_on(param)
|
194
|
+
param = ::File.new(param) if param.is_a? String
|
195
|
+
param = param.read.split(/\r?\n/) if param.is_a? IO
|
198
196
|
|
199
|
-
|
200
|
-
|
201
|
-
|
197
|
+
param.each do |filename|
|
198
|
+
try_execute(filename)
|
199
|
+
end
|
200
|
+
end
|
202
201
|
|
203
|
-
|
204
|
-
execute_on_file($stdin)
|
205
|
-
else
|
206
|
-
execute_on_array(ARGV)
|
202
|
+
def self.results
|
207
203
|
end
|
208
204
|
|
209
|
-
|
210
|
-
|
205
|
+
def self.initialize
|
206
|
+
@output_mutex = Mutex.new
|
207
|
+
@execution_threads = ThreadGroup.new
|
208
|
+
|
209
|
+
@options = [
|
210
|
+
["--help", "-?", GetoptLong::NO_ARGUMENT],
|
211
|
+
["--quiet", "-q", GetoptLong::NO_ARGUMENT],
|
212
|
+
["--recursive", "-R", GetoptLong::NO_ARGUMENT],
|
213
|
+
]
|
211
214
|
end
|
212
215
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
216
|
+
def self.main
|
217
|
+
initialize
|
218
|
+
|
219
|
+
begin
|
220
|
+
parse_arguments
|
221
|
+
|
222
|
+
# collect all the arguments passed; if the argument starts
|
223
|
+
# with '@', then open the file and split the lines in further
|
224
|
+
# arguments.
|
225
|
+
@targets = ARGV.collect { |argument|
|
226
|
+
if argument[0..0] == "@"
|
227
|
+
::File.new(argument[1..-1]).read.split(/\r?\n/)
|
228
|
+
else
|
229
|
+
argument
|
230
|
+
end
|
231
|
+
}.flatten
|
232
|
+
|
233
|
+
after_options
|
219
234
|
|
220
|
-
|
221
|
-
|
222
|
-
|
235
|
+
# if we have no targets (neither direct arguments, nor added
|
236
|
+
# by #after_options, we readthe targets from stdin.
|
237
|
+
if @targets.empty?
|
238
|
+
$stdin.each_line { |input|
|
239
|
+
try_execute(input.sub(/\r?\n/, ''))
|
240
|
+
}
|
241
|
+
else
|
242
|
+
@targets.uniq.each { |target| try_execute(target) }
|
243
|
+
end
|
244
|
+
|
245
|
+
if @execution_threads
|
246
|
+
@execution_threads.list.each do |thread|
|
247
|
+
thread.join
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
results
|
252
|
+
rescue Interrupt
|
253
|
+
puterror "Interrupted"
|
254
|
+
exit 1
|
255
|
+
end
|
256
|
+
end
|
223
257
|
end
|
224
258
|
end
|