ruby-elf 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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/bin/elfgrep
CHANGED
@@ -18,169 +18,190 @@
|
|
18
18
|
|
19
19
|
require 'elf/tools'
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
21
|
+
module Elf::Tools
|
22
|
+
class ElfGrep < Elf::Tool
|
23
|
+
def self.initialize
|
24
|
+
super
|
25
|
+
|
26
|
+
@options |= [
|
27
|
+
# Expression to match on the symbol name
|
28
|
+
["--regexp", "-e", GetoptLong::REQUIRED_ARGUMENT],
|
29
|
+
# Append the version information when matching the symbol
|
30
|
+
# name
|
31
|
+
["--match-version", "-V", GetoptLong::NO_ARGUMENT],
|
32
|
+
# Don't match undefined symbols
|
33
|
+
["--match-undefined", "-U", GetoptLong::NO_ARGUMENT],
|
34
|
+
# Don't match defined symbols
|
35
|
+
["--match-defined", "-D", GetoptLong::NO_ARGUMENT],
|
36
|
+
# Invert selection, show symbols not matching the
|
37
|
+
# expression
|
38
|
+
["--invert-match", "-v", GetoptLong::NO_ARGUMENT],
|
39
|
+
# List only files with matches
|
40
|
+
["--files-with-matches", "-l", GetoptLong::NO_ARGUMENT],
|
41
|
+
# List only files without match
|
42
|
+
["--files-without-match", "-L", GetoptLong::NO_ARGUMENT],
|
43
|
+
# Print the name of the file for each match
|
44
|
+
["--with-filename", "-H", GetoptLong::NO_ARGUMENT],
|
45
|
+
# Don't print the name of the file for each match
|
46
|
+
["--no-filename", "-h", GetoptLong::NO_ARGUMENT],
|
47
|
+
# Only output matches' count
|
48
|
+
["--count", "-c", GetoptLong::NO_ARGUMENT],
|
49
|
+
# Match fixed strings and not regular expressions
|
50
|
+
["--fixed-strings", "-F", GetoptLong::NO_ARGUMENT],
|
51
|
+
# Make elfgrep case-insensitive
|
52
|
+
["--ignore-case", "-i", GetoptLong::NO_ARGUMENT],
|
53
|
+
# Use NULLs to terminate filenames
|
54
|
+
["--null", "-Z", GetoptLong::NO_ARGUMENT],
|
55
|
+
]
|
56
|
+
|
57
|
+
@invert_match = false
|
58
|
+
@show = :full_match
|
59
|
+
@match = :regexp
|
60
|
+
@match_symbols = nil
|
61
|
+
|
62
|
+
@patterns = []
|
63
|
+
end
|
60
64
|
|
61
|
-
|
62
|
-
|
63
|
-
|
65
|
+
# We define callbacks for some behaviour-changing options as those
|
66
|
+
# will let us consider them positionally, similar to what grep(1)
|
67
|
+
# does. If you try -lLl and similar combinations on grep(1), the last
|
68
|
+
# one passed is the one to be considered.
|
64
69
|
|
65
|
-
def self.
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
70
|
+
def self.match_undefined_cb
|
71
|
+
if @match_symbols == :defined
|
72
|
+
@match_symbols = :all
|
73
|
+
else
|
74
|
+
@match_symbols = :undefined
|
75
|
+
end
|
76
|
+
end
|
72
77
|
|
73
|
-
def self.
|
74
|
-
|
75
|
-
|
78
|
+
def self.match_defined_cb
|
79
|
+
if @match_symbols == :undefined
|
80
|
+
@match_symbols = :all
|
81
|
+
else
|
82
|
+
@match_symbols = :defined
|
83
|
+
end
|
84
|
+
end
|
76
85
|
|
77
|
-
def self.
|
78
|
-
|
79
|
-
end
|
86
|
+
def self.fixed_strings_cb
|
87
|
+
@match = :fixed_strings
|
88
|
+
end
|
80
89
|
|
81
|
-
def self.
|
82
|
-
|
83
|
-
|
84
|
-
@match_defined = true
|
85
|
-
@show = :full_match
|
86
|
-
@match = :regexp
|
90
|
+
def self.files_with_matches_cb
|
91
|
+
@show = :files_with_matches
|
92
|
+
end
|
87
93
|
|
88
|
-
|
89
|
-
|
94
|
+
def self.files_without_match_cb
|
95
|
+
@show = :files_without_match
|
96
|
+
end
|
90
97
|
|
91
|
-
def self.
|
92
|
-
|
93
|
-
|
94
|
-
exit -1
|
95
|
-
end
|
98
|
+
def self.with_filename_cb
|
99
|
+
@print_filename = true
|
100
|
+
end
|
96
101
|
|
97
|
-
|
98
|
-
|
102
|
+
def self.no_filename_cb
|
103
|
+
@print_filename = false
|
104
|
+
end
|
99
105
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
# to be given by stdin, and that won't include the actual pattern,
|
104
|
-
# if we have an @-prefixed argument, that's a list of targets and
|
105
|
-
# can't include the pattern either.
|
106
|
-
if (@patterns.size == 0 and
|
107
|
-
ARGV.size >= 1 and
|
108
|
-
ARGV[0] !~ /^@/)
|
106
|
+
def self.regexp_cb(pattern)
|
107
|
+
@patterns << pattern
|
108
|
+
end
|
109
109
|
|
110
|
-
|
111
|
-
|
110
|
+
def self.after_options
|
111
|
+
if @no_match_undefined and @no_match_defined
|
112
|
+
puterror "you need to match at least defined or undefined symbols"
|
113
|
+
exit -1
|
114
|
+
end
|
112
115
|
|
113
|
-
|
114
|
-
|
115
|
-
exit -1
|
116
|
-
end
|
116
|
+
@match_undefined = @match_symbols != :defined
|
117
|
+
@match_defined = @match_symbols != :undefined
|
117
118
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
}
|
128
|
-
else
|
129
|
-
Regexp.new(pattern, regexp_options)
|
130
|
-
end
|
131
|
-
}.flatten)
|
132
|
-
end
|
119
|
+
# if we don't have listed patterns yet (i.e. no -e option), let's
|
120
|
+
# check whether we have at least one argument and if that argument
|
121
|
+
# doesn't start with '@'. If we have no arguments, the targets are
|
122
|
+
# to be given by stdin, and that won't include the actual pattern,
|
123
|
+
# if we have an @-prefixed argument, that's a list of targets and
|
124
|
+
# can't include the pattern either.
|
125
|
+
if (@patterns.size == 0 and
|
126
|
+
@targets.size >= 1 and
|
127
|
+
@targets[0] !~ /^@/)
|
133
128
|
|
134
|
-
|
135
|
-
|
136
|
-
file_list = sprintf(@file_list_fmt, file)
|
129
|
+
@patterns << @targets.delete_at(0)
|
130
|
+
end
|
137
131
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
(elf[table].class == Elf::SymbolTable)
|
142
|
-
rescue Elf::File::MissingSection
|
143
|
-
false
|
132
|
+
if @patterns.size == 0
|
133
|
+
puterror "you need to provide at least expression"
|
134
|
+
exit -1
|
144
135
|
end
|
145
|
-
end
|
146
136
|
|
147
|
-
|
148
|
-
|
149
|
-
|
137
|
+
@print_filename = (@recursive or @targets.size > 1) if @print_filename.nil?
|
138
|
+
@file_prefix_fmt = @print_filename ? "%s#{@null ? "\0" : ":"} " : ""
|
139
|
+
@file_list_fmt = "%s#{@null ? "\0" : "\n"}"
|
140
|
+
|
141
|
+
regexp_options = @ignore_case ? Regexp::IGNORECASE : 0
|
142
|
+
@regexp = Regexp.union(@patterns.collect { |pattern|
|
143
|
+
if @match == :fixed_strings
|
144
|
+
pattern.split(/\r?\n/).collect { |string|
|
145
|
+
Regexp.new(Regexp.escape(string), regexp_options)
|
146
|
+
}
|
147
|
+
else
|
148
|
+
Regexp.new(pattern, regexp_options)
|
149
|
+
end
|
150
|
+
}.flatten)
|
150
151
|
end
|
151
152
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
153
|
+
def self.analysis(file)
|
154
|
+
file_prefix = sprintf(@file_prefix_fmt, file)
|
155
|
+
file_list = sprintf(@file_list_fmt, file)
|
156
|
+
|
157
|
+
Elf::File.open(file) do |elf|
|
158
|
+
table = [".dynsym", ".symtab"].find do |table|
|
159
|
+
begin
|
160
|
+
(elf[table].class == Elf::SymbolTable)
|
161
|
+
rescue Elf::File::MissingSection, Elf::File::MissingStringTable
|
162
|
+
false
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
if table.nil?
|
167
|
+
putnotice "#{file}: unable to find symbol table"
|
168
|
+
return
|
169
|
+
end
|
170
|
+
|
171
|
+
matches = 0
|
172
|
+
elf[table].each do |symbol|
|
173
|
+
next if
|
174
|
+
(symbol.section == Elf::Section::Abs) or
|
175
|
+
(symbol.name == '') or
|
176
|
+
(symbol.section == Elf::Section::Undef and
|
177
|
+
not @match_undefined) or
|
178
|
+
(symbol.section != Elf::Section::Undef and
|
179
|
+
not @match_defined)
|
180
|
+
|
181
|
+
symname = symbol.name
|
182
|
+
symname += "@#{symbol.version}" if @match_version
|
183
|
+
|
184
|
+
# We don't care where it matches, but we do care that it matches
|
185
|
+
# or not; we use an invert match since we have to further compare
|
186
|
+
# that to @invert_match
|
187
|
+
if (@invert_match == (@regexp =~ symname).nil?)
|
188
|
+
matches = matches+1
|
189
|
+
|
190
|
+
break unless @show == :full_match
|
191
|
+
next if @count
|
192
|
+
|
193
|
+
puts "#{file_prefix}#{symbol.address_string} #{symbol.nm_code rescue '?'} #{symname}"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
if @show == :files_with_matches
|
198
|
+
puts file_list if matches > 0
|
199
|
+
elsif @show == :files_without_match
|
200
|
+
puts file_list if matches == 0
|
201
|
+
elsif @count
|
202
|
+
puts "#{file_prefix}#{matches}"
|
203
|
+
end
|
175
204
|
end
|
176
205
|
end
|
177
|
-
|
178
|
-
if @show == :files_with_matches
|
179
|
-
puts file_list if matches > 0
|
180
|
-
elsif @show == :files_without_match
|
181
|
-
puts file_list if matches == 0
|
182
|
-
elsif @count
|
183
|
-
puts "#{file_prefix}#{matches}"
|
184
|
-
end
|
185
206
|
end
|
186
207
|
end
|
data/bin/missingstatic
CHANGED
@@ -20,93 +20,94 @@
|
|
20
20
|
|
21
21
|
require 'elf/tools'
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
23
|
+
module Elf::Tools
|
24
|
+
class MissingStatic < Elf::Tool
|
25
|
+
def self.initialize
|
26
|
+
super
|
27
|
+
@options |= [
|
28
|
+
# Exclude functions with a given prefix (exported functions)
|
29
|
+
["--exclude-regexp", "-x", GetoptLong::REQUIRED_ARGUMENT],
|
30
|
+
# Only scan hidden symbols, ignore exported ones
|
31
|
+
["--hidden-only", "-h", GetoptLong::NO_ARGUMENT],
|
32
|
+
# Show the type of symbol (function, variable, constant)
|
33
|
+
["--show-type", "-t", GetoptLong::NO_ARGUMENT],
|
34
|
+
# Exclude symbols present in a tags file (from exuberant-ctags)
|
35
|
+
["--exclude-tags", "-X", GetoptLong::REQUIRED_ARGUMENT]
|
36
|
+
]
|
33
37
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
# List of patterns to exclude; this include all the used
|
39
|
+
# symbols, the entrypoints to the system's dynamic loader, and
|
40
|
+
# those provided by the user as to be ignored.
|
41
|
+
#
|
42
|
+
# For technical limits, we have two arrays, one includes all the
|
43
|
+
# regular expressions given by the user, the other is a list of
|
44
|
+
# full strings matching.
|
45
|
+
@exclude_patterns = []
|
46
|
+
@exclude_symbols = Set.new([ "main" ])
|
41
47
|
|
42
|
-
|
43
|
-
|
44
|
-
end
|
48
|
+
@hidden_only = false
|
49
|
+
@show_type = false
|
45
50
|
|
46
|
-
|
47
|
-
|
48
|
-
# reporting it is pointless as it will always be a false
|
49
|
-
# positive. It cannot be marked static.
|
50
|
-
#
|
51
|
-
# The excluded_names variable will contain also all the used symbols
|
52
|
-
@exclude_names = ["main"]
|
53
|
-
@exclude_regexps = []
|
54
|
-
@hidden_only = false
|
55
|
-
@show_type = false
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.after_options
|
59
|
-
@all_defined = []
|
60
|
-
end
|
51
|
+
@all_defined = []
|
52
|
+
end
|
61
53
|
|
62
|
-
def self.
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
54
|
+
def self.exclude_tags_cb(arg)
|
55
|
+
@exclude_symbols |= File.readlines(arg).delete_if do |line|
|
56
|
+
line[0..0] == '!' # Internal exuberant-ctags symbol
|
57
|
+
end.collect do |line|
|
58
|
+
line.split[0]
|
59
|
+
end
|
67
60
|
end
|
68
|
-
|
69
|
-
|
70
|
-
|
61
|
+
|
62
|
+
def self.exclude_regexp_cb(arg)
|
63
|
+
@exclude_patterns << Regexp.new(arg)
|
71
64
|
end
|
72
65
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
66
|
+
def self.analysis(filename)
|
67
|
+
Elf::File.open(filename) do |elf|
|
68
|
+
if elf.type != Elf::File::Type::Rel
|
69
|
+
putnotice "#{file}: not an object file"
|
70
|
+
next
|
71
|
+
end
|
72
|
+
unless elf.has_section?('.symtab')
|
73
|
+
putnotice "#{file}: no .symtab section found"
|
74
|
+
next
|
75
|
+
end
|
83
76
|
|
84
|
-
|
77
|
+
# Gather all the symbols, defined and missing in the translation unit
|
78
|
+
elf['.symtab'].each do |sym|
|
79
|
+
if sym.section == Elf::Section::Undef
|
80
|
+
@exclude_symbols << sym.name
|
81
|
+
elsif sym.bind == Elf::Symbol::Binding::Local
|
82
|
+
next
|
83
|
+
elsif (sym.section.is_a? Elf::Section) or
|
84
|
+
(sym.section == Elf::Section::Common)
|
85
|
+
next if @hidden_only and
|
86
|
+
sym.visibility != Elf::Symbol::Visibility::Hidden
|
87
|
+
|
88
|
+
@all_defined << sym
|
89
|
+
end
|
90
|
+
end
|
85
91
|
end
|
86
92
|
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def self.results
|
91
|
-
@exclude_names.uniq!
|
92
93
|
|
93
|
-
|
94
|
-
|
94
|
+
def self.results
|
95
|
+
@exclude_patterns << Regexp.union(@exclude_symbols.collect { |sym| /^#{Regexp.escape(sym)}$/ })
|
96
|
+
exclusions = Regexp.union(@exclude_patterns)
|
95
97
|
|
96
|
-
|
97
|
-
|
98
|
-
break if excluded = exclude_sym.match(symbol.name)
|
99
|
-
end
|
100
|
-
next if excluded
|
98
|
+
@all_defined.each do |symbol|
|
99
|
+
next if symbol.name =~ exclusions
|
101
100
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
101
|
+
if @show_type
|
102
|
+
begin
|
103
|
+
prefix = "#{symbol.nm_code} "
|
104
|
+
rescue Elf::Symbol::UnknownNMCode => e
|
105
|
+
puterror e.message
|
106
|
+
prefix = "? "
|
107
|
+
end
|
108
|
+
end
|
109
|
+
puts "#{prefix}#{symbol.name} (#{symbol.file.path})"
|
108
110
|
end
|
109
111
|
end
|
110
|
-
puts "#{prefix}#{symbol.name} (#{symbol.file.path})"
|
111
112
|
end
|
112
113
|
end
|