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/manpages/cowstats.1
CHANGED
@@ -139,10 +139,7 @@ Recursively descend into directories to search for files to scan\&. This affects
|
|
139
139
|
.PP
|
140
140
|
\fB@\fR\fIpath\fR
|
141
141
|
.RS 4
|
142
|
-
Read the list of files to analyse from the given file
|
143
|
-
\fIpath\fR
|
144
|
-
is
|
145
|
-
\-)\&. Useful to pass a long list of files\&. When used with stdin or named pipes, this also allows asynchronous analysis\&.
|
142
|
+
Read the list of files to analyse from the given file; useful to pass a long list of files\&. The files are read before the processing start, so that the list of target files is available\&.
|
146
143
|
.RE
|
147
144
|
.SH "BUGS"
|
148
145
|
.PP
|
data/manpages/elfgrep.1
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
'\" t
|
2
2
|
.\" Title: elfgrep
|
3
3
|
.\" Author:
|
4
|
-
.\" Generator: DocBook XSL-NS Stylesheets v1.76.
|
4
|
+
.\" Generator: DocBook XSL-NS Stylesheets v1.76.1 <http://docbook.sf.net/>
|
5
5
|
.\" Date: January 2011
|
6
6
|
.\" Manual: Reference
|
7
7
|
.\" Source: ruby-elf
|
@@ -31,7 +31,7 @@
|
|
31
31
|
elfgrep \- Search for symbols matching an expression in ELF files
|
32
32
|
.SH "SYNOPSIS"
|
33
33
|
.HP \w'\fBelfgrep\fR\ 'u
|
34
|
-
\fBelfgrep\fR [\fB\-\-fixed\-strings\fR] [\fB\-\-ignore\-case\fR] [\fB\-\-match\-version\fR] [\fB\-\-
|
34
|
+
\fBelfgrep\fR [\fB\-\-fixed\-strings\fR] [\fB\-\-ignore\-case\fR] [\fB\-\-match\-version\fR] [\fB\-\-match\-undefined\fR | \fB\-\-match\-defined\fR] [\fB\-\-invert\-match\fR] [\fB\-\-count\fR] [\fB\-\-files\-without\-match\fR | \fB\-\-files\-with\-matches\fR] [\fB\-\-with\-filename\fR | \fB\-\-no\-filename\fR] [\fB\-\-null\fR] [\fB\-\-quiet\fR] [\fB\-\-recursive\fR] {\fB\-\-regexp\fR\ \fIPATTERN\fR... | \fIPATTERN\fR} [\fB@\fR\fIfile\fR | \fIfile\fR...]
|
35
35
|
.SH "DESCRIPTION"
|
36
36
|
.PP
|
37
37
|
|
@@ -68,14 +68,18 @@ and the symbols\*(Aq names\&.
|
|
68
68
|
Append the ELF version information for the symbol, separated by an @ symbol, before testing the expression for match\&. This allows to match only symbols that are defined with a particular version\&.
|
69
69
|
.RE
|
70
70
|
.PP
|
71
|
-
\fB\-U\fR, \fB\-\-
|
71
|
+
\fB\-U\fR, \fB\-\-match\-undefined\fR
|
72
72
|
.RS 4
|
73
|
-
|
73
|
+
Report matches on undefined symbols\&. By default
|
74
|
+
\fBelfgrep\fR
|
75
|
+
will report matches on both defined and undefined symbols\&. This switch makes it ignore matches on defined symbols\&.
|
74
76
|
.RE
|
75
77
|
.PP
|
76
78
|
\fB\-D\fR, \fB\-\-no\-match\-defined\fR
|
77
79
|
.RS 4
|
78
|
-
|
80
|
+
Report matches on defined symbols\&. By default
|
81
|
+
\fBelfgrep\fR
|
82
|
+
will report matches on both defined and undefined symbols\&. This switch makes it ignore matches on undefined symbols\&.
|
79
83
|
.RE
|
80
84
|
.PP
|
81
85
|
\fB\-v\fR, \fB\-\-invert\-match\fR
|
@@ -137,10 +141,7 @@ Recursively descend into directories to search for files to scan\&. This affects
|
|
137
141
|
.PP
|
138
142
|
\fB@\fR\fIpath\fR
|
139
143
|
.RS 4
|
140
|
-
Read the list of files to analyse from the given file
|
141
|
-
\fIpath\fR
|
142
|
-
is
|
143
|
-
\-)\&. Useful to pass a long list of files\&. When used with stdin or named pipes, this also allows asynchronous analysis\&.
|
144
|
+
Read the list of files to analyse from the given file; useful to pass a long list of files\&. The files are read before the processing start, so that the list of target files is available\&.
|
144
145
|
.RE
|
145
146
|
.SH "BUGS AND MISSING FEATURES"
|
146
147
|
.PP
|
data/manpages/elfgrep.1.xml
CHANGED
@@ -60,11 +60,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
60
60
|
|
61
61
|
<group choice="opt">
|
62
62
|
<arg choice="plain">
|
63
|
-
<option>--
|
63
|
+
<option>--match-undefined</option>
|
64
64
|
</arg>
|
65
65
|
|
66
66
|
<arg choice="plain">
|
67
|
-
<option>--
|
67
|
+
<option>--match-defined</option>
|
68
68
|
</arg>
|
69
69
|
</group>
|
70
70
|
|
@@ -190,11 +190,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
190
190
|
|
191
191
|
<varlistentry>
|
192
192
|
<term><option>-U</option></term>
|
193
|
-
<term><option>--
|
193
|
+
<term><option>--match-undefined</option></term>
|
194
194
|
<listitem>
|
195
195
|
<para>
|
196
|
-
|
197
|
-
|
196
|
+
Report matches on undefined symbols. By default <command>elfgrep</command> will
|
197
|
+
report matches on both defined and undefined symbols. This switch makes it ignore
|
198
|
+
matches on defined symbols.
|
198
199
|
</para>
|
199
200
|
</listitem>
|
200
201
|
</varlistentry>
|
@@ -204,8 +205,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
204
205
|
<term><option>--no-match-defined</option></term>
|
205
206
|
<listitem>
|
206
207
|
<para>
|
207
|
-
|
208
|
-
|
208
|
+
Report matches on defined symbols. By default <command>elfgrep</command> will
|
209
|
+
report matches on both defined and undefined symbols. This switch makes it ignore
|
210
|
+
matches on undefined symbols.
|
209
211
|
</para>
|
210
212
|
</listitem>
|
211
213
|
</varlistentry>
|
data/manpages/missingstatic.1
CHANGED
@@ -100,10 +100,7 @@ Recursively descend into directories to search for files to scan\&. This affects
|
|
100
100
|
.PP
|
101
101
|
\fB@\fR\fIpath\fR
|
102
102
|
.RS 4
|
103
|
-
Read the list of files to analyse from the given file
|
104
|
-
\fIpath\fR
|
105
|
-
is
|
106
|
-
\-)\&. Useful to pass a long list of files\&. When used with stdin or named pipes, this also allows asynchronous analysis\&.
|
103
|
+
Read the list of files to analyse from the given file; useful to pass a long list of files\&. The files are read before the processing start, so that the list of target files is available\&.
|
107
104
|
.RE
|
108
105
|
.SH "EXAMPLES"
|
109
106
|
.SS "Generating the tags file"
|
data/manpages/rbelf-size.1
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
'\" t
|
2
2
|
.\" Title: rbelf-size
|
3
3
|
.\" Author:
|
4
|
-
.\" Generator: DocBook XSL-NS Stylesheets v1.76.
|
4
|
+
.\" Generator: DocBook XSL-NS Stylesheets v1.76.1 <http://docbook.sf.net/>
|
5
5
|
.\" Date: October 2008
|
6
6
|
.\" Manual: Reference
|
7
7
|
.\" Source: ruby-elf
|
@@ -31,7 +31,7 @@
|
|
31
31
|
rbelf-size \- List section sizes of ELF files
|
32
32
|
.SH "SYNOPSIS"
|
33
33
|
.HP \w'\fBrbelf\-size\fR\ 'u
|
34
|
-
\fBrbelf\-size\fR [\fB\-\-relocation\-stats\fR\ [\fB\-\-decibel\fR]] [\fB\-\-quiet\fR] [\fB\-\-recursive\fR] [\fB@\fR\fIfile\fR | \fIfile\fR...]
|
34
|
+
\fBrbelf\-size\fR [\fB\-\-relocation\-stats\fR\ [\fB\-\-decibel\fR]] [\fB\-\-differential\fR] [\fB\-\-quiet\fR] [\fB\-\-recursive\fR] [\fB@\fR\fIfile\fR | \fIfile\fR...]
|
35
35
|
.SH "DESCRIPTION"
|
36
36
|
.PP
|
37
37
|
|
@@ -57,6 +57,11 @@ option, a ratio is displayed between the size of relocated and shared code areas
|
|
57
57
|
Since that ratio can easily skyrocket into thousands if there is very little relocated data, and a lot of shared data, it might be easier to appreciate the results by using a logaritmic scale\&.
|
58
58
|
.RE
|
59
59
|
.PP
|
60
|
+
\fB\-D\fR, \fB\-\-differential\fR
|
61
|
+
.RS 4
|
62
|
+
Provides sizes of multiple arguments as a comparison to the first\&. It is designed to allow comparing multiple versions of the same binary or multiple implementation of the same application to evaluate the overall trend in static memory allocation\&.
|
63
|
+
.RE
|
64
|
+
.PP
|
60
65
|
\fB\-q\fR, \fB\-\-quiet\fR
|
61
66
|
.RS 4
|
62
67
|
Do not output warnings and errors to the standard error\&. Designed to increase the signal\-to\-noise ratio when analysing eterogeneous trees recursively, or when producing output to redirect to automated systems\&.
|
@@ -69,10 +74,7 @@ Recursively descend into directories to search for files to scan\&. This affects
|
|
69
74
|
.PP
|
70
75
|
\fB@\fR\fIpath\fR
|
71
76
|
.RS 4
|
72
|
-
Read the list of files to analyse from the given file
|
73
|
-
\fIpath\fR
|
74
|
-
is
|
75
|
-
\-)\&. Useful to pass a long list of files\&. When used with stdin or named pipes, this also allows asynchronous analysis\&.
|
77
|
+
Read the list of files to analyse from the given file; useful to pass a long list of files\&. The files are read before the processing start, so that the list of target files is available\&.
|
76
78
|
.RE
|
77
79
|
.SH "COMPATIBLE OUTPUT"
|
78
80
|
.PP
|
@@ -115,9 +117,9 @@ overhead
|
|
115
117
|
Total size of sections providing object\*(Aqs metadata for the link editor and dynamic loader\&. These include the symbol and string tables, the version tables and the hash table used during linking and execution\&. These values are usually tied to the amount of symbols exposed by an object, and can easily be reduced by hiding internal, non\-public symbols\&.
|
116
118
|
.RE
|
117
119
|
.PP
|
118
|
-
|
120
|
+
allocated
|
119
121
|
.RS 4
|
120
|
-
|
122
|
+
Sum of the size of all the previously\-shown sections, which shows the actual allocated memory used by the object\&. It is important to note that sections are usually loaded on an alignment of the page size, which on Linux is 4KiB (4096 bytes), so the size of the actual memory reserved for the ELF structures in memory is going to be higher than this number\&.
|
121
123
|
.RE
|
122
124
|
.SH "RELOCATION STATISTICS"
|
123
125
|
.PP
|
@@ -158,13 +160,9 @@ the value will be represented in deciBels, which should make it even easier to u
|
|
158
160
|
.RE
|
159
161
|
.SH "BUGS AND MISSING FEATURES"
|
160
162
|
.PP
|
161
|
-
|
162
|
-
\
|
163
|
-
|
164
|
-
.PP
|
165
|
-
Right now, all the sections are counted in if their flags match, it might be better to limit to allocated sections all over the place\&.
|
166
|
-
.PP
|
167
|
-
The size of the columns is fixed to 8 digits, it might not be enough to fill in enough space for some big ELF files\&.
|
163
|
+
When using the
|
164
|
+
\fB\-\-differential\fR
|
165
|
+
option, analysis of the arguments happens sequentially in a single thread rather than in parallel\&.
|
168
166
|
.PP
|
169
167
|
Parsing of files to provide further arguments (\fB@\fR\fIfile\fR) is not entirely comforming to other tools handling of the same syntax\&. No options are parsed from the file, and filenames are expected to be separated by newlines rather than whitespace\&.
|
170
168
|
.PP
|
data/manpages/rbelf-size.1.xml
CHANGED
@@ -51,6 +51,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
51
51
|
<arg choice="opt"><option>--decibel</option></arg>
|
52
52
|
</arg>
|
53
53
|
|
54
|
+
<arg choice="opt">
|
55
|
+
<option>--differential</option>
|
56
|
+
</arg>
|
57
|
+
|
54
58
|
<xi:include href="common.xmli" xpointer="xpointer(id('filelist.synopsis')/*)" />
|
55
59
|
</cmdsynopsis>
|
56
60
|
</refsynopsisdiv>
|
@@ -102,6 +106,19 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
102
106
|
</listitem>
|
103
107
|
</varlistentry>
|
104
108
|
|
109
|
+
<varlistentry>
|
110
|
+
<term><option>-D</option></term>
|
111
|
+
<term><option>--differential</option></term>
|
112
|
+
|
113
|
+
<listitem>
|
114
|
+
<para>
|
115
|
+
Provides sizes of multiple arguments as a comparison to the first. It is designed to
|
116
|
+
allow comparing multiple versions of the same binary or multiple implementation of
|
117
|
+
the same application to evaluate the overall trend in static memory allocation.
|
118
|
+
</para>
|
119
|
+
</listitem>
|
120
|
+
</varlistentry>
|
121
|
+
|
105
122
|
<xi:include href="common.xmli" xpointer="xpointer(id('filelist.option')/*)" />
|
106
123
|
</variablelist>
|
107
124
|
</refsect1>
|
@@ -179,10 +196,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
179
196
|
</varlistentry>
|
180
197
|
|
181
198
|
<varlistentry>
|
182
|
-
<term>
|
199
|
+
<term>allocated</term>
|
183
200
|
<listitem>
|
184
201
|
<para>
|
185
|
-
|
202
|
+
Sum of the size of all the previously-shown sections, which shows the actual
|
203
|
+
allocated memory used by the object. It is important to note that sections are
|
204
|
+
usually loaded on an alignment of the page size, which on Linux is 4KiB (4096
|
205
|
+
bytes), so the size of the actual memory reserved for the ELF structures in memory
|
206
|
+
is going to be higher than this number.
|
186
207
|
</para>
|
187
208
|
</listitem>
|
188
209
|
</varlistentry>
|
@@ -276,18 +297,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
276
297
|
<title>Bugs and Missing Features</title>
|
277
298
|
|
278
299
|
<para>
|
279
|
-
|
280
|
-
|
281
|
-
</para>
|
282
|
-
|
283
|
-
<para>
|
284
|
-
Right now, all the sections are counted in if their flags match, it might be better to
|
285
|
-
limit to allocated sections all over the place.
|
286
|
-
</para>
|
287
|
-
|
288
|
-
<para>
|
289
|
-
The size of the columns is fixed to 8 digits, it might not be enough to fill in enough
|
290
|
-
space for some big ELF files.
|
300
|
+
When using the <option>--differential</option> option, analysis of the arguments happens
|
301
|
+
sequentially in a single thread rather than in parallel.
|
291
302
|
</para>
|
292
303
|
|
293
304
|
<xi:include href="common.xmli" xpointer="xpointer(id('filelist.bugpara')/*)" />
|
data/manpages/verify-lfs.1
CHANGED
@@ -56,10 +56,7 @@ Recursively descend into directories to search for files to scan\&. This affects
|
|
56
56
|
.PP
|
57
57
|
\fB@\fR\fIpath\fR
|
58
58
|
.RS 4
|
59
|
-
Read the list of files to analyse from the given file
|
60
|
-
\fIpath\fR
|
61
|
-
is
|
62
|
-
\-)\&. Useful to pass a long list of files\&. When used with stdin or named pipes, this also allows asynchronous analysis\&.
|
59
|
+
Read the list of files to analyse from the given file; useful to pass a long list of files\&. The files are read before the processing start, so that the list of target files is available\&.
|
63
60
|
.RE
|
64
61
|
.SH "BUGS AND MISSING FEATURES"
|
65
62
|
.PP
|
@@ -26,342 +26,307 @@ require 'pg'
|
|
26
26
|
|
27
27
|
require 'elf'
|
28
28
|
require 'elf/utils/loader'
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
29
|
+
require 'elf/tools'
|
30
|
+
|
31
|
+
module Elf::Tools
|
32
|
+
class CollisionsHarvester < Elf::Tool
|
33
|
+
def self.initialize
|
34
|
+
super
|
35
|
+
# this script doesn't support running multithreaded, since we
|
36
|
+
# need synchronous access to the database itself, so for now
|
37
|
+
# simply make sure that it never is executed with threads.
|
38
|
+
@execution_threads = nil
|
39
|
+
|
40
|
+
@options |= [
|
41
|
+
["--no-scan-ldpath", "-L", GetoptLong::NO_ARGUMENT ],
|
42
|
+
["--scan-path", "-p", GetoptLong::NO_ARGUMENT ],
|
43
|
+
["--suppressions", "-s", GetoptLong::REQUIRED_ARGUMENT ],
|
44
|
+
["--multimplementations", "-m", GetoptLong::REQUIRED_ARGUMENT ],
|
45
|
+
["--elf-machine", "-M", GetoptLong::REQUIRED_ARGUMENT ],
|
46
|
+
["--postgres-username", "-U", GetoptLong::REQUIRED_ARGUMENT ],
|
47
|
+
["--postgres-password", "-P", GetoptLong::REQUIRED_ARGUMENT ],
|
48
|
+
["--postgres-hostname", "-H", GetoptLong::REQUIRED_ARGUMENT ],
|
49
|
+
["--postgres-port", "-T", GetoptLong::REQUIRED_ARGUMENT ],
|
50
|
+
["--postgres-database", "-D", GetoptLong::REQUIRED_ARGUMENT ],
|
51
|
+
["--output", "-o", GetoptLong::REQUIRED_ARGUMENT ],
|
52
|
+
]
|
53
|
+
|
54
|
+
# we remove the -R option since we always want to be recursive in our search
|
55
|
+
@options.delete_if { |opt| opt[1] == "-R" }
|
56
|
+
@recursive = true
|
57
|
+
|
58
|
+
@suppression_files = File.exist?('suppressions') ? [ 'suppressions' ] : []
|
59
|
+
@multimplementation_files = File.exist?('multimplementations') ? [ 'multimplementations' ] : []
|
60
|
+
|
61
|
+
# Total suppressions are for directories to skip entirely
|
62
|
+
# Partial suppressions are the ones that apply only to a subset
|
63
|
+
# of symbols.
|
64
|
+
@total_suppressions = []
|
65
|
+
@partial_suppressions = []
|
66
|
+
|
67
|
+
@multimplementations = []
|
68
|
+
|
69
|
+
@output = "collisions.log"
|
67
70
|
end
|
68
|
-
multimplementation_files << arg
|
69
|
-
when '--scan-path'
|
70
|
-
scan_path = true
|
71
|
-
when '--no-scan-ldpath'
|
72
|
-
scan_ldpath = false
|
73
|
-
when '--scan-directory'
|
74
|
-
scan_directories << arg
|
75
|
-
when '--recursive-scan'
|
76
|
-
recursive_scan = true
|
77
|
-
when "--elf-machine"
|
78
|
-
machine_str = arg.dup
|
79
|
-
|
80
|
-
# Remove the EM_ prefix if present
|
81
|
-
machine_str = machine_str[3..-1] if arg[0..2].upcase == "EM_"
|
82
|
-
|
83
|
-
# Remove underscores if present (we don't keep them in our
|
84
|
-
# constants)
|
85
|
-
machine_str.delete!("_")
|
86
|
-
|
87
|
-
machine_val = Elf::Machine.from_string(machine_str)
|
88
|
-
|
89
|
-
if machine_val.nil?
|
90
|
-
$stderr.puts "harvest.rb: unknown machine string - #{arg}"
|
91
|
-
else
|
92
|
-
$machines << machine_val unless machine_val.nil?
|
93
|
-
end
|
94
|
-
when '--postgres-username' then pg_params[:user] = arg
|
95
|
-
when '--postgres-password' then pg_params[:password] = arg
|
96
|
-
when '--postgres-hostname' then pg_params[:host] = arg
|
97
|
-
when '--postgres-port' then pg_params[:port] = arg
|
98
|
-
when '--postgres-database' then pg_params[:dbname] = arg
|
99
|
-
end
|
100
|
-
end
|
101
71
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
db.exec("DROP TABLE IF EXISTS symbols, multimplementations, objects CASCADE")
|
107
|
-
|
108
|
-
db.exec("CREATE TABLE objects ( id INTEGER PRIMARY KEY, name VARCHAR(4096), abi VARCHAR(255), exported BOOLEAN, UNIQUE(name, abi) )")
|
109
|
-
db.exec("CREATE TABLE multimplementations ( id INTEGER REFERENCES objects(id) ON DELETE CASCADE, path VARCHAR(4096), UNIQUE(path) )")
|
110
|
-
db.exec("CREATE TABLE symbols ( object INTEGER REFERENCES objects(id) ON DELETE CASCADE, symbol TEXT,
|
111
|
-
PRIMARY KEY(object, symbol) )")
|
112
|
-
|
113
|
-
db.exec("CREATE VIEW symbol_count AS
|
114
|
-
SELECT symbol, abi, COUNT(*) AS occurrences, BOOL_OR(objects.exported) AS exported FROM symbols INNER JOIN objects ON symbols.object = objects.id GROUP BY symbol, abi")
|
115
|
-
db.exec("CREATE VIEW duplicate_symbols AS
|
116
|
-
SELECT * FROM symbol_count WHERE occurrences > 1 AND exported = 't' ORDER BY occurrences DESC, symbol ASC")
|
117
|
-
|
118
|
-
db.exec("PREPARE newmulti (int, text) AS
|
119
|
-
INSERT INTO multimplementations (id, path) VALUES($1, $2)")
|
120
|
-
db.exec("PREPARE newobject (int, text, text, boolean) AS
|
121
|
-
INSERT INTO objects(id, name, abi, exported) VALUES($1, $2, $3, $4)")
|
122
|
-
db.exec("PREPARE newsymbol (int, text) AS
|
123
|
-
INSERT INTO symbols VALUES($1, $2)")
|
124
|
-
|
125
|
-
db.exec("PREPARE checkimplementation(text, text) AS
|
126
|
-
SELECT id FROM objects WHERE name = $1 AND abi = $2")
|
127
|
-
db.exec("PREPARE checkdupsymbol (int, text) AS
|
128
|
-
SELECT 1 FROM symbols WHERE object = $1 AND symbol = $2")
|
129
|
-
|
130
|
-
# Total suppressions are for directories to skip entirely
|
131
|
-
# Partial suppressions are the ones that apply only to a subset
|
132
|
-
# of symbols.
|
133
|
-
$total_suppressions = []
|
134
|
-
$partial_suppressions = []
|
135
|
-
|
136
|
-
suppression_files.each do |suppression|
|
137
|
-
File.open(suppression) do |file|
|
138
|
-
file.each_line do |line|
|
139
|
-
path, symbols = line.
|
140
|
-
gsub(/#\s.*/, '').
|
141
|
-
strip.
|
142
|
-
split(/\s+/, 2)
|
143
|
-
|
144
|
-
next unless path
|
145
|
-
|
146
|
-
if not symbols or symbols == ""
|
147
|
-
$total_suppressions << Regexp.new(path)
|
148
|
-
else
|
149
|
-
$partial_suppressions << [Regexp.new(path), Regexp.new(symbols)]
|
72
|
+
def self.suppressions_cb(arg)
|
73
|
+
unless File.exist? arg
|
74
|
+
puterror("no such file or directory - #{arg}")
|
75
|
+
exit -1
|
150
76
|
end
|
77
|
+
@suppression_files << arg
|
151
78
|
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
multimplementations = []
|
156
|
-
|
157
|
-
multimplementation_files.each do |multimplementation|
|
158
|
-
File.open(multimplementation) do |file|
|
159
|
-
file.each_line do |line|
|
160
|
-
implementation, paths = line.
|
161
|
-
gsub(/#\s.*/, '').
|
162
|
-
strip.
|
163
|
-
split(/\s+/, 2)
|
164
|
-
|
165
|
-
next unless implementation
|
166
|
-
next unless paths
|
167
|
-
|
168
|
-
multimplementations << [ implementation, Regexp.new(paths) ]
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
so_files = Set.new
|
174
79
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
return nil if to_s =~ supp
|
80
|
+
def self.multimplementations_cb(arg)
|
81
|
+
unless File.exist? arg
|
82
|
+
puterror("no such file or directory - #{arg}")
|
83
|
+
exit -1
|
84
|
+
end
|
85
|
+
@multimplementation_files << arg
|
182
86
|
end
|
183
87
|
|
184
|
-
|
88
|
+
def self.elf_machine_cb(arg)
|
89
|
+
machine_str = (arg[0..2].upcase == "EM_" ? arg[3..-1] : arg).delete("_")
|
90
|
+
machine_val = Elf::Machine.from_string(machine_str)
|
185
91
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
if ($machines.nil? or $machines.include?(elf.machine)) and
|
190
|
-
(elf.has_section?('.dynsym') and elf.has_section?('.dynstr') and
|
191
|
-
elf.has_section?('.dynamic')) and
|
192
|
-
(elf[".dynsym"].class == Elf::SymbolTable)
|
193
|
-
return to_s
|
92
|
+
if machine_val.nil?
|
93
|
+
puterror("unknown machine string - #{arg}")
|
194
94
|
else
|
195
|
-
|
95
|
+
@machines ||= []
|
96
|
+
@machines << machine_val
|
196
97
|
end
|
197
|
-
# Explicitly list this so that it won't pollute the output
|
198
|
-
rescue Elf::File::NotAnELF
|
199
|
-
return nil
|
200
|
-
rescue Exception => e
|
201
|
-
$stderr.puts "harvest.rb: #{e.message} - #{self.to_s}"
|
202
|
-
ensure
|
203
|
-
elf.close unless elf.nil?
|
204
98
|
end
|
205
|
-
end
|
206
99
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
100
|
+
def self.after_options
|
101
|
+
pg_params = {
|
102
|
+
:dbname => @postgres_database,
|
103
|
+
:host => @postgres_hostname,
|
104
|
+
:password => @postgres_password,
|
105
|
+
:port => @postgres_port,
|
106
|
+
:user => @postgres_username,
|
107
|
+
}
|
108
|
+
|
109
|
+
@suppression_files.each do |suppression|
|
110
|
+
File.open(suppression) do |file|
|
111
|
+
file.each_line do |line|
|
112
|
+
path, symbols = line.
|
113
|
+
gsub(/#\s.*/, '').
|
114
|
+
strip.
|
115
|
+
split(/\s+/, 2)
|
116
|
+
|
117
|
+
next unless path
|
118
|
+
|
119
|
+
if not symbols or symbols == ""
|
120
|
+
@total_suppressions << Regexp.new(path)
|
121
|
+
else
|
122
|
+
@partial_suppressions << [Regexp.new(path), Regexp.new(symbols)]
|
123
|
+
end
|
124
|
+
end
|
216
125
|
end
|
217
|
-
# When using C-c to stop, well, stop.
|
218
|
-
rescue Interrupt
|
219
|
-
raise
|
220
|
-
rescue Exception => e
|
221
|
-
$stderr.puts "Ignoring #{entry} (#{e.message})"
|
222
|
-
next
|
223
126
|
end
|
224
|
-
end
|
225
127
|
|
226
|
-
|
227
|
-
end
|
228
|
-
end
|
128
|
+
@total_suppressions = Regexp.union(@total_suppressions)
|
229
129
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
130
|
+
@multimplementation_files.each do |multimplementation|
|
131
|
+
@multimplementations |= \
|
132
|
+
File.new(multimplementation).read.split(/\r?\n/).collect do |line|
|
133
|
+
implementation, paths = line.
|
134
|
+
gsub(/#\s.*/, '').
|
135
|
+
strip.
|
136
|
+
split(/\s+/, 2)
|
240
137
|
|
241
|
-
if
|
242
|
-
ENV['PATH'].split(":").each do |path|
|
243
|
-
begin
|
244
|
-
so_files.merge Pathname.new(path).so_files(false)
|
245
|
-
rescue Errno::ENOENT
|
246
|
-
$stderr.puts "harvest.rb: No such file or directory - #{path}"
|
247
|
-
next
|
248
|
-
end
|
249
|
-
end
|
250
|
-
end
|
138
|
+
next if (implementation.nil? or paths.nil?)
|
251
139
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
140
|
+
[ implementation, Regexp.new(paths) ]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
@multimplementations.delete_if { |x| x.nil? }
|
144
|
+
|
145
|
+
@targets |=
|
146
|
+
( !@no_scan_ldpath ? Elf::Utilities.system_library_path : [] ) |
|
147
|
+
( (@scan_path and ENV['PATH']) ? ENV['PATH'].split(":") : [] )
|
148
|
+
|
149
|
+
@db = PGconn.open(pg_params)
|
150
|
+
|
151
|
+
@db.exec(<<EOF)
|
152
|
+
BEGIN TRANSACTION;
|
153
|
+
|
154
|
+
DROP TABLE IF EXISTS symbols, multimplementations, objects CASCADE;
|
155
|
+
DROP LANGUAGE IF EXISTS plpgsql CASCADE;
|
156
|
+
|
157
|
+
CREATE LANGUAGE plpgsql;
|
158
|
+
CREATE TABLE objects (
|
159
|
+
id SERIAL PRIMARY KEY,
|
160
|
+
name VARCHAR(4096),
|
161
|
+
abi VARCHAR(255),
|
162
|
+
exported BOOLEAN,
|
163
|
+
UNIQUE(name, abi)
|
164
|
+
);
|
165
|
+
CREATE TABLE multimplementations (
|
166
|
+
id INTEGER REFERENCES objects(id) ON DELETE CASCADE,
|
167
|
+
path VARCHAR(4096),
|
168
|
+
UNIQUE(path)
|
169
|
+
);
|
170
|
+
CREATE TABLE symbols (
|
171
|
+
object INTEGER REFERENCES objects(id) ON DELETE CASCADE,
|
172
|
+
symbol TEXT,
|
173
|
+
PRIMARY KEY(object, symbol)
|
174
|
+
);
|
175
|
+
|
176
|
+
CREATE VIEW symbol_count AS
|
177
|
+
SELECT symbol, abi, COUNT(*) AS occurrences, BOOL_OR(objects.exported) AS exported
|
178
|
+
FROM symbols INNER JOIN objects ON symbols.object = objects.id GROUP BY symbol, abi;
|
179
|
+
CREATE VIEW duplicate_symbols AS
|
180
|
+
SELECT * FROM symbol_count
|
181
|
+
WHERE occurrences > 1 AND exported = 't'
|
182
|
+
ORDER BY occurrences DESC, symbol ASC;
|
183
|
+
|
184
|
+
PREPARE getinstances (text, text) AS
|
185
|
+
SELECT name FROM symbols INNER JOIN objects ON symbols.object = objects.id
|
186
|
+
WHERE symbol = $1 AND abi = $2 ORDER BY name;
|
187
|
+
|
188
|
+
CREATE FUNCTION implementation (
|
189
|
+
p_implementation TEXT,
|
190
|
+
p_abi TEXT,
|
191
|
+
p_exported BOOLEAN,
|
192
|
+
OUT implementation_id INTEGER,
|
193
|
+
OUT created BOOLEAN
|
194
|
+
) AS $$
|
195
|
+
BEGIN
|
196
|
+
SELECT INTO implementation_id id FROM objects
|
197
|
+
WHERE name = p_implementation AND abi = p_abi;
|
198
|
+
|
199
|
+
IF implementation_id IS NULL THEN
|
200
|
+
created := TRUE;
|
201
|
+
|
202
|
+
INSERT INTO objects(name, abi, exported)
|
203
|
+
VALUES(p_implementation, p_abi, p_exported);
|
204
|
+
SELECT INTO implementation_id
|
205
|
+
currval(pg_get_serial_sequence('objects', 'id'));
|
206
|
+
END IF;
|
207
|
+
END;
|
208
|
+
$$ LANGUAGE 'plpgsql';
|
209
|
+
|
210
|
+
CREATE FUNCTION multimplementation (
|
211
|
+
p_id INTEGER,
|
212
|
+
p_filepath TEXT
|
213
|
+
) RETURNS BOOLEAN AS $$
|
214
|
+
BEGIN
|
215
|
+
INSERT INTO multimplementations (id, path) VALUES(p_id, p_filepath);
|
216
|
+
RETURN 't';
|
217
|
+
EXCEPTION
|
218
|
+
WHEN unique_violation THEN
|
219
|
+
RETURN 'f';
|
220
|
+
END;
|
221
|
+
$$ LANGUAGE 'plpgsql';
|
222
|
+
|
223
|
+
CREATE FUNCTION symbol (
|
224
|
+
p_object INTEGER,
|
225
|
+
p_symbol TEXT
|
226
|
+
) RETURNS VOID AS '
|
227
|
+
BEGIN
|
228
|
+
INSERT INTO symbols VALUES(p_object, p_symbol);
|
229
|
+
RETURN;
|
230
|
+
EXCEPTION
|
231
|
+
WHEN unique_violation THEN
|
232
|
+
RETURN;
|
233
|
+
END;
|
234
|
+
' LANGUAGE 'plpgsql';
|
235
|
+
|
236
|
+
COMMIT;
|
237
|
+
EOF
|
238
|
+
end
|
260
239
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
so_files << Pathname.new(path).maybe_queue
|
265
|
-
end
|
240
|
+
def self.db_exec(query)
|
241
|
+
@db.exec(query)
|
242
|
+
end
|
266
243
|
|
267
|
-
|
268
|
-
|
269
|
-
if ARGV.size == 0 and not scan_path and scan_directories.size == 0
|
270
|
-
$stdin.each_line do |path|
|
271
|
-
so_files << Pathname.new(path.chomp("\n")).maybe_queue
|
272
|
-
end
|
273
|
-
end
|
244
|
+
def self.analysis(filename)
|
245
|
+
return if filename =~ @total_suppressions
|
274
246
|
|
275
|
-
|
247
|
+
Elf::File.open(filename) do |elf|
|
248
|
+
unless ($machines.nil? or $machines.include?(elf.machine)) and
|
249
|
+
(elf.has_section?('.dynsym') and elf.has_section?('.dynstr') and
|
250
|
+
elf.has_section?('.dynamic')) and
|
251
|
+
(elf[".dynsym"].class == Elf::SymbolTable)
|
252
|
+
return
|
253
|
+
end
|
276
254
|
|
277
|
-
|
278
|
-
val = 0
|
255
|
+
local_suppressions = Regexp.union((@partial_suppressions.dup.delete_if{ |s| filename.to_s !~ s[0] }).collect { |s| s[1] })
|
279
256
|
|
280
|
-
|
281
|
-
|
257
|
+
name = filename
|
258
|
+
abi = "#{elf.elf_class} #{elf.abi} #{elf.machine}".gsub("'", "''")
|
282
259
|
|
283
|
-
|
284
|
-
|
285
|
-
|
260
|
+
@multimplementations.each do |implementation, paths|
|
261
|
+
# Get the full matchdata because we might need to get the matches.
|
262
|
+
match = paths.match(filename)
|
286
263
|
|
287
|
-
|
288
|
-
local_suppressions = $partial_suppressions.dup.delete_if { |s| not so.to_s =~ s[0] }
|
264
|
+
next unless match
|
289
265
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
266
|
+
while implementation =~ /\$([0-9]+)/ do
|
267
|
+
match_idx = $1.to_i
|
268
|
+
replacement = match[match_idx]
|
269
|
+
replacement = "" if replacement.nil?
|
270
|
+
implementation = implementation.gsub("$#{match_idx}", replacement)
|
271
|
+
end
|
294
272
|
|
295
|
-
|
273
|
+
name = implementation
|
274
|
+
break
|
275
|
+
end
|
296
276
|
|
297
|
-
|
298
|
-
# Get the full matchdata because we might need to get the matches.
|
299
|
-
match = paths.match(so)
|
277
|
+
shared = (filename != name) || (elf['.dynamic'].soname != nil)
|
300
278
|
|
301
|
-
|
279
|
+
res = db_exec("SELECT * FROM implementation('#{name}', '#{abi}', '#{shared}')")
|
280
|
+
impid = res[0]["implementation_id"]
|
302
281
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
replacement = "" if replacement.nil?
|
307
|
-
implementation = implementation.gsub("$#{match_idx}", replacement)
|
282
|
+
if filename != name
|
283
|
+
# If this is a collapsed multimplementation, add it to the list
|
284
|
+
res = db_exec("SELECT multimplementation(#{impid}, '#{filename}') AS created");
|
308
285
|
end
|
309
286
|
|
310
|
-
|
311
|
-
|
312
|
-
|
287
|
+
# skip over the file if we already visited it (either directly
|
288
|
+
# or as a multimplementation.
|
289
|
+
next if res[0]["created"] != "t"
|
290
|
+
|
291
|
+
query = ""
|
292
|
+
elf['.dynsym'].each do |sym|
|
293
|
+
begin
|
294
|
+
next if sym.idx == 0 or
|
295
|
+
sym.bind != Elf::Symbol::Binding::Global or
|
296
|
+
sym.section.nil? or
|
297
|
+
sym.value == 0 or
|
298
|
+
sym.section.is_a? Integer or
|
299
|
+
sym.section.name == '.init' or
|
300
|
+
sym.section.name == '.bss'
|
301
|
+
|
302
|
+
next if (sym.name =~ local_suppressions);
|
303
|
+
|
304
|
+
query << "SELECT symbol('#{impid}', '#{sym.name}@#{sym.version}');"
|
305
|
+
rescue Exception
|
306
|
+
$stderr.puts "Mangling symbol #{sym.name}"
|
307
|
+
raise
|
308
|
+
end
|
313
309
|
end
|
314
|
-
|
315
|
-
end
|
316
|
-
|
317
|
-
shared = (so != name) || (elf['.dynamic'].soname != nil)
|
318
|
-
|
319
|
-
unless impid
|
320
|
-
val += 1
|
321
|
-
impid = val
|
322
|
-
|
323
|
-
db.exec("EXECUTE newobject(#{impid}, '#{name}', '#{abi}', '#{shared}')")
|
310
|
+
db_exec("BEGIN TRANSACTION;" + query + "COMMIT;") unless query.empty?
|
324
311
|
end
|
312
|
+
end
|
325
313
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
sym.value == 0 or
|
334
|
-
sym.section.is_a? Integer or
|
335
|
-
sym.section.name == '.init' or
|
336
|
-
sym.section.name == '.bss'
|
337
|
-
|
338
|
-
skip = false
|
339
|
-
|
340
|
-
local_suppressions.each do |supp|
|
341
|
-
if sym.name =~ supp[1]
|
342
|
-
skip = true
|
343
|
-
break
|
344
|
-
end
|
345
|
-
end
|
314
|
+
def self.results
|
315
|
+
db_exec(<<EOF)
|
316
|
+
BEGIN TRANSACTION;
|
317
|
+
CREATE INDEX objects_name ON objects(name);
|
318
|
+
CREATE INDEX symbols_symbol ON symbols(symbol);
|
319
|
+
COMMIT;
|
320
|
+
EOF
|
346
321
|
|
347
|
-
|
322
|
+
outfile = File.new(@output, "w")
|
348
323
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
raise
|
324
|
+
db_exec("SELECT * FROM duplicate_symbols").each do |row|
|
325
|
+
outfile.puts "Symbol #{row['symbol']} (#{row['abi']}) present #{row['occurrences']} times"
|
326
|
+
db_exec( "EXECUTE getinstances ('#{row['symbol']}', '#{row['abi']}')" ).each do |path|
|
327
|
+
outfile.puts " #{path['name']}"
|
354
328
|
end
|
355
329
|
end
|
356
330
|
end
|
357
|
-
rescue Exception
|
358
|
-
$stderr.puts "Checking #{so}"
|
359
|
-
raise
|
360
331
|
end
|
361
|
-
|
362
|
-
pbar.inc if pbar
|
363
332
|
end
|
364
|
-
|
365
|
-
db.exec("CREATE INDEX objects_name ON objects(name)")
|
366
|
-
db.exec("CREATE INDEX symbols_symbol ON symbols(symbol)")
|
367
|
-
db.exec("COMMIT")
|