mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
Under -flto the compiler emits raw LLVM bitcode objects, but the configured nm carries --no-llvm-bc (added so it ignores Rust's newer-version bitcode) and rejects them with "not recognized", flooding the clang-22 LTO build log. leaked-globals now drops those objects, still checking the linked library and any ELF objects, and the symbol-prefix probe in configure silences nm's stderr. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
134 lines
3.7 KiB
Ruby
Executable File
134 lines
3.7 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")
|
|
|
|
# Under LTO the compiler emits raw LLVM bitcode objects (magic "BC\xC0\xDE", or
|
|
# the bitcode wrapper magic). nm carries --no-llvm-bc (added in configure so it
|
|
# ignores Rust's newer-version bitcode) and so cannot read them, only emitting
|
|
# "not recognized" noise. Drop them; leaked symbols are still checked on the
|
|
# linked library and any non-bitcode objects.
|
|
if defined?(NM) and NM.include?("--no-llvm-bc")
|
|
bitcode = 0
|
|
ARGV.reject! do |n|
|
|
next false unless File.file?(n)
|
|
case File.binread(n, 4)&.bytes
|
|
when [0x42, 0x43, 0xC0, 0xDE], [0xDE, 0xC0, 0x17, 0x0B]
|
|
bitcode += 1
|
|
end
|
|
end
|
|
puts "Skip #{col.skip("#{bitcode} LLVM bitcode object(s)")}" if bitcode > 0
|
|
end
|
|
|
|
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_")
|
|
next if !so and n.start_with?("__dtrace")
|
|
case n
|
|
when /\A(?:Init_|InitVM_|pm_|[Oo]nig|dln_|coroutine_)/
|
|
next
|
|
when /\Aruby_static_id_/
|
|
next unless so
|
|
when /\A(?:RUBY_|ruby_|rb_|rbimpl_)/
|
|
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 unless ARGV.empty?
|
|
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
|