ruby/tool/leaked-globals
Jeremy Evans ac429df64f Ignore retguard symbols when looking for leaked symbols
retguard symbols are added on OpenBSD as part of stack protection.
They should be ignored by the leaked symbols checker, just as we
ignore asan symbols.
2024-06-04 13:04:41 -07:00

116 lines
2.9 KiB
Ruby
Executable File

#!/usr/bin/ruby
require_relative 'lib/colorize'
require 'shellwords'
until ARGV.empty?
case ARGV[0]
when /\A SYMBOL_PREFIX=(.*)/x
SYMBOL_PREFIX = $1
when /\A NM=(.*)/x # may be multiple words
NM = $1.shellsplit
when /\A PLATFORM=(.+)?/x
platform = $1
when /\A SOEXT=(.+)?/x
soext = $1
when /\A SYMBOLS_IN_EMPTYLIB=(.*)/x
SYMBOLS_IN_EMPTYLIB = $1.split(" ")
when /\A EXTSTATIC=(.+)?/x
EXTSTATIC = true
else
break
end
ARGV.shift
end
SYMBOLS_IN_EMPTYLIB ||= nil
EXTSTATIC ||= false
config = ARGV.shift
count = 0
col = Colorize.new
config_code = File.read(config)
REPLACE = config_code.scan(/\bAC_(?:REPLACE|CHECK)_FUNCS?\((\w+)/).flatten
# REPLACE << 'memcmp' if /\bAC_FUNC_MEMCMP\b/ =~ config_code
REPLACE.push('main', 'DllMain')
if platform and !platform.empty?
begin
h = File.read(platform)
rescue Errno::ENOENT
else
REPLACE.concat(
h .gsub(%r[/\*.*?\*/]m, " ") # delete block comments
.gsub(%r[//.*], " ") # delete oneline comments
.gsub(/^\s*#.*(?:\\\n.*)*/, "") # delete preprocessor directives
.gsub(/(?:\A|;)\K\s*typedef\s.*?;/m, "")
.scan(/\b((?!rb_|DEPRECATED|_)\w+)\s*\(.*\);/)
.flatten)
end
end
missing = File.dirname(config) + "/missing/"
ARGV.reject! do |n|
base = File.basename(n, ".*")
next true if REPLACE.include?(base)
unless (src = Dir.glob(missing + base + ".[cS]")).empty?
puts "Ignore #{col.skip(n)} because of #{src.map {|s| File.basename(s)}.join(', ')} under missing"
true
end
end
# darwin's ld64 seems to require exception handling personality functions to be
# extern, so we allow the Rust one.
REPLACE.push("rust_eh_personality") if RUBY_PLATFORM.include?("darwin")
print "Checking leaked global symbols..."
STDOUT.flush
if soext
soext = /\.#{soext}(?:$|\.)/
if EXTSTATIC
ARGV.delete_if {|n| soext =~ n}
elsif ARGV.size == 1
so = soext =~ ARGV.first
end
end
Pipe = Struct.new(:command) do
def open(&block) IO.popen(command, &block) end
def each(&block) open {|f| f.each(&block)} end
end
Pipe.new(NM + ARGV).each do |line|
line.chomp!
next so = nil if line.empty?
if so.nil? and line.chomp!(":")
so = soext =~ line || false
next
end
n, t, = line.split
next unless /[A-TV-Z]/ =~ t
next unless n.sub!(/^#{SYMBOL_PREFIX}/o, "")
next if n.include?(".")
next if !so and n.start_with?("___asan_")
next if !so and n.start_with?("__odr_asan_")
next if !so and n.start_with?("__retguard_")
case n
when /\A(?:Init_|InitVM_|pm_|[Oo]nig|dln_|coroutine_)/
next
when /\Aruby_static_id_/
next unless so
when /\A(?:RUBY_|ruby_|rb_)/
next unless so and /_(threadptr|ec)_/ =~ n
when *SYMBOLS_IN_EMPTYLIB
next
end
next if REPLACE.include?(n)
puts col.fail("leaked") if count.zero?
count += 1
puts " #{n}"
end
case count
when 0
puts col.pass("none")
when 1
abort col.fail("1 un-prefixed symbol leaked")
else
abort col.fail("#{count} un-prefixed symbols leaked")
end