ruby-elf 1.0.0 → 1.0.1

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/bin/elfgrep CHANGED
@@ -74,16 +74,6 @@ def self.no_filename_cb
74
74
  @print_filename = false
75
75
  end
76
76
 
77
- # we make this a method so that we don't have to worry about deciding
78
- # on the base of how many targets we have; we have to do this
79
- # because we cannot know, in after_options, whether the user passed
80
- # @-file lists.
81
- def self.print_filename
82
- @print_filename = !@single_target if @print_filename.nil?
83
-
84
- @print_filename
85
- end
86
-
87
77
  def self.regexp_cb(pattern)
88
78
  @patterns << pattern
89
79
  end
@@ -99,11 +89,6 @@ def self.before_options
99
89
  end
100
90
 
101
91
  def self.after_options
102
- if @patterns.size ==0
103
- puterror "you need to provide at least expression"
104
- exit -1
105
- end
106
-
107
92
  if @no_match_undefined and @no_match_defined
108
93
  puterror "you need to match at least defined or undefined symbols"
109
94
  exit -1
@@ -112,27 +97,43 @@ def self.after_options
112
97
  @match_undefined = !@no_match_undefined
113
98
  @match_defined = !@no_match_defined
114
99
 
100
+ # if we don't have listed patterns yet (i.e. no -e option), let's
101
+ # check whether we have at least one argument and if that argument
102
+ # doesn't start with '@'. If we have no arguments, the targets are
103
+ # to be given by stdin, and that won't include the actual pattern,
104
+ # if we have an @-prefixed argument, that's a list of targets and
105
+ # can't include the pattern either.
106
+ if (@patterns.size == 0 and
107
+ ARGV.size >= 1 and
108
+ ARGV[0] !~ /^@/)
109
+
110
+ @patterns << ARGV.delete_at(0)
111
+ end
112
+
113
+ if @patterns.size == 0
114
+ puterror "you need to provide at least expression"
115
+ exit -1
116
+ end
117
+
118
+ @print_filename = true if @print_filename.nil? and not single_target?
119
+ @file_prefix_fmt = @print_filename ? "%s#{@null ? "\0" : ":"} " : ""
120
+ @file_list_fmt = "%s#{@null ? "\0" : "\n"}"
121
+
115
122
  regexp_options = @ignore_case ? Regexp::IGNORECASE : 0
116
123
  @regexp = Regexp.union(@patterns.collect { |pattern|
117
- # escape the pattern, so that it works like
118
- # it was a fixed string.
119
- pattern = Regexp.escape(pattern) if @match == :fixed_strings
120
-
121
- Regexp.new(pattern, regexp_options)
122
- })
124
+ if @match == :fixed_strings
125
+ pattern.split(/\r?\n/).collect { |string|
126
+ Regexp.new(Regexp.escape(string), regexp_options)
127
+ }
128
+ else
129
+ Regexp.new(pattern, regexp_options)
130
+ end
131
+ }.flatten)
123
132
  end
124
133
 
125
134
  def self.analysis(file)
126
- if not @print_filename
127
- file_prefix = ""
128
- else
129
- file_prefix = sprintf("%s%s ",
130
- file,
131
- @null ? "\0" : ":")
132
- end
133
- file_list = sprintf("%s%s",
134
- file,
135
- @null ? "\0" : "\n")
135
+ file_prefix = sprintf(@file_prefix_fmt, file)
136
+ file_list = sprintf(@file_list_fmt, file)
136
137
 
137
138
  Elf::File.open(file) do |elf|
138
139
  table = [".dynsym", ".symtab"].find do |table|
data/bin/rbelf-size CHANGED
@@ -43,12 +43,18 @@ def self.analysis(file)
43
43
  :rodata => 0,
44
44
  :relro => 0,
45
45
  :bss => 0,
46
+ :overhead => 0,
46
47
  :total => 0
47
48
  }
48
49
 
49
50
  # Get the size of each section, and then, depending on its type,
50
51
  # flags and eventually name, decide what to sum it to.
51
52
  elf.each_section do |section|
53
+ # This tool only interests itself with size of sections that are
54
+ # loaded into memory at runtime, and not those that only impact
55
+ # the size of the on-disk file.
56
+ next unless section.flags.include?(Elf::Section::Flags::Alloc)
57
+
52
58
  case
53
59
  # When the section is NoBits, it is not allocated in the file,
54
60
  # and is only allocated in ram, this is the case of .bss and
@@ -65,9 +71,7 @@ def self.analysis(file)
65
71
  #
66
72
  # We check further though since we might want to count it
67
73
  # separately.
68
- when (section.flags.include?(Elf::Section::Flags::Write) and
69
- section.flags.include?(Elf::Section::Flags::Alloc))
70
-
74
+ when section.flags.include?(Elf::Section::Flags::Write)
71
75
  # This makes it GCC-specific but that's just because I
72
76
  # cannot find anything in ELF specs that gives an easy way
73
77
  # to deal with this.
@@ -76,7 +80,21 @@ def self.analysis(file)
76
80
  # the exception of prelinking, where this area can then
77
81
  # become mostly read-only and thus not creating dirty pages.
78
82
  sectype = (section.name =~ /^\.data\.rel\.ro(\..+)?/) ? :relro : :data
79
- when section.flags.include?(Elf::Section::Flags::Alloc)
83
+ # A number of sections are loaded into memory on the image but
84
+ # are not really used for anything beside providing metadata for
85
+ # link editor and loader; these section are an object's
86
+ # "overhead" and can usually be reduced by reducing the amount
87
+ # of symbols exposed by the object itself.
88
+ when (section.class == Elf::StringTable or
89
+ section.class == Elf::SymbolTable or
90
+ section.type == Elf::Section::Type::Dynamic or
91
+ section.type == Elf::Section::Type::GNU::VerDef or
92
+ section.type == Elf::Section::Type::GNU::VerNeed or
93
+ section.type == Elf::Section::Type::GNU::VerSym or
94
+ section.type == Elf::Section::Type::Hash or
95
+ section.type == Elf::Section::Type::GNU::Hash)
96
+ sectype = :overhead
97
+ else
80
98
  sectype = :rodata
81
99
  end
82
100
 
@@ -111,13 +129,17 @@ end
111
129
 
112
130
  def self.standard_size(results, file)
113
131
  if @header
114
- puts " exec data rodata relro bss total filename"
132
+ puts " exec data rodata relro bss overhead total filename"
115
133
  @header = false
116
134
  end
117
135
 
118
- results.each_pair do |key, val|
119
- results[key] = val.to_s.rjust(9)
120
- end
121
-
122
- puts "#{results[:exec]} #{results[:data]} #{results[:rodata]} #{results[:relro]} #{results[:bss]} #{results[:total]} #{file}"
136
+ printf("% 9d % 9d % 9d % 9d % 9d % 9d % 9d %s\n",
137
+ results[:exec],
138
+ results[:data],
139
+ results[:rodata],
140
+ results[:relro],
141
+ results[:bss],
142
+ results[:overhead],
143
+ results[:total],
144
+ file)
123
145
  end
data/lib/elf.rb CHANGED
@@ -28,15 +28,8 @@ require 'elf/symbol'
28
28
  require 'elf/file'
29
29
  require 'elf/section'
30
30
 
31
- # We need this quite e lot
32
- class Integer
33
- def hex
34
- sprintf "%x", self
35
- end
36
- end
37
-
38
31
  module Elf
39
- VERSION = "1.0.0"
32
+ VERSION = "1.0.1"
40
33
 
41
34
  MagicString = "\177ELF"
42
35
 
data/lib/elf/file.rb CHANGED
@@ -230,7 +230,8 @@ module Elf
230
230
  # When the section header string table index is set to zero,
231
231
  # there is not going to be a string table in the file, this
232
232
  # happens usually when the file is a static ELF file built
233
- # directly with an assembler.
233
+ # directly with an assembler, or when it was passed through
234
+ # the elfkickers' sstrip utility.
234
235
  #
235
236
  # To handle this specific case, set the @string_table attribute
236
237
  # to false, that is distinct from nil, and raise
@@ -316,7 +317,7 @@ module Elf
316
317
  def has_section?(sect_idx_or_name)
317
318
 
318
319
  if sect_idx_or_name.is_a? String and not @string_table.is_a? Elf::Section
319
- return false if @string_table == false
320
+ raise MissingStringTable.new(sect_idx_or_name) if @string_table == false
320
321
  raise StringTableNotLoaded.new(sect_idx_or_name) if @string_table.nil?
321
322
  end
322
323
 
data/lib/elf/symbol.rb CHANGED
@@ -133,14 +133,18 @@ module Elf
133
133
  end
134
134
 
135
135
  rescue Elf::Value::OutOfBound => e
136
- e.append_message("While processing symbol #{@idx}. Symbol info: 0x#{info.hex}")
136
+ e.append_message(sprintf("While processing symbol %d. Symbol 'info' value: 0x%x",
137
+ @idx,
138
+ info))
137
139
  raise e
138
140
  end
139
141
 
140
142
  begin
141
143
  @visibility = Visibility[@other & 0x03]
142
144
  rescue Elf::Value::OutOfBound => e
143
- e.append_message("While procesing symbol #{@idx}. Symbol other info: 0x#{@other.hex}")
145
+ e.append_message(sprintf("While procesing symbol %d. Symbol 'other' value: 0x%x",
146
+ @idx,
147
+ other))
144
148
  raise e
145
149
  end
146
150
 
@@ -223,7 +227,7 @@ module Elf
223
227
  section = if symbol.section.nil?
224
228
  nil
225
229
  elsif symbol.section.is_a?(Integer)
226
- symbol.section.hex
230
+ sprintf("%x", symbol.section)
227
231
  else
228
232
  symbol.section.name
229
233
  end
data/lib/elf/tools.rb CHANGED
@@ -67,14 +67,9 @@ def self.parse_arguments
67
67
  if opt == "--help"
68
68
  # check if we're executing from a tarball or the git repository,
69
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
70
+ manpage = File.expand_path("../../../manpages/#{to_s}.1", __FILE__)
71
+ manpage = to_s unless File.exist?(manpage)
72
+ exec("man #{manpage}")
78
73
  end
79
74
 
80
75
  attrname = opt.gsub(/^--/, "").gsub("-", "_")
@@ -99,6 +94,16 @@ def self.parse_arguments
99
94
  raise ArgumentError("wrong number of arguments in callback (#{cb.arity} for #{arg.size})")
100
95
  end
101
96
  end
97
+
98
+ @parsed_options = true
99
+ end
100
+
101
+ def self.single_target?
102
+ raise Exception.new("You can't call this until options are parsed") unless @parsed_options
103
+
104
+ # We consider having a single target means that we're given exactly
105
+ # one argument, and that argument is not a targets' list itself.
106
+ return (ARGV.size == 1 and ARGV[0] !~ /^@/)
102
107
  end
103
108
 
104
109
  def execute(filename)
@@ -178,8 +183,6 @@ end
178
183
 
179
184
  # Execute the analysis function on all the lines of a file
180
185
  def self.execute_on_file(file)
181
- @single_target = false
182
-
183
186
  file = $stdin if file == "-"
184
187
  file = File.new(file) if file.class == String
185
188
 
@@ -197,13 +200,6 @@ def self.main
197
200
  parse_arguments
198
201
  after_options if respond_to? :after_options
199
202
 
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
203
  if ARGV.size == 0
208
204
  execute_on_file($stdin)
209
205
  else
@@ -0,0 +1,234 @@
1
+ <?xml version='1.0'?>
2
+ <!--
3
+ Copyright © 2008-2011, Diego "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
+ <article xmlns="http://docbook.org/ns/docbook"
20
+ xmlns:xl="http://www.w3.org/1999/xlink"
21
+ xmlns:xi="http://www.w3.org/2001/XInclude"
22
+ version="5.0" xml:lang="en">
23
+ <info>
24
+ <title>cowstats</title>
25
+
26
+ <xi:include parse="xml" href="author.xmli" />
27
+ </info>
28
+
29
+ <section>
30
+ <title>Reference</title>
31
+
32
+ <refentry>
33
+ <info>
34
+ <date>October 2008</date>
35
+ <productname>ruby-elf</productname>
36
+ </info>
37
+ <refmeta>
38
+ <refentrytitle>cowstats</refentrytitle>
39
+ <manvolnum>1</manvolnum>
40
+ </refmeta>
41
+ <refnamediv>
42
+ <refname>cowstats</refname>
43
+ <refpurpose>ELF Copy-on-Write analyzer</refpurpose>
44
+ </refnamediv>
45
+ <refsynopsisdiv>
46
+ <cmdsynopsis>
47
+ <command>cowstats</command>
48
+ <arg choice="opt"><option>--statistics</option></arg>
49
+ <arg choice="opt"><option>--total</option></arg>
50
+ <arg choice="opt"><option>--ignore-cxx</option></arg>
51
+ <arg choice="opt"><option>--ignore-profiling</option></arg>
52
+ <arg choice="opt"><option>--ignore-data-rel-ro</option></arg>
53
+ <arg choice="opt">
54
+ <option>--sort-by</option>
55
+ <replaceable>section-column</replaceable>
56
+ </arg>
57
+
58
+ <xi:include href="common.xmli" xpointer="xpointer(id('filelist.synopsis')/*)" />
59
+ </cmdsynopsis>
60
+ </refsynopsisdiv>
61
+
62
+ <refsect1>
63
+ <title>Description</title>
64
+ <para>
65
+ <command>cowstats</command> is a script that analyses ELF object files, results of
66
+ compilation of C, C++ or other languages on an Unix system, and reports about the
67
+ variables that enter Copy-on-Write sections.
68
+ </para>
69
+
70
+ <para>
71
+ Static variables (initialised and not) and constant pointers on PIC or PIE enabled object
72
+ files are emitted in the so-called Copy-on-Write sections, which require copying over
73
+ pages from the original ELF executable file to a private resident area of memory at
74
+ runtime.
75
+ </para>
76
+
77
+ <para>
78
+ <command>cowstats</command> reports the possible symbols that were emitted in
79
+ Copy-on-Write sections so that they can be looked after to see if they can be made
80
+ constant and/or removed or reworked.
81
+ </para>
82
+ </refsect1>
83
+
84
+ <refsect1>
85
+ <title>Options</title>
86
+
87
+ <variablelist>
88
+ <varlistentry>
89
+ <term><option>-s</option></term>
90
+ <term><option>--statistics</option></term>
91
+ <listitem>
92
+ <para>
93
+ Instead of reporting all the variables found in Copy-on-Write sections, only
94
+ generate a table showing the sie of data in Copy-on-Write sections per each file,
95
+ divided into <constant>.data</constant>, <constant>.bss</constant> and
96
+ <constant>.data.rel</constant> (for variables, uninitialised variables, and
97
+ relocated variables and constants).
98
+ </para>
99
+ </listitem>
100
+ </varlistentry>
101
+
102
+ <varlistentry>
103
+ <term><option>-t</option></term>
104
+ <term><option>--total</option></term>
105
+ <listitem>
106
+ <para>
107
+ Shows some rough totals for the amount of data in Copy-on-Write sections for the
108
+ program, assuming all the object files given are linked in the same executable. This
109
+ will also show a rough page-based total, which bases itself on 4K-sized pages.
110
+ </para>
111
+ </listitem>
112
+ </varlistentry>
113
+
114
+ <varlistentry>
115
+ <term><option>-x</option></term>
116
+ <term><option>--ignore-cxx</option></term>
117
+ <listitem>
118
+ <para>
119
+ Ignore some C++ entries that could be considered false positives. C++ object files
120
+ will report as CoW data the vtables and typeinfo objects for C++ classes, since they
121
+ are actually emitted in Copy-on-Write sections. Since they cannot be moved from
122
+ thre, this option hides them on the output, to reduce clutter and noise.
123
+ </para>
124
+ </listitem>
125
+ </varlistentry>
126
+
127
+ <varlistentry>
128
+ <term><option>-p</option></term>
129
+ <term><option>--ignore-profiling</option></term>
130
+ <listitem>
131
+ <para>
132
+ Similarly to C++, also profiling (with <command>gcov</command>) will add some
133
+ symbols that would be identified as CoW data. Use this option to avoid reporting
134
+ those symbols.
135
+ </para>
136
+ </listitem>
137
+ </varlistentry>
138
+
139
+ <varlistentry>
140
+ <term><option>-r</option></term>
141
+ <term><option>--ignore-data-rel-ro</option></term>
142
+ <listitem>
143
+ <para>
144
+ Don't report constants found in the .data.rel.ro section, and consider it as
145
+ non-relocated. This is helpful to reduce the noise when looking for writable data
146
+ symbols, or when analysing non-PIC builds.
147
+ </para>
148
+ </listitem>
149
+ </varlistentry>
150
+
151
+ <varlistentry>
152
+ <term>
153
+ <option>-S</option>
154
+ <replaceable>section-column</replaceable>
155
+ </term>
156
+ <term>
157
+ <option>--sort-by</option>
158
+ <replaceable>section-column</replaceable>
159
+ </term>
160
+
161
+ <listitem>
162
+ <para>
163
+ Sort the output of <option>--statistics</option> by the given column. Useful when
164
+ looking for which objects have the most hit for one particular CoW problem. The
165
+ column can be one of the following section names:
166
+ </para>
167
+
168
+ <itemizedlist>
169
+ <listitem><para>.bss</para></listitem>
170
+ <listitem><para>.data</para></listitem>
171
+ <listitem><para>.data.rel</para></listitem>
172
+ <listitem><para>.data.rel.ro</para></listitem>
173
+ </itemizedlist>
174
+ </listitem>
175
+ </varlistentry>
176
+
177
+ <xi:include href="common.xmli" xpointer="xpointer(id('filelist.option')/*)" />
178
+
179
+ </variablelist>
180
+ </refsect1>
181
+
182
+ <refsect1>
183
+ <title>Bugs</title>
184
+
185
+ <para>
186
+ <command>cowstats</command> is still an experiment, and is
187
+ not yet entirely complete, there are thus a number of bugs
188
+ that haven't been discovered or well tested yet.
189
+ </para>
190
+
191
+ <para>
192
+ A known "bug" or misbehaviour is that <command>cowstats</command> cannot know whether
193
+ multple object files will be linked together in the same module (executable or shared
194
+ object) or not. For this reason the output of <option>--total</option> might not be
195
+ consistent with the runtime behaviour of the module itself.
196
+ </para>
197
+
198
+ <xi:include href="common.xmli" xpointer="xpointer(id('filelist.bugpara')/*)" />
199
+ </refsect1>
200
+
201
+ <refsect1>
202
+ <title>See Also</title>
203
+ <para>
204
+ <citation xl:href="http://blog.flameeyes.eu/">Flameeyes's Weblog</citation>
205
+ http://blog.flameeyes.eu/
206
+ </para>
207
+
208
+ <para>
209
+ Related tools:
210
+
211
+ <citerefentry>
212
+ <refentrytitle>rbelf-size</refentrytitle>
213
+ <manvolnum>1</manvolnum>
214
+ </citerefentry>,
215
+
216
+ <citerefentry>
217
+ <refentrytitle>objdump</refentrytitle>
218
+ <manvolnum>1</manvolnum>
219
+ </citerefentry>.
220
+ </para>
221
+ </refsect1>
222
+ </refentry>
223
+ </section>
224
+ </article>
225
+ <!--
226
+ Local Variables:
227
+ mode: nxml
228
+ mode: auto-fill
229
+ mode: flyspell
230
+ ispell-local-dictionary: "english"
231
+ fill-column: 100
232
+ indent-tabs-mode: nil
233
+ End:
234
+ -->