ruby-elf 1.0.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.
- data/COPYING +339 -0
- data/DONATING +42 -0
- data/bin/cowstats +264 -0
- data/bin/elfgrep +185 -0
- data/bin/missingstatic +112 -0
- data/bin/rbelf-size +123 -0
- data/bin/verify-lfs +120 -0
- data/extras/README.extras +5 -0
- data/extras/bindings-parsers.rb +157 -0
- data/lib/bytestream-reader.rb +271 -0
- data/lib/elf.rb +248 -0
- data/lib/elf/dynamic.rb +392 -0
- data/lib/elf/file.rb +366 -0
- data/lib/elf/gnu.rb +174 -0
- data/lib/elf/section.rb +321 -0
- data/lib/elf/stringtable.rb +49 -0
- data/lib/elf/sunw.rb +158 -0
- data/lib/elf/symbol.rb +368 -0
- data/lib/elf/symbol/demangler_gcc3.rb +1952 -0
- data/lib/elf/symboltable.rb +90 -0
- data/lib/elf/tools.rb +228 -0
- data/lib/elf/utils/loader.rb +112 -0
- data/lib/elf/utils/pool.rb +37 -0
- data/lib/elf/value.rb +128 -0
- data/manpages/cowstats.1 +180 -0
- data/manpages/elfgrep.1 +188 -0
- data/manpages/missingstatic.1 +176 -0
- data/manpages/rbelf-size.1 +186 -0
- data/manpages/verify-lfs.1 +95 -0
- data/tools/assess_duplicate_save.rb +105 -0
- data/tools/link-collisions/analyse.rb +57 -0
- data/tools/link-collisions/harvest.rb +367 -0
- data/tools/link-collisions/known-broken +165 -0
- data/tools/link-collisions/multimplementations +125 -0
- data/tools/link-collisions/suppress.rb +84 -0
- data/tools/link-collisions/suppressions +279 -0
- data/tools/nm.rb +78 -0
- data/tools/rbelf-lddtree.rb +49 -0
- data/tools/readelf-d.rb +76 -0
- metadata +114 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Simple ELF parser for Ruby
|
3
|
+
#
|
4
|
+
# Copyright © 2007-2010 Diego E. "Flameeyes" Pettenò <flameeyes@gmail.com>
|
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
|
+
require 'set'
|
25
|
+
|
26
|
+
module Elf
|
27
|
+
class SymbolTable < Section
|
28
|
+
def load_internal
|
29
|
+
@symbols = []
|
30
|
+
@symbol_names = {}
|
31
|
+
for i in 1..(@numentries)
|
32
|
+
sym = Symbol.new(@file, self, i-1)
|
33
|
+
@symbols << sym
|
34
|
+
@symbol_names[sym.name] = sym.idx
|
35
|
+
end
|
36
|
+
|
37
|
+
return nil
|
38
|
+
end
|
39
|
+
|
40
|
+
# Exception thrown when requesting a symbol that is not in the
|
41
|
+
# table
|
42
|
+
class UnknownSymbol < Exception
|
43
|
+
def initialize(name_or_idx, section)
|
44
|
+
super("Symbol #{name_or_idx} not found in section #{section.name}")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def [](idx)
|
49
|
+
load unless @symbols
|
50
|
+
|
51
|
+
if idx.is_a?(Numeric)
|
52
|
+
raise UnknownSymbol.new(idx, self) unless @symbols[idx] != nil
|
53
|
+
return @symbols[idx]
|
54
|
+
elsif idx.respond_to?("to_s")
|
55
|
+
idx = idx.to_s
|
56
|
+
raise UnknownSymbol.new(idx, self) unless @symbol_names.has_key?(idx)
|
57
|
+
return @symbols[@symbol_names[idx]]
|
58
|
+
else
|
59
|
+
raise TypeError.new("wrong argument type #{sect_idx_or_name.class} (expected String or Integer)")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Iterate over each symbols, replaces section.symbol.each
|
64
|
+
def each(&block)
|
65
|
+
symbols.each(&block)
|
66
|
+
end
|
67
|
+
|
68
|
+
include ::Enumerable
|
69
|
+
|
70
|
+
# Return the number of symbols in the section
|
71
|
+
def size
|
72
|
+
symbols.size
|
73
|
+
end
|
74
|
+
|
75
|
+
# Get a set with all the symbols in the table that are defined,
|
76
|
+
# ignoring common, absolute and undefined symbols.
|
77
|
+
def defined_symbols
|
78
|
+
symbols.find_all do |sym|
|
79
|
+
sym.defined?
|
80
|
+
end.to_set
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
def symbols
|
85
|
+
load unless @symbols
|
86
|
+
@symbols
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
data/lib/elf/tools.rb
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright © 2008-2010 Diego E. "Flameeyes" Pettenò <flameeyes@gmail.com>
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this generator; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
17
|
+
|
18
|
+
require 'getoptlong'
|
19
|
+
require 'thread'
|
20
|
+
require 'elf'
|
21
|
+
|
22
|
+
# This file allows to wrap aroudn the most common features of
|
23
|
+
# Ruby-Elf based tools, that follow a series of common traits.
|
24
|
+
#
|
25
|
+
# The tools using this file are tools that inspect a series of ELF
|
26
|
+
# files, passed through command line, stdin, or through a file
|
27
|
+
# parameter; they accept a series of arguments that may or might not
|
28
|
+
# require arguments (in that latter case they are considered on/off
|
29
|
+
# switches), and so on.
|
30
|
+
|
31
|
+
# Gets the name of the tool
|
32
|
+
def self.to_s
|
33
|
+
File.basename($0)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Output an error message, prefixed with the tool name.
|
37
|
+
def self.puterror(string)
|
38
|
+
return if @quiet
|
39
|
+
|
40
|
+
@output_mutex.synchronize {
|
41
|
+
$stderr.puts "#{to_s}: #{string}"
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
# Output a notice about a file, do not prefix with the tool name, do
|
46
|
+
# not print if doing recursive analysis
|
47
|
+
def self.putnotice(message)
|
48
|
+
return if @quiet or @recursive
|
49
|
+
|
50
|
+
@output_mutex.synchronize {
|
51
|
+
$stderr.puts message
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
# Parse the arguments for the tool; it does not parse the @file
|
56
|
+
# options, since they are only expected to contain file names,
|
57
|
+
# rather than options.
|
58
|
+
def self.parse_arguments
|
59
|
+
opts = Options + [
|
60
|
+
["--help", "-?", GetoptLong::NO_ARGUMENT],
|
61
|
+
["--quiet", "-q", GetoptLong::NO_ARGUMENT],
|
62
|
+
["--recursive", "-R", GetoptLong::NO_ARGUMENT],
|
63
|
+
]
|
64
|
+
|
65
|
+
opts = GetoptLong.new(*opts)
|
66
|
+
opts.each do |opt, arg|
|
67
|
+
if opt == "--help"
|
68
|
+
# check if we're executing from a tarball or the git repository,
|
69
|
+
# if so we can't use the system man page.
|
70
|
+
require 'pathname'
|
71
|
+
filepath = Pathname.new($0)
|
72
|
+
localman = filepath.dirname + "../manpages" + filepath.basename.sub(".rb", ".1")
|
73
|
+
if localman.exist?
|
74
|
+
exec("man #{localman.to_s}")
|
75
|
+
else
|
76
|
+
exec("man #{to_s}")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
attrname = opt.gsub(/^--/, "").gsub("-", "_")
|
81
|
+
attrval = arg.size == 0 ? true : arg
|
82
|
+
|
83
|
+
# If there is a function with the same name of the parameter
|
84
|
+
# defined (with a _cb suffix), call that, otherwise set the
|
85
|
+
# attribute with the same name to the given value.
|
86
|
+
cb = method("#{attrname}_cb") rescue nil
|
87
|
+
case
|
88
|
+
when cb.nil?
|
89
|
+
instance_variable_set("@#{attrname}", attrval)
|
90
|
+
when cb.arity == 0
|
91
|
+
raise ArgumentError("wrong number of arguments in callback (0 for 1)") unless
|
92
|
+
arg.size == 0
|
93
|
+
cb.call
|
94
|
+
when cb.arity == 1
|
95
|
+
# fallback to provide a single "true" parameter if there was no
|
96
|
+
# required argument
|
97
|
+
cb.call(attrval)
|
98
|
+
else
|
99
|
+
raise ArgumentError("wrong number of arguments in callback (#{cb.arity} for #{arg.size})")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def execute(filename)
|
105
|
+
begin
|
106
|
+
analysis(filename)
|
107
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::EISDIR, Elf::File::NotAnELF,
|
108
|
+
Elf::File::InvalidElfClass, Elf::File::InvalidDataEncoding,
|
109
|
+
Elf::File::UnsupportedElfVersion, Elf::File::InvalidOsAbi, Elf::File::InvalidElfType,
|
110
|
+
Elf::File::InvalidMachine => e
|
111
|
+
# The Errno exceptions have their message ending in " - FILENAME",
|
112
|
+
# so we take the FILENAME out and just use the one we know
|
113
|
+
# already. We also take out the final dot on the phrase so that
|
114
|
+
# we follow the output messages from other tools, like cat.
|
115
|
+
putnotice "#{filename}: #{e.message.gsub(/\.? - .*/, '')}"
|
116
|
+
rescue Exception => e
|
117
|
+
puterror "#{filename}: #{e.message} (#{e.class})\n\t#{e.backtrace.join("\n\t")}"
|
118
|
+
exit -1
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Try to execute the analysis function on a given filename argument.
|
123
|
+
def self.try_execute(filename)
|
124
|
+
begin
|
125
|
+
# if the file name starts with '@', it is not a target, but a file
|
126
|
+
# with a list of targets, so load it with execute_on_file.
|
127
|
+
if filename[0..0] == "@"
|
128
|
+
execute_on_file(filename[1..-1])
|
129
|
+
return
|
130
|
+
end
|
131
|
+
|
132
|
+
# find the file type so we don't have to look it up many times; if
|
133
|
+
# we're running a recursive scan, we don't want to look into
|
134
|
+
# symlinks as they might create loops or duplicate content, while
|
135
|
+
# we usually want to check them out if they are given directly in
|
136
|
+
# the list of files to analyse
|
137
|
+
file_stat = if @recursive
|
138
|
+
File.lstat(filename)
|
139
|
+
else
|
140
|
+
File.stat(filename)
|
141
|
+
end
|
142
|
+
|
143
|
+
# if the path references a directory, and we're going to run
|
144
|
+
# recursively, descend into that.
|
145
|
+
if @recursive and file_stat.directory?
|
146
|
+
Dir.foreach(filename) do |children|
|
147
|
+
next if children == "." or children == ".."
|
148
|
+
try_execute(File.join(filename, children))
|
149
|
+
end
|
150
|
+
# if the path does not point to a regular file, ignore it
|
151
|
+
elsif not file_stat.file?
|
152
|
+
putnotice "#{filename}: not a regular file"
|
153
|
+
else
|
154
|
+
@execution_threads.add(Thread.new {
|
155
|
+
execute(filename)
|
156
|
+
})
|
157
|
+
end
|
158
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::EISDIR, Elf::File::NotAnELF => e
|
159
|
+
# The Errno exceptions have their message ending in " - FILENAME",
|
160
|
+
# so we take the FILENAME out and just use the one we know
|
161
|
+
# already. We also take out the final dot on the phrase so that
|
162
|
+
# we follow the output messages from other tools, like cat.
|
163
|
+
putnotice "#{filename}: #{e.message.gsub(/\.? - .*/, '')}"
|
164
|
+
rescue SystemExit => e
|
165
|
+
exit e.status
|
166
|
+
rescue Exception => e
|
167
|
+
puterror "#{filename}: #{e.message} (#{e.class})\n\t#{e.backtrace.join("\n\t")}"
|
168
|
+
exit -1
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Execute the analysis function on all the elements of an array.
|
173
|
+
def self.execute_on_array(array)
|
174
|
+
array.each do |filename|
|
175
|
+
try_execute(filename)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Execute the analysis function on all the lines of a file
|
180
|
+
def self.execute_on_file(file)
|
181
|
+
@single_target = false
|
182
|
+
|
183
|
+
file = $stdin if file == "-"
|
184
|
+
file = File.new(file) if file.class == String
|
185
|
+
|
186
|
+
file.each_line do |line|
|
187
|
+
try_execute(line.chomp("\n"))
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.main
|
192
|
+
begin
|
193
|
+
@output_mutex = Mutex.new
|
194
|
+
@execution_threads = ThreadGroup.new
|
195
|
+
|
196
|
+
before_options if respond_to? :before_options
|
197
|
+
parse_arguments
|
198
|
+
after_options if respond_to? :after_options
|
199
|
+
|
200
|
+
# We set the @single_target attribute to true if we're given a
|
201
|
+
# single filename as a target, so that tools like elfgrep can
|
202
|
+
# avoid printing again the filename on the output. Since we could
|
203
|
+
# be given a single @-prefixed file to use as a list, we'll reset
|
204
|
+
# @single_target in self.execute_on_file
|
205
|
+
@single_target = (ARGV.size == 1)
|
206
|
+
|
207
|
+
if ARGV.size == 0
|
208
|
+
execute_on_file($stdin)
|
209
|
+
else
|
210
|
+
execute_on_array(ARGV)
|
211
|
+
end
|
212
|
+
|
213
|
+
@execution_threads.list.each do |thread|
|
214
|
+
thread.join
|
215
|
+
end
|
216
|
+
|
217
|
+
results if respond_to? :results
|
218
|
+
rescue Interrupt
|
219
|
+
puterror "Interrupted"
|
220
|
+
exit 1
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
at_exit do
|
225
|
+
unless $!
|
226
|
+
main
|
227
|
+
end
|
228
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright © 2009-2010 Diego E. "Flameeyes" Pettenò <flameeyes@gmail.com>
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this generator; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
17
|
+
|
18
|
+
require 'elf'
|
19
|
+
require 'elf/utils/pool'
|
20
|
+
|
21
|
+
# This file provides some utilities to deal with the runtime lodader
|
22
|
+
# functions. In particular it provides access to the same kind of
|
23
|
+
# library search as the loader provides.
|
24
|
+
|
25
|
+
module Elf
|
26
|
+
module Utilities
|
27
|
+
@@system_library_path = nil
|
28
|
+
|
29
|
+
# Convenience function to append an array to the list of system
|
30
|
+
# library paths.
|
31
|
+
#
|
32
|
+
# This is just used to avoid repeating the same block of code for
|
33
|
+
# both the data read from /etc/ld.so.conf and from LD_LIBRARY_PATH
|
34
|
+
# environment variable if requested.
|
35
|
+
def self.append_to_library_path(morepaths)
|
36
|
+
morepaths.each do |path|
|
37
|
+
begin
|
38
|
+
# Since we can have symlinks and similar issues, try to
|
39
|
+
# canonicalise the paths so that we can expect them to be
|
40
|
+
# truly unique (and thus never pass through the same directory
|
41
|
+
# twice).
|
42
|
+
@@system_library_path << Pathname.new(path).realpath.to_s
|
43
|
+
rescue Errno::ENOENT, Errno::EACCES
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return the system library path to look for libraries, just like
|
49
|
+
# the loader would.
|
50
|
+
def self.system_library_path
|
51
|
+
|
52
|
+
# Try to cache the request since we most likely have multiple
|
53
|
+
# request per process and we don't care if the settings change
|
54
|
+
# between them.
|
55
|
+
if @@system_library_path.nil?
|
56
|
+
@@system_library_path = Array.new
|
57
|
+
|
58
|
+
# We have to put by default /lib and /usr/lib since they are
|
59
|
+
# implicit in all systems. In particular for Gentoo/Linux
|
60
|
+
# these two are not in the list on x86 systems (but are on
|
61
|
+
# amd64).
|
62
|
+
#
|
63
|
+
# Since LD_LIBRARY_PATH would win over this, but we expect
|
64
|
+
# /etc/ld.so.conf not to, add them here.
|
65
|
+
append_to_library_path(["/lib", "/usr/lib"])
|
66
|
+
|
67
|
+
# We might not have the ld.so.conf file, if that's the case
|
68
|
+
# just ignore it.
|
69
|
+
begin
|
70
|
+
# This implements for now the glibc-style loader
|
71
|
+
# configuration; in the future it might be reimplemented to
|
72
|
+
# take into consideration different operating systems.
|
73
|
+
::File.open("/etc/ld.so.conf") do |ld_so_conf|
|
74
|
+
ld_so_conf.each_line do |line|
|
75
|
+
# Comment lines in the configuration file are prefixed
|
76
|
+
# with the hash character, and the remaining content is
|
77
|
+
# just a single huge list of paths, separated by colon,
|
78
|
+
# comma, space, tabs or newlines.
|
79
|
+
append_to_library_path(line.gsub(/#.*/, '').split(/[:, \t\n]/))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
rescue Errno::ENOENT
|
83
|
+
end
|
84
|
+
|
85
|
+
# Make sure the resulting list is uniq to avoid scanning the
|
86
|
+
# same directory multiple times.
|
87
|
+
@@system_library_path.uniq!
|
88
|
+
end
|
89
|
+
|
90
|
+
return @@system_library_path
|
91
|
+
end
|
92
|
+
|
93
|
+
# Return the environment library path
|
94
|
+
#
|
95
|
+
# We assume the LD_LIBRARY_PATH variable is not going to change
|
96
|
+
# between calls.
|
97
|
+
#
|
98
|
+
# TODO: systems like Solaris implement further variables like
|
99
|
+
# LD_LIBRARY_PATH_32 and LD_LIBRARY_PATH_64, we should pay
|
100
|
+
# attention to those too.
|
101
|
+
def self.environment_library_path
|
102
|
+
return [] if ENV['LD_LIBRARY_PATH'].nil?
|
103
|
+
|
104
|
+
ENV['LD_LIBRARY_PATH'].split(":").collect do |path|
|
105
|
+
begin
|
106
|
+
Pathname.new(path).realpath.to_s
|
107
|
+
rescue Errno::ENOENT, Errno::EACCES
|
108
|
+
end
|
109
|
+
end.uniq
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright © 2009 Alex Legler <a3li@gentoo.org>
|
3
|
+
# Copyright © 2009-2010 Diego E. "Flameeyes" Pettenò <flameeyes@gmail.com>
|
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
|
+
|
22
|
+
module Elf::Utilities
|
23
|
+
# Pool for ELF files.
|
24
|
+
#
|
25
|
+
# This pool is useful for tools that recurse over a tree of
|
26
|
+
# dependencies to avoid creating multiple instances of Elf::File
|
27
|
+
# accessing the same file.
|
28
|
+
class FilePool
|
29
|
+
@pool = Hash.new
|
30
|
+
|
31
|
+
def self.[](file)
|
32
|
+
realfile = Pathname.new(file).realpath
|
33
|
+
|
34
|
+
@pool[realfile] ||= Elf::File.new(realfile)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|