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/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 (or standard input if
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.0 <http://docbook.sf.net/>
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\-\-no\-match\-undefined\fR | \fB\-\-no\-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...]
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\-\-no\-match\-undefined\fR
71
+ \fB\-U\fR, \fB\-\-match\-undefined\fR
72
72
  .RS 4
73
- Do not report matches on undefined symbols; useful if you\*(Aqre looking for the objects defining the symbol, and not those using it\&.
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
- Do not report matches on defined symbols; useful if you\*(Aqre looking for the objects using the symbol, and not those defining it\&.
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 (or standard input if
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
@@ -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>--no-match-undefined</option>
63
+ <option>--match-undefined</option>
64
64
  </arg>
65
65
 
66
66
  <arg choice="plain">
67
- <option>--no-match-defined</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>--no-match-undefined</option></term>
193
+ <term><option>--match-undefined</option></term>
194
194
  <listitem>
195
195
  <para>
196
- Do not report matches on undefined symbols; useful if you're looking for the objects
197
- defining the symbol, and not those using it.
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
- Do not report matches on defined symbols; useful if you're looking for the objects
208
- using the symbol, and not those defining it.
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>
@@ -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 (or standard input if
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"
@@ -1,7 +1,7 @@
1
1
  '\" t
2
2
  .\" Title: rbelf-size
3
3
  .\" Author:
4
- .\" Generator: DocBook XSL-NS Stylesheets v1.76.0 <http://docbook.sf.net/>
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 (or standard input if
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
- total
120
+ allocated
119
121
  .RS 4
120
- Counts in the sum of all the previous sections\&.
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
- The name
162
- \fItotal\fR
163
- for the sum of the colums is misleading since it\*(Aqs actually not the total size of the file, nor the total size of the allocated entries\&.
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
@@ -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>total</term>
199
+ <term>allocated</term>
183
200
  <listitem>
184
201
  <para>
185
- Counts in the sum of all the previous sections.
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
- The name <varname>total</varname> for the sum of the colums is misleading since it's
280
- actually not the total size of the file, nor the total size of the allocated entries.
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')/*)" />
@@ -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 (or standard input if
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
- opts = GetoptLong.new(
31
- ["--no-scan-ldpath", "-L", GetoptLong::NO_ARGUMENT ],
32
- ["--scan-path", "-p", GetoptLong::NO_ARGUMENT ],
33
- ["--suppressions", "-s", GetoptLong::REQUIRED_ARGUMENT ],
34
- ["--multiplementations", "-m", GetoptLong::REQUIRED_ARGUMENT ],
35
- ["--scan-directory", "-d", GetoptLong::REQUIRED_ARGUMENT ],
36
- ["--recursive-scan", "-r", GetoptLong::NO_ARGUMENT ],
37
- ["--elf-machine", "-M", GetoptLong::REQUIRED_ARGUMENT ],
38
- ["--postgres-username", "-U", GetoptLong::REQUIRED_ARGUMENT ],
39
- ["--postgres-password", "-P", GetoptLong::REQUIRED_ARGUMENT ],
40
- ["--postgres-hostname", "-H", GetoptLong::REQUIRED_ARGUMENT ],
41
- ["--postgres-port", "-T", GetoptLong::REQUIRED_ARGUMENT ],
42
- ["--postgres-database", "-D", GetoptLong::REQUIRED_ARGUMENT ]
43
- )
44
-
45
- suppression_files = File.exist?('suppressions') ? [ 'suppressions' ] : []
46
- multimplementation_files = File.exist?('multimplementations') ? [ 'multimplementations' ] : []
47
- scan_path = false
48
- scan_ldpath = true
49
- recursive_scan = false
50
- scan_directories = []
51
- $machines = []
52
-
53
- pg_params = {}
54
-
55
- opts.each do |opt, arg|
56
- case opt
57
- when '--suppressions'
58
- unless File.exist? arg
59
- $stderr.puts "harvest.rb: no such file or directory - #{arg}"
60
- exit -1
61
- end
62
- suppression_files << arg
63
- when "--multiplementations"
64
- unless File.exist? arg
65
- $stderr.puts "harvest.rb: no such file or directory - #{arg}"
66
- exit -1
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
- $machines = nil if $machines.empty?
103
-
104
- db = PGconn.open(pg_params)
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
- # Extend Pathname with a so_files method
176
- class Pathname
177
- def maybe_queue
178
- return nil if symlink?
179
-
180
- $total_suppressions.each do |supp|
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
- elf = nil
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
- begin
187
- elf = Elf::File.open(self)
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
- return nil
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
- def so_files(recursive = true)
208
- res = Set.new
209
- children.each do |entry|
210
- begin
211
- case entry.ftype
212
- when "directory"
213
- res.merge entry.so_files if recursive
214
- when "file"
215
- res << entry.maybe_queue
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
- return res
227
- end
228
- end
128
+ @total_suppressions = Regexp.union(@total_suppressions)
229
129
 
230
- if scan_ldpath
231
- Elf::Utilities.system_library_path.each do |path|
232
- begin
233
- so_files.merge Pathname.new(path).so_files
234
- rescue Errno::ENOENT
235
- $stderr.puts "harvest.rb: No such file or directory - #{path}"
236
- next
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 scan_path and ENV['PATH']
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
- scan_directories.each do |path|
253
- begin
254
- so_files.merge Pathname.new(path).so_files(recursive_scan)
255
- rescue Errno::ENOENT
256
- $stderr.puts "harvest.rb: No such file or directory - #{path}"
257
- next
258
- end
259
- end
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
- # if there are explicit files listed in the standard input, scan those
262
- # right away.
263
- ARGV.each do |path|
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
- # finally if none of the above matched, try checking for data on the
268
- # standard input
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
- so_files.delete(nil)
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
- db.exec("BEGIN TRANSACTION")
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
- begin
281
- require 'progressbar'
257
+ name = filename
258
+ abi = "#{elf.elf_class} #{elf.abi} #{elf.machine}".gsub("'", "''")
282
259
 
283
- pbar = ProgressBar.new("harvest", so_files.size)
284
- rescue LoadError, NameError
285
- end
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
- so_files.each do |so|
288
- local_suppressions = $partial_suppressions.dup.delete_if { |s| not so.to_s =~ s[0] }
264
+ next unless match
289
265
 
290
- begin
291
- Elf::File.open(so) do |elf|
292
- name = so
293
- abi = "#{elf.elf_class} #{elf.abi} #{elf.machine.to_s.gsub("'", "\\'" )}"
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
- impid = nil
273
+ name = implementation
274
+ break
275
+ end
296
276
 
297
- multimplementations.each do |implementation, paths|
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
- next unless match
279
+ res = db_exec("SELECT * FROM implementation('#{name}', '#{abi}', '#{shared}')")
280
+ impid = res[0]["implementation_id"]
302
281
 
303
- while implementation =~ /\$([0-9]+)/ do
304
- match_idx = $1.to_i
305
- replacement = match[match_idx]
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
- name = implementation
311
- db.exec("EXECUTE checkimplementation('#{implementation}', '#{abi}')").each do |row|
312
- impid = row['id']
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
- break
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
- db.exec("EXECUTE newmulti(#{impid}, '#{so}')") if so != name
327
-
328
- elf['.dynsym'].each do |sym|
329
- begin
330
- next if sym.idx == 0 or
331
- sym.bind != Elf::Symbol::Binding::Global or
332
- sym.section.nil? or
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
- next if skip or (db.exec("EXECUTE checkdupsymbol('#{impid}', '#{sym.name}@#{sym.version}')").num_tuples > 0)
322
+ outfile = File.new(@output, "w")
348
323
 
349
- db.exec("EXECUTE newsymbol('#{impid}', '#{sym.name}@#{sym.version}')")
350
-
351
- rescue Exception
352
- $stderr.puts "Mangling symbol #{sym.name}"
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")