Class: Elf::Tool

Inherits:
Object
  • Object
show all
Defined in:
lib/elf/tools.rb

Class Method Summary collapse

Class Method Details

.after_optionsObject



68
69
# File 'lib/elf/tools.rb', line 68

def self.after_options
end

.execute(filename) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/elf/tools.rb', line 119

def self.execute(filename)
  begin
    analysis(filename)
  rescue Errno::ENOENT, Errno::EACCES, Errno::EISDIR, Elf::File::NotAnELF,
    Elf::File::InvalidElfClass, Elf::File::InvalidDataEncoding,
    Elf::File::UnsupportedElfVersion, Elf::File::InvalidOsAbi, Elf::File::InvalidElfType,
    Elf::File::InvalidMachine => e
    # The Errno exceptions have their message ending in " - FILENAME",
    # so we take the FILENAME out and just use the one we know
    # already.  We also take out the final dot on the phrase so that
    # we follow the output messages from other tools, like cat.
    putnotice "#{filename}: #{e.message.gsub(/\.? - .*/, '')}"
  rescue Exception => e
    puterror "#{filename}: #{e.message} (#{e.class})\n\t#{e.backtrace.join("\n\t")}"
    exit -1
  end
end

.execute_on(param) ⇒ Object

Execute the analysis function on all the elements of an array.



193
194
195
196
197
198
199
200
# File 'lib/elf/tools.rb', line 193

def self.execute_on(param)
  param = ::File.new(param) if param.is_a? String
  param = param.read.split(/\r?\n/) if param.is_a? IO

  param.each do |filename|
    try_execute(filename)
  end
end

.inherited(klass) ⇒ Object

Raises:

  • (Exception)


33
34
35
36
37
38
39
40
41
42
# File 'lib/elf/tools.rb', line 33

def self.inherited(klass)
  raise Exception.new("Another Tool has been already defined") if @tool_defined
  @tool_defined = true

  at_exit do
    unless $!
      klass.main
    end
  end
end

.initializeObject



205
206
207
208
209
210
211
212
213
214
# File 'lib/elf/tools.rb', line 205

def self.initialize
  @output_mutex = Mutex.new
  @execution_threads = ThreadGroup.new

  @options = [
              ["--help", "-?", GetoptLong::NO_ARGUMENT],
              ["--quiet", "-q", GetoptLong::NO_ARGUMENT],
              ["--recursive", "-R", GetoptLong::NO_ARGUMENT],
             ]
end

.mainObject



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/elf/tools.rb', line 216

def self.main
  initialize

  begin
    parse_arguments

    # collect all the arguments passed; if the argument starts
    # with '@', then open the file and split the lines in further
    # arguments.
    @targets = ARGV.collect { |argument|
      if argument[0..0] == "@"
        ::File.read(argument[1..-1]).split(/\r?\n/)
      else
        argument
      end
    }.flatten

    after_options

    # if we have no targets (neither direct arguments, nor added
    # by #after_options, we readthe targets from stdin.
    if @targets.empty?
      $stdin.each_line { |input|
        try_execute(input.sub(/\r?\n/, ''))
      }
    else
      $stdin.close
      @targets.uniq.each { |target| try_execute(target) }
    end

    if @execution_threads
      @execution_threads.list.each do |thread|
        thread.join
      end
    end

    results
  rescue Interrupt
    puterror "Interrupted"
    exit 1
  end
end

.parse_argumentsObject

Parse the arguments for the tool; it does not parse the @file options, since they are only expected to contain file names, rather than options.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/elf/tools.rb', line 74

def self.parse_arguments
  opts = GetoptLong.new(*@options)
  opts.each do |opt, arg|
    if opt == "--help"
      # check if we're executing from a tarball or the git repository,
      # if so we can't use the system man page.
      manpage = File.expand_path("../../../manpages/#{to_s}.1", __FILE__)
      manpage = to_s unless File.exist?(manpage)
      exec("man #{manpage}")
    end

    attrname = opt.gsub(/^--/, "").gsub("-", "_")
    attrval = arg.size == 0 ? true : arg

    # If there is a function with the same name of the parameter
    # defined (with a _cb suffix), call that, otherwise set the
    # attribute with the same name to the given value.
    cb = method("#{attrname}_cb") rescue nil
    case
    when cb.nil?
      instance_variable_set("@#{attrname}", attrval)
    when cb.arity == 0
      raise ArgumentError("wrong number of arguments in callback (0 for 1)") unless
        arg.size == 0
      cb.call
    when cb.arity == 1
      # fallback to provide a single "true" parameter if there was no
      # required argument
      cb.call(attrval)
    else
      raise ArgumentError("wrong number of arguments in callback (#{cb.arity} for #{arg.size})")
    end
  end

  @parsed_options = true
end

.puterror(string) ⇒ Object

Output an error message, prefixed with the tool name.



50
51
52
53
54
55
56
# File 'lib/elf/tools.rb', line 50

def self.puterror(string)
  return if @quiet

  @output_mutex.synchronize {
    $stderr.puts "#{to_s}: #{string}"
  }
end

.putnotice(message) ⇒ Object

Output a notice about a file, do not prefix with the tool name, do not print if doing recursive analysis



60
61
62
63
64
65
66
# File 'lib/elf/tools.rb', line 60

def self.putnotice(message)
  return if @quiet or @recursive

  @output_mutex.synchronize {
    $stderr.puts message
  }
end

.resultsObject



202
203
# File 'lib/elf/tools.rb', line 202

def self.results
end

.single_target?Boolean

Returns:

  • (Boolean)

Raises:

  • (Exception)


111
112
113
114
115
116
117
# File 'lib/elf/tools.rb', line 111

def self.single_target?
  raise Exception.new("You can't call this until options are parsed") unless @parsed_options

  # We consider having a single target means that we're given exactly
  # one argument, and that argument is not a targets' list itself.
  return @targets.size == 1
end

.thread_execute(filename) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/elf/tools.rb', line 137

def self.thread_execute(filename)
  # If our child set @execution_threads to nil, it doesn't really
  # support running multithreaded, this is the case for instance
  # of the link collision harvester script, where the db access
  # and pkey generation has to be synchronous.
  unless @execution_threads.nil?
    @execution_threads.add(Thread.new {
                             execute(filename)
                           })
  else
    execute(filename)
  end
end

.to_sObject

Gets the name of the tool



45
46
47
# File 'lib/elf/tools.rb', line 45

def self.to_s
  File.basename($0)
end

.try_execute(filename) ⇒ Object

Try to execute the analysis function on a given filename argument.



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/elf/tools.rb', line 152

def self.try_execute(filename)
  begin
    # find the file type so we don't have to look it up many times; if
    # we're running a recursive scan, we don't want to look into
    # symlinks as they might create loops or duplicate content, while
    # we usually want to check them out if they are given directly in
    # the list of files to analyse
    file_stat = if @recursive
                  File.lstat(filename)
                else
                  File.stat(filename)
                end

    # if the path references a directory, and we're going to run
    # recursively, descend into that.
    if @recursive and file_stat.directory?
      Dir.foreach(filename) do |children|
        next if children == "." or children == ".."
        try_execute(File.join(filename, children))
      end
      # if the path does not point to a regular file, ignore it
    elsif not file_stat.file?
      putnotice "#{filename}: not a regular file"
    else
      thread_execute(filename)
    end
  rescue Errno::ENOENT, Errno::EACCES, Errno::EISDIR, Elf::File::NotAnELF => e
    # The Errno exceptions have their message ending in " - FILENAME",
    # so we take the FILENAME out and just use the one we know
    # already.  We also take out the final dot on the phrase so that
    # we follow the output messages from other tools, like cat.
    putnotice "#{filename}: #{e.message.gsub(/\.? - .*/, '')}"
  rescue SystemExit => e
    exit e.status
  rescue Exception => e
    puterror "#{filename}: #{e.message} (#{e.class})\n\t#{e.backtrace.join("\n\t")}"
    exit -1
  end
end