ruby-elf 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby; coding: utf-8 -*-
3
+ # Copyright © 2011 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/tools'
20
+
21
+ Options = [
22
+ # Expression to match on the symbol name
23
+ ["--regexp", "-e", GetoptLong::REQUIRED_ARGUMENT],
24
+ # Append the version information when matching the symbol
25
+ # name
26
+ ["--match-version", "-V", GetoptLong::NO_ARGUMENT],
27
+ # Don't match undefined symbols
28
+ ["--no-match-undefined", "-U", GetoptLong::NO_ARGUMENT],
29
+ # Don't match defined symbols
30
+ ["--no-match-defined", "-D", GetoptLong::NO_ARGUMENT],
31
+ # Invert selection, show symbols not matching the
32
+ # expression
33
+ ["--invert-match", "-v", GetoptLong::NO_ARGUMENT],
34
+ # List only files with matches
35
+ ["--files-with-matches", "-l", GetoptLong::NO_ARGUMENT],
36
+ # List only files without match
37
+ ["--files-without-match", "-L", GetoptLong::NO_ARGUMENT],
38
+ # Print the name of the file for each match
39
+ ["--with-filename", "-H", GetoptLong::NO_ARGUMENT],
40
+ # Don't print the name of the file for each match
41
+ ["--no-filename", "-h", GetoptLong::NO_ARGUMENT],
42
+ # Only output matches' count
43
+ ["--count", "-c", GetoptLong::NO_ARGUMENT],
44
+ # Match fixed strings and not regular expressions
45
+ ["--fixed-strings", "-F", GetoptLong::NO_ARGUMENT],
46
+ # Make elfgrep case-insensitive
47
+ ["--ignore-case", "-i", GetoptLong::NO_ARGUMENT],
48
+ # Use NULLs to terminate filenames
49
+ ["--null", "-Z", GetoptLong::NO_ARGUMENT],
50
+ ]
51
+
52
+ # We define callbacks for some behaviour-changing options as those
53
+ # will let us consider them positionally, similar to what grep(1)
54
+ # does. If you try -lLl and similar combinations on grep(1), the last
55
+ # one passed is the one to be considered.
56
+
57
+ def self.fixed_strings_cb
58
+ @match = :fixed_strings
59
+ end
60
+
61
+ def self.files_with_matches_cb
62
+ @show = :files_with_matches
63
+ end
64
+
65
+ def self.files_without_match_cb
66
+ @show = :files_without_match
67
+ end
68
+
69
+ def self.with_filename_cb
70
+ @print_filename = true
71
+ end
72
+
73
+ def self.no_filename_cb
74
+ @print_filename = false
75
+ end
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
+ def self.regexp_cb(pattern)
88
+ @patterns << pattern
89
+ end
90
+
91
+ def self.before_options
92
+ @invert_match = false
93
+ @match_undefined = true
94
+ @match_defined = true
95
+ @show = :full_match
96
+ @match = :regexp
97
+
98
+ @patterns = []
99
+ end
100
+
101
+ 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
+ if @no_match_undefined and @no_match_defined
108
+ puterror "you need to match at least defined or undefined symbols"
109
+ exit -1
110
+ end
111
+
112
+ @match_undefined = !@no_match_undefined
113
+ @match_defined = !@no_match_defined
114
+
115
+ regexp_options = @ignore_case ? Regexp::IGNORECASE : 0
116
+ @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
+ })
123
+ end
124
+
125
+ 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")
136
+
137
+ Elf::File.open(file) do |elf|
138
+ table = [".dynsym", ".symtab"].find do |table|
139
+ begin
140
+ (elf[table].class == Elf::SymbolTable)
141
+ rescue Elf::File::MissingSection
142
+ false
143
+ end
144
+ end
145
+
146
+ if table.nil?
147
+ putnotice "#{file}: unable to find symbol table"
148
+ return
149
+ end
150
+
151
+ matches = 0
152
+ elf[table].each do |symbol|
153
+ next if
154
+ (symbol.section == Elf::Section::Abs) or
155
+ (symbol.name == '') or
156
+ (symbol.section == Elf::Section::Undef and
157
+ not @match_undefined) or
158
+ (symbol.section != Elf::Section::Undef and
159
+ not @match_defined)
160
+
161
+ symname = symbol.name
162
+ symname += "@#{symbol.version}" if @match_version
163
+
164
+ # We don't care where it matches, but we do care that it matches
165
+ # or not; we use an invert match since we have to further compare
166
+ # that to @invert_match
167
+ if (@invert_match == (@regexp =~ symname).nil?)
168
+ matches = matches+1
169
+
170
+ break unless @show == :full_match
171
+ next if @count
172
+
173
+ puts "#{file_prefix}#{symbol.address_string} #{symbol.nm_code rescue '?'} #{symname}"
174
+ end
175
+ end
176
+
177
+ if @show == :files_with_matches
178
+ puts file_list if matches > 0
179
+ elsif @show == :files_without_match
180
+ puts file_list if matches == 0
181
+ elsif @count
182
+ puts "#{file_prefix}#{matches}"
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby; coding: utf-8 -*-
3
+ # Copyright © 2008-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
+ # check for functions that are not used but in their translation unit
20
+
21
+ require 'elf/tools'
22
+
23
+ Options = [
24
+ # Exclude functions with a given prefix (exported functions)
25
+ ["--exclude-regexp", "-x", GetoptLong::REQUIRED_ARGUMENT],
26
+ # Only scan hidden symbols, ignore exported ones
27
+ ["--hidden-only", "-h", GetoptLong::NO_ARGUMENT],
28
+ # Show the type of symbol (function, variable, constant)
29
+ ["--show-type", "-t", GetoptLong::NO_ARGUMENT],
30
+ # Exclude symbols present in a tags file (from exuberant-ctags)
31
+ ["--exclude-tags", "-X", GetoptLong::REQUIRED_ARGUMENT]
32
+ ]
33
+
34
+ def self.exclude_tags_cb(arg)
35
+ @exclude_names += File.readlines(arg).delete_if do |line|
36
+ line[0..0] == '!' # Internal exuberant-ctags symbol
37
+ end.collect do |line|
38
+ line.split[0]
39
+ end
40
+ end
41
+
42
+ def self.exclude_regexp_cb(arg)
43
+ @exclude_regexps << Regexp.new(arg)
44
+ end
45
+
46
+ def self.before_options
47
+ # The main symbol is used by all the standalone executables,
48
+ # reporting it is pointless as it will always be a false
49
+ # positive. It cannot be marked static.
50
+ #
51
+ # The excluded_names variable will contain also all the used symbols
52
+ @exclude_names = ["main"]
53
+ @exclude_regexps = []
54
+ @hidden_only = false
55
+ @show_type = false
56
+ end
57
+
58
+ def self.after_options
59
+ @all_defined = []
60
+ end
61
+
62
+ def self.analysis(filename)
63
+ Elf::File.open(filename) do |elf|
64
+ if elf.type != Elf::File::Type::Rel
65
+ putnotice "#{file}: not an object file"
66
+ next
67
+ end
68
+ unless elf.has_section?('.symtab')
69
+ putnotice "#{file}: no .symtab section found"
70
+ next
71
+ end
72
+
73
+ # Gather all the symbols, defined and missing in the translation unit
74
+ elf['.symtab'].each do |sym|
75
+ if sym.section == Elf::Section::Undef
76
+ @exclude_names << sym.name
77
+ elsif sym.bind == Elf::Symbol::Binding::Local
78
+ next
79
+ elsif (sym.section.is_a? Elf::Section) or
80
+ (sym.section == Elf::Section::Common)
81
+ next if @hidden_only and
82
+ sym.visibility != Elf::Symbol::Visibility::Hidden
83
+
84
+ @all_defined << sym
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ def self.results
91
+ @exclude_names.uniq!
92
+
93
+ @all_defined.each do |symbol|
94
+ next if @exclude_names.include? symbol.name
95
+
96
+ excluded = false
97
+ @exclude_regexps.each do |exclude_sym|
98
+ break if excluded = exclude_sym.match(symbol.name)
99
+ end
100
+ next if excluded
101
+
102
+ if @show_type
103
+ begin
104
+ prefix = "#{symbol.nm_code} "
105
+ rescue Elf::Symbol::UnknownNMCode => e
106
+ puterror e.message
107
+ prefix = "? "
108
+ end
109
+ end
110
+ puts "#{prefix}#{symbol.name} (#{symbol.file.path})"
111
+ end
112
+ end
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby; coding: utf-8 -*-
3
+ # Copyright © 2008-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
+ # replacement for size(1) utility that takes into consideration .rodata sections
20
+
21
+ require 'elf/tools'
22
+
23
+ Options = [
24
+ # Give relocation data for shared object assesment
25
+ ["--relocation-stats", "-r", GetoptLong::NO_ARGUMENT],
26
+ # Use deciBel scale for the shared-to-relocated ratio
27
+ ["--decibel", "-d", GetoptLong::NO_ARGUMENT]
28
+ ]
29
+
30
+ def self.before_options
31
+ @relocation_stats = false
32
+ end
33
+
34
+ def self.after_options
35
+ @header = true
36
+ end
37
+
38
+ def self.analysis(file)
39
+ Elf::File.open(file) do |elf|
40
+ results = {
41
+ :exec => 0,
42
+ :data => 0,
43
+ :rodata => 0,
44
+ :relro => 0,
45
+ :bss => 0,
46
+ :total => 0
47
+ }
48
+
49
+ # Get the size of each section, and then, depending on its type,
50
+ # flags and eventually name, decide what to sum it to.
51
+ elf.each_section do |section|
52
+ case
53
+ # When the section is NoBits, it is not allocated in the file,
54
+ # and is only allocated in ram, this is the case of .bss and
55
+ # .tbss sections.
56
+ when section.type == Elf::Section::Type::NoBits
57
+ sectype = :bss
58
+ # If the section contains executable code, count it separately;
59
+ # size(1) will count it all as text, but we don't need to do
60
+ # that.
61
+ when section.flags.include?(Elf::Section::Flags::ExecInstr)
62
+ sectype = :exec
63
+ # If the section is going to be allocated and writeable at
64
+ # runtime, it is usually a data section, of some kind.
65
+ #
66
+ # We check further though since we might want to count it
67
+ # separately.
68
+ when (section.flags.include?(Elf::Section::Flags::Write) and
69
+ section.flags.include?(Elf::Section::Flags::Alloc))
70
+
71
+ # This makes it GCC-specific but that's just because I
72
+ # cannot find anything in ELF specs that gives an easy way
73
+ # to deal with this.
74
+ #
75
+ # By all means, .data.rel.ro is just the same as .data, with
76
+ # the exception of prelinking, where this area can then
77
+ # become mostly read-only and thus not creating dirty pages.
78
+ sectype = (section.name =~ /^\.data\.rel\.ro(\..+)?/) ? :relro : :data
79
+ when section.flags.include?(Elf::Section::Flags::Alloc)
80
+ sectype = :rodata
81
+ end
82
+
83
+ results[sectype] += section.size unless sectype.nil?
84
+ end
85
+
86
+ results[:total] = results.values.inject { |sum, val| sum += val }
87
+
88
+ if @relocation_stats
89
+ relocation_stats(results, file)
90
+ else
91
+ standard_size(results, file)
92
+ end
93
+ end
94
+ end
95
+
96
+ def self.relocation_stats(results, file)
97
+ if @header
98
+ puts " shared private relocated ratio filename"
99
+ @header = false
100
+ end
101
+
102
+ size_shared = results[:exec] + results[:rodata]
103
+ size_private = results[:data] + results[:bss]
104
+ size_relocated = results[:relro]
105
+
106
+ ratio = size_shared.to_f/size_relocated
107
+ ratio = 10 * Math::log10(ratio) if @decibel
108
+
109
+ printf "% 12s % 12s % 12s % 12.2f %s\n", size_shared, size_private, size_relocated, ratio, file
110
+ end
111
+
112
+ def self.standard_size(results, file)
113
+ if @header
114
+ puts " exec data rodata relro bss total filename"
115
+ @header = false
116
+ end
117
+
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}"
123
+ end
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby; coding: utf-8 -*-
3
+ # Copyright © 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
+ # This script verifies whether a file or a tree of files is using
20
+ # non-LFS interfaces, or mixing non-LFS and LFS interfaces.
21
+
22
+ require 'elf/tools'
23
+
24
+ Options = [
25
+ # Scan only object files (rather than non-object files)
26
+ ["--objects", "-o", GetoptLong::NO_ARGUMENT]
27
+ ]
28
+
29
+ SymbolNamesList = [
30
+ "__[fl]?xstat",
31
+ "statv?fs",
32
+ "(|_IO_)f[gs]etpos",
33
+ "(read|scan)dir",
34
+ "getdirentries",
35
+ "mko?stemp",
36
+ "(|__)p(read|write)",
37
+ "p(read|write)v",
38
+ "(send|tmp)file",
39
+ "[gs]etrlimit",
40
+ "versionsort",
41
+ "f?truncate",
42
+ "(|f(|re))open",
43
+ "openat",
44
+ "fseeko",
45
+ "ftello",
46
+ "lseek",
47
+ "glob(|free)",
48
+ "ftw",
49
+ "lockf"
50
+ ]
51
+
52
+ SymbolNames = "(#{SymbolNamesList.join("|")})"
53
+
54
+ def self.before_options
55
+ end
56
+
57
+ def self.after_options
58
+ @files_mixing = []
59
+ @files_nolfs = []
60
+
61
+ if @objects
62
+ @elftypes = [ Elf::File::Type::Rel ]
63
+ @elfdescr = "a relocatable object file"
64
+ @elftable = ".symtab"
65
+ else
66
+ @elftypes = [ Elf::File::Type::Exec, Elf::File::Type::Dyn ]
67
+ @elfdescr = "an executable or dynamic file"
68
+ @elftable = ".dynsym"
69
+ end
70
+ end
71
+
72
+ def self.analysis(file)
73
+ Elf::File.open(file) do |elf|
74
+ unless @elftypes.include? elf.type
75
+ putnotice "#{file}: not #{@elfdescr}"
76
+ next
77
+ end
78
+
79
+ if not elf.has_section?(@elftable) or elf[@elftable].class != Elf::SymbolTable
80
+ putnotice "#{file}: not a dynamically linked file"
81
+ next
82
+ end
83
+
84
+ if elf.elf_class == Elf::Class::Elf64
85
+ putnotice "#{file}: testing 64-bit ELF files is meaningless"
86
+ next
87
+ end
88
+
89
+ use_stat32 = false
90
+ use_stat64 = false
91
+
92
+ elf[@elftable].each do |symbol|
93
+ next unless symbol.section == Elf::Section::Undef
94
+
95
+ use_stat32 ||= (symbol.to_s =~ /^#{SymbolNames}$/)
96
+ use_stat64 ||= (symbol.to_s =~ /^#{SymbolNames}64$/)
97
+
98
+ # avoid running the whole list if we hit both as we cannot hit
99
+ # _more_
100
+ break if use_stat32 and use_stat64
101
+ end
102
+
103
+ if use_stat32 and use_stat64
104
+ @files_mixing << file
105
+ elsif use_stat32 and not use_stat64
106
+ @files_nolfs << file
107
+ end
108
+ end
109
+ end
110
+
111
+ def self.results
112
+ if @files_mixing.size > 0
113
+ puts "The following files are mixing LFS and non-LFS library calls:"
114
+ puts " " + @files_mixing.join("\n ")
115
+ end
116
+ if @files_nolfs.size > 0
117
+ puts "The following files are using non-LFS library calls:"
118
+ puts " " + @files_nolfs.join("\n ")
119
+ end
120
+ end