ony-ruby-elf 1.1.0

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.
@@ -0,0 +1,49 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Offset-based string table
3
+ #
4
+ # Copyright © 2007-2012 Diego Elio Pettenò <flameeyes@flameeyes.eu>
5
+ #
6
+ # This program is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this generator; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
+
20
+ module Elf
21
+ module Utilities
22
+ class OffsetTable
23
+ class InvalidIndex < Exception
24
+ def initialize(idx, max_idx)
25
+ super("Invalid index #{idx} (maximum index: #{max_idx})")
26
+ end
27
+ end
28
+
29
+ def initialize(content, separator)
30
+ @content = content
31
+ @separator = separator
32
+ end
33
+
34
+ def size
35
+ @content.size
36
+ end
37
+
38
+ def [](idx)
39
+ raise InvalidIndex.new(idx, size) if idx >= size
40
+
41
+ # find the first occurrence of the separator starting from the
42
+ # given index
43
+ endidx = @content.index(@separator, idx)
44
+
45
+ return @content[idx..endidx].chomp(@separator)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright © 2009 Alex Legler <a3li@gentoo.org>
3
+ # Copyright © 2009-2010 Diego Elio Pettenò <flameeyes@flameeyes.eu>
4
+ #
5
+ # This program is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation; either version 2 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this generator; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
+
19
+ require 'elf'
20
+ require 'pathname'
21
+ require 'weakref'
22
+
23
+ module Elf::Utilities
24
+ # Pool for ELF files.
25
+ #
26
+ # This pool is useful for tools that recurse over a tree of
27
+ # dependencies to avoid creating multiple instances of Elf::File
28
+ # being open and accessing the same file.
29
+ #
30
+ # Notes:
31
+ # - Instances might be re-created if no strong references left to specific
32
+ # Elf::File
33
+ # - New instance will be created on access if previous one were closed
34
+ class FilePool
35
+ @pool = Hash.new
36
+
37
+ def self.[](file)
38
+ realfile = Pathname.new(file).realpath
39
+
40
+ cached = @pool[realfile].__getobj__ rescue nil
41
+ if cached.nil? || cached.closed?
42
+ cached = Elf::File.new(realfile)
43
+ @pool[realfile] = WeakRef.new(cached)
44
+ end
45
+
46
+ cached
47
+ end
48
+ end
49
+ end
data/lib/elf/value.rb ADDED
@@ -0,0 +1,128 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Simple ELF parser for Ruby
3
+ #
4
+ # Copyright © 2007-2010 Diego Elio Pettenò <flameeyes@flameeyes.eu>
5
+ # Portions inspired by elf.py
6
+ # Copyright © 2002 Netgraft Corporation
7
+ # Portions inspired by elf.h
8
+ # Copyright © 1995-2006 Free Software Foundation, Inc.
9
+ #
10
+ # This program is free software; you can redistribute it and/or modify
11
+ # it under the terms of the GNU General Public License as published by
12
+ # the Free Software Foundation; either version 2 of the License, or
13
+ # (at your option) any later version.
14
+ #
15
+ # This program is distributed in the hope that it will be useful,
16
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ # GNU General Public License for more details.
19
+ #
20
+ # You should have received a copy of the GNU General Public License
21
+ # along with this generator; if not, write to the Free Software
22
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23
+
24
+ module Elf
25
+ class Value
26
+ class OutOfBound < Exception
27
+ attr_reader :val
28
+
29
+ def initialize(val)
30
+ @val = val
31
+ @appendix = ""
32
+ end
33
+
34
+ def message
35
+ "Value #{@val} out of bound#{@appendix}"
36
+ end
37
+
38
+ def append_message(s)
39
+ @appendix << "\n#{s}"
40
+ end
41
+ end
42
+
43
+ def initialize(val, params)
44
+ @val = val
45
+ @mnemonic = params[0].to_s
46
+ @desc = params[1]
47
+ end
48
+
49
+ attr_reader :desc, :val, :mnemonic
50
+ alias :to_i :val
51
+ alias :to_s :desc
52
+
53
+ def ==(other)
54
+ self.class == other.class and @val == other.to_i
55
+ end
56
+
57
+ def Value.[](idx)
58
+ return @enums[idx] if @enums[idx]
59
+
60
+ # If the class has defined special ranges, handle them; a
61
+ # special range is a range of values for which unknown values
62
+ # are allowed (because they are bound to specific usage we don't
63
+ # know about — where on the other hand unknown values outside of
64
+ # these ranges are frown upon); different type of values have
65
+ # different special ranges, each with its own base name, so
66
+ # leave that to be decided by the class itself.
67
+ if self.const_defined?("SpecialRanges")
68
+ self::SpecialRanges.each_pair do |base, range|
69
+ return self::Unknown.new(idx, sprintf("%s+%07x", base, idx-range.first)) if range.include? idx
70
+ end
71
+ end
72
+
73
+ raise OutOfBound.new(idx)
74
+ end
75
+
76
+ def Value.from_string(str)
77
+ str = str.downcase
78
+
79
+ each do |value|
80
+ return value if value.mnemonic.downcase == str
81
+ end
82
+
83
+ return nil
84
+ end
85
+
86
+ def Value.has_key?(idx)
87
+ @enums.has_key?(idx)
88
+ end
89
+
90
+ def Value.fill(*hash)
91
+ if hash.size == 1 && hash[0].is_a?(Hash)
92
+ hash = hash[0]
93
+ end
94
+
95
+ @enums = { }
96
+
97
+ hash.each_pair do |index, value|
98
+ @enums[index] = self.new(index, value)
99
+ const_set(value[0], @enums[index])
100
+ end
101
+ end
102
+
103
+ def Value.each(&block)
104
+ @enums.each_value(&block)
105
+ end
106
+
107
+ private_class_method :fill
108
+
109
+ # Class for unknown values
110
+ #
111
+ # This class is used to provide a way to access at least basic
112
+ # data for values that are not known but are known valid (like OS-
113
+ # or CPU-specific types for files, sections and symbols).
114
+ #
115
+ # It mimics the basis of a Value but is custom-filled by the using
116
+ # code.
117
+ class Unknown
118
+ def initialize(val, desc)
119
+ @val = val
120
+ @desc = desc
121
+ end
122
+
123
+ attr_reader :desc, :val
124
+ alias :to_i :val
125
+ alias :to_s :desc
126
+ end
127
+ end
128
+ end
data/ruby-elf.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- ruby -*- coding: utf-8 -*-
2
+ $:.unshift(File.dirname(__FILE__) + '/lib')
3
+ require 'elf'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'ony-ruby-elf'
7
+ s.version = Elf::VERSION
8
+ s.summary = "Pure Ruby ELF file parser and utilities"
9
+
10
+ s.requirements << 'none'
11
+ s.require_path = 'lib'
12
+ s.homepage = "http://www.flameeyes.eu/projects/ruby-elf"
13
+ s.license = "GPL-2 or later"
14
+ s.author = "Diego Elio Pettenò"
15
+ s.email = "flameeyes@flameeyes.eu"
16
+
17
+ s.description = <<EOF
18
+ Ruby-Elf is a pure-Ruby library for parse and fetch information about
19
+ ELF format used by Linux, FreeBSD, Solaris and other Unix-like
20
+ operating systems, and include a set of analysis tools helpful for
21
+ both optimisations and verification of compiled ELF files.
22
+ EOF
23
+
24
+ s.files = %w{COPYING README.md DONATING ruby-elf.gemspec}
25
+ s.files += Dir['lib/**/*.rb']
26
+ s.files += Dir['bin/**/*.rb']
27
+ s.files += Dir['tools/**/*.rb']
28
+ s.files += Dir['manpages/*.1']
29
+ end
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright © 2008-2010 Diego Elio Pettenò <flameeyes@flameeyes.eu>
4
+ #
5
+ # This program is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation; either version 2 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this generator; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
+
19
+ # Simple script to assess the amount of space saved by duplicate removal of
20
+ # entries in symbols' tables.
21
+
22
+ require 'elf'
23
+
24
+ file_list = nil
25
+
26
+ # If there are no arguments passed through the command line
27
+ # consider it like we're going to act on stdin.
28
+ if not file_list and ARGV.size == 0
29
+ file_list = $stdin
30
+ end
31
+
32
+ def assess_save(file)
33
+ begin
34
+ Elf::File.open(file) do |elf|
35
+ seenstr = Set.new
36
+
37
+ symsec = elf['.dynsym']
38
+ strsec = elf['.dynstr']
39
+
40
+ next unless symsec and strsec
41
+
42
+ # The NULL-entry can be aliased on the last string anyway by
43
+ # letting it point to sectionsize-1
44
+ fullsize = 0
45
+
46
+ symsec.each do |sym|
47
+ next if seenstr.include? sym.name
48
+ seenstr.add sym.name
49
+ fullsize += sym.name.length+1
50
+ end
51
+
52
+ # Dynamic executables and shared objects keep more data into the
53
+ # .dynstr than static executables, in particular they have symbols
54
+ # versions, their soname and their NEEDED sections strings.
55
+ versec = elf['.gnu.version_d']
56
+ if versec
57
+ versec.each do |veridx, ver|
58
+ ver[:names].each do |vername|
59
+ next if seenstr.include? vername
60
+ seenstr.add vername
61
+ fullsize += vername.length+1
62
+ end
63
+ end
64
+ end
65
+
66
+ versec = elf['.gnu.version_r']
67
+ if versec
68
+ versec.each do |veridx, ver|
69
+ next if seenstr.include? ver[:name]
70
+ seenstr.add ver[:name]
71
+ fullsize += ver[:name].length+1
72
+ end
73
+ end
74
+
75
+ elf['.dynamic'].each_entry do |entry|
76
+ case entry[:type]
77
+ when Elf::Dynamic::Type::Needed, Elf::Dynamic::Type::SoName,
78
+ Elf::Dynamic::Type::RPath, Elf::Dynamic::Type::RunPath
79
+
80
+ next if seenstr.include? entry[:parsed]
81
+ seenstr.add entry[:parsed]
82
+ fullsize += entry[:parsed].length+1
83
+ end
84
+ end
85
+
86
+ puts "#{file}: current size #{strsec.size}, full size #{fullsize} difference #{fullsize-strsec.size}"
87
+ end
88
+ rescue Errno::ENOENT
89
+ $stderr.puts "assess_duplicate_save.rb: #{file}: no such file"
90
+ rescue Errno::EISDIR
91
+ $stderr.puts "assess_duplicate_save.rb: #{file}: is a directory"
92
+ rescue Elf::File::NotAnELF
93
+ $stderr.puts "assess_duplicate_save.rb: #{file}: not a valid ELF file."
94
+ end
95
+ end
96
+
97
+ if file_list
98
+ file_list.each_line do |file|
99
+ assess_save(file.rstrip)
100
+ end
101
+ else
102
+ ARGV.each do |file|
103
+ assess_save(file)
104
+ end
105
+ end
@@ -0,0 +1,340 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright © 2007-2010 Diego Elio Pettenò <flameeyes@flameeyes.eu>
4
+ #
5
+ # This program is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation; either version 2 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this generator; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
+
19
+ # This script is used to harvest the symbols defined in the shared
20
+ # objects of the whole system.
21
+
22
+ require 'getoptlong'
23
+ require 'set'
24
+ require 'pathname'
25
+ require 'pg'
26
+
27
+ require 'elf'
28
+ require 'elf/utils/loader'
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
+ ["--analyze-only", "-A", GetoptLong::NO_ARGUMENT ],
53
+ ]
54
+
55
+ # we remove the -R option since we always want to be recursive in our search
56
+ @options.delete_if { |opt| opt[1] == "-R" }
57
+ @recursive = true
58
+ @analyze_only = false
59
+
60
+ @suppression_files = File.exist?('suppressions') ? [ 'suppressions' ] : []
61
+ @multimplementation_files = File.exist?('multimplementations') ? [ 'multimplementations' ] : []
62
+
63
+ # Total suppressions are for directories to skip entirely
64
+ # Partial suppressions are the ones that apply only to a subset
65
+ # of symbols.
66
+ @total_suppressions = []
67
+ @partial_suppressions = []
68
+
69
+ @multimplementations = []
70
+
71
+ @output = "collisions.log"
72
+ end
73
+
74
+ def self.suppressions_cb(arg)
75
+ unless File.exist? arg
76
+ puterror("no such file or directory - #{arg}")
77
+ exit -1
78
+ end
79
+ @suppression_files << arg
80
+ end
81
+
82
+ def self.multimplementations_cb(arg)
83
+ unless File.exist? arg
84
+ puterror("no such file or directory - #{arg}")
85
+ exit -1
86
+ end
87
+ @multimplementation_files << arg
88
+ end
89
+
90
+ def self.elf_machine_cb(arg)
91
+ machine_str = (arg[0..2].upcase == "EM_" ? arg[3..-1] : arg).delete("_")
92
+ machine_val = Elf::Machine.from_string(machine_str)
93
+
94
+ if machine_val.nil?
95
+ puterror("unknown machine string - #{arg}")
96
+ else
97
+ @machines ||= []
98
+ @machines << machine_val
99
+ end
100
+ end
101
+
102
+ def self.after_options
103
+ pg_params = {
104
+ :dbname => @postgres_database,
105
+ :host => @postgres_hostname,
106
+ :password => @postgres_password,
107
+ :port => @postgres_port,
108
+ :user => @postgres_username,
109
+ }
110
+
111
+ @suppression_files.each do |suppression|
112
+ File.open(suppression) do |file|
113
+ file.each_line do |line|
114
+ path, symbols = line.
115
+ gsub(/#\s.*/, '').
116
+ strip.
117
+ split(/\s+/, 2)
118
+
119
+ next unless path
120
+
121
+ if not symbols or symbols == ""
122
+ @total_suppressions << Regexp.new(path)
123
+ else
124
+ @partial_suppressions << [Regexp.new(path), Regexp.new(symbols)]
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ @total_suppressions = Regexp.union(@total_suppressions)
131
+
132
+ @multimplementation_files.each do |multimplementation|
133
+ @multimplementations |= \
134
+ File.read(multimplementation).split(/\r?\n/).collect do |line|
135
+ implementation, paths = line.
136
+ gsub(/#\s.*/, '').
137
+ strip.
138
+ split(/\s+/, 2)
139
+
140
+ next if (implementation.nil? or paths.nil?)
141
+
142
+ [ implementation, Regexp.new(paths) ]
143
+ end
144
+ end
145
+ @multimplementations.delete_if { |x| x.nil? }
146
+
147
+ @targets |=
148
+ ( !@no_scan_ldpath ? Elf::Utilities.system_library_path : [] ) |
149
+ ( (@scan_path and ENV['PATH']) ? ENV['PATH'].split(":") : [] )
150
+
151
+ return if @analyse_only
152
+
153
+ @db = PGconn.open(pg_params)
154
+
155
+ @db.exec(<<EOF)
156
+ BEGIN TRANSACTION;
157
+
158
+ DROP TABLE IF EXISTS symbols, multimplementations, objects CASCADE;
159
+ DROP EXTENSION IF EXISTS plpgsql CASCADE;
160
+
161
+ CREATE EXTENSION plpgsql;
162
+ CREATE TABLE objects (
163
+ id SERIAL PRIMARY KEY,
164
+ name VARCHAR(4096),
165
+ abi VARCHAR(255),
166
+ exported BOOLEAN,
167
+ UNIQUE(name, abi)
168
+ );
169
+ CREATE TABLE multimplementations (
170
+ id INTEGER REFERENCES objects(id) ON DELETE CASCADE,
171
+ path VARCHAR(4096),
172
+ UNIQUE(path)
173
+ );
174
+ CREATE TABLE symbols (
175
+ object INTEGER REFERENCES objects(id) ON DELETE CASCADE,
176
+ symbol TEXT,
177
+ PRIMARY KEY(object, symbol)
178
+ );
179
+
180
+ CREATE VIEW symbol_count AS
181
+ SELECT symbol, abi, COUNT(*) AS occurrences, BOOL_OR(objects.exported) AS exported
182
+ FROM symbols INNER JOIN objects ON symbols.object = objects.id GROUP BY symbol, abi;
183
+ CREATE VIEW duplicate_symbols AS
184
+ SELECT * FROM symbol_count
185
+ WHERE occurrences > 1 AND exported = 't'
186
+ ORDER BY occurrences DESC, symbol ASC;
187
+
188
+ PREPARE getinstances (text, text) AS
189
+ SELECT name FROM symbols INNER JOIN objects ON symbols.object = objects.id
190
+ WHERE symbol = $1 AND abi = $2 ORDER BY name;
191
+
192
+ CREATE FUNCTION implementation (
193
+ p_implementation TEXT,
194
+ p_abi TEXT,
195
+ p_exported BOOLEAN,
196
+ OUT implementation_id INTEGER,
197
+ OUT created BOOLEAN
198
+ ) AS $$
199
+ BEGIN
200
+ SELECT INTO implementation_id id FROM objects
201
+ WHERE name = p_implementation AND abi = p_abi;
202
+
203
+ IF implementation_id IS NULL THEN
204
+ created := TRUE;
205
+
206
+ INSERT INTO objects(name, abi, exported)
207
+ VALUES(p_implementation, p_abi, p_exported);
208
+ SELECT INTO implementation_id
209
+ currval(pg_get_serial_sequence('objects', 'id'));
210
+ END IF;
211
+ END;
212
+ $$ LANGUAGE 'plpgsql';
213
+
214
+ CREATE FUNCTION multimplementation (
215
+ p_id INTEGER,
216
+ p_filepath TEXT
217
+ ) RETURNS BOOLEAN AS $$
218
+ BEGIN
219
+ INSERT INTO multimplementations (id, path) VALUES(p_id, p_filepath);
220
+ RETURN 't';
221
+ EXCEPTION
222
+ WHEN unique_violation THEN
223
+ RETURN 'f';
224
+ END;
225
+ $$ LANGUAGE 'plpgsql';
226
+
227
+ CREATE FUNCTION symbol (
228
+ p_object INTEGER,
229
+ p_symbol TEXT
230
+ ) RETURNS VOID AS '
231
+ BEGIN
232
+ INSERT INTO symbols VALUES(p_object, p_symbol);
233
+ RETURN;
234
+ EXCEPTION
235
+ WHEN unique_violation THEN
236
+ RETURN;
237
+ END;
238
+ ' LANGUAGE 'plpgsql';
239
+
240
+ COMMIT;
241
+ EOF
242
+ end
243
+
244
+ def self.db_exec(query)
245
+ @db.exec(query)
246
+ end
247
+
248
+ def self.analysis(filename)
249
+ return if filename =~ @total_suppressions
250
+
251
+ begin
252
+ Elf::File.open(filename) do |elf|
253
+ unless ($machines.nil? or $machines.include?(elf.machine)) and
254
+ (elf.has_section?('.dynsym') and elf.has_section?('.dynstr') and
255
+ elf.has_section?('.dynamic')) and
256
+ (elf[".dynsym"].class == Elf::SymbolTable)
257
+ return
258
+ end
259
+
260
+ local_suppressions = Regexp.union((@partial_suppressions.dup.delete_if{ |s| filename.to_s !~ s[0] }).collect { |s| s[1] })
261
+
262
+ name = filename
263
+ abi = "#{elf.elf_class} #{elf.abi} #{elf.machine}".gsub("'", "''")
264
+
265
+ @multimplementations.each do |implementation, paths|
266
+ # Get the full matchdata because we might need to get the matches.
267
+ match = paths.match(filename)
268
+
269
+ next unless match
270
+
271
+ while implementation =~ /\$([0-9]+)/ do
272
+ match_idx = $1.to_i
273
+ replacement = match[match_idx]
274
+ replacement = "" if replacement.nil?
275
+ implementation = implementation.gsub("$#{match_idx}", replacement)
276
+ end
277
+
278
+ name = implementation
279
+ break
280
+ end
281
+
282
+ shared = (filename != name) || (elf['.dynamic'].soname != nil)
283
+
284
+ res = db_exec("SELECT * FROM implementation('#{name}', '#{abi}', '#{shared}')")
285
+ impid = res[0]["implementation_id"]
286
+
287
+ if filename != name
288
+ # If this is a collapsed multimplementation, add it to the list
289
+ res = db_exec("SELECT multimplementation(#{impid}, '#{filename}') AS created");
290
+ end
291
+
292
+ # skip over the file if we already visited it (either directly
293
+ # or as a multimplementation.
294
+ next if res[0]["created"] != "t"
295
+
296
+ query = ""
297
+ elf['.dynsym'].each do |sym|
298
+ begin
299
+ next if sym.idx == 0 or
300
+ sym.bind != Elf::Symbol::Binding::Global or
301
+ sym.section.nil? or
302
+ sym.value == 0 or
303
+ sym.section.is_a? Integer or
304
+ sym.section.name == '.init' or
305
+ sym.section.name == '.bss'
306
+
307
+ next if (sym.name =~ local_suppressions);
308
+
309
+ query << "SELECT symbol('#{impid}', '#{sym.name}@#{sym.version}');"
310
+ rescue Exception
311
+ $stderr.puts "Mangling symbol #{sym.name}"
312
+ raise
313
+ end
314
+ end
315
+ db_exec("BEGIN TRANSACTION;" + query + "COMMIT;") unless query.empty?
316
+ end
317
+ rescue Exception => e
318
+ putnotice "#{filename}: #{e.message}"
319
+ end
320
+ end
321
+
322
+ def self.results
323
+ db_exec(<<EOF)
324
+ BEGIN TRANSACTION;
325
+ CREATE INDEX objects_name ON objects(name);
326
+ CREATE INDEX symbols_symbol ON symbols(symbol);
327
+ COMMIT;
328
+ EOF
329
+
330
+ outfile = File.new(@output, "w")
331
+
332
+ db_exec("SELECT * FROM duplicate_symbols").each do |row|
333
+ outfile.puts "Symbol #{row['symbol']} (#{row['abi']}) present #{row['occurrences']} times"
334
+ db_exec( "EXECUTE getinstances ('#{row['symbol']}', '#{row['abi'].gsub("'", "''")}')" ).each do |path|
335
+ outfile.puts " #{path['name']}"
336
+ end
337
+ end
338
+ end
339
+ end
340
+ end