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.
@@ -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