From 0191a275ccc3e1a9377c956751bbc1c4ba7b0cea Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Mon, 15 Nov 2021 02:24:16 +0800 Subject: [PATCH] linkage_checker: check variable references with dlopen The linkage check currently does nothing to check the validity of variable-referenced libraries (prefixed with an `@`). We could rectify that by mimicking the dynamic linker in looking up the variable-referenced library, but this could get quite complicated. Instead, let's let the linker do the hard work by checking if we can `dlopen` libraries and bundles that contain variable linkage. The `dlopen` will fail if the linker cannot resolve the variable reference. There are at least two disadvantages to this approach relative to the alternative suggested above: 1. This doesn't work for binary executables. 2. This doesn't identify which variable references are broken. It's still better than not checking them at all, which is what we do currently, and saves us from having to carry around code that parses and verifies variable references directly. --- Library/Homebrew/linkage_checker.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/linkage_checker.rb b/Library/Homebrew/linkage_checker.rb index 9af36adb0e..c3d2bd9fe1 100644 --- a/Library/Homebrew/linkage_checker.rb +++ b/Library/Homebrew/linkage_checker.rb @@ -32,6 +32,7 @@ class LinkageChecker @unnecessary_deps = [] @unwanted_system_dylibs = [] @version_conflict_deps = [] + @broken_variable_dylibs = [] check_dylibs(rebuild_cache: rebuild_cache) end @@ -46,6 +47,7 @@ class LinkageChecker display_items "Undeclared dependencies with linkage", @undeclared_deps display_items "Dependencies with no linkage", @unnecessary_deps display_items "Unwanted system libraries", @unwanted_system_dylibs + display_items "Libraries with broken variable references", @broken_variable_dylibs end def display_reverse_output @@ -68,11 +70,12 @@ class LinkageChecker display_items "Broken dependencies", @broken_deps, puts_output: puts_output display_items "Unwanted system libraries", @unwanted_system_dylibs, puts_output: puts_output display_items "Conflicting libraries", @version_conflict_deps, puts_output: puts_output + display_items "Libraries with broken variable references", @broken_variable_dylibs, puts_output: puts_output end sig { returns(T::Boolean) } def broken_library_linkage? - issues = [@broken_deps, @unwanted_system_dylibs, @version_conflict_deps] + issues = [@broken_deps, @unwanted_system_dylibs, @version_conflict_deps, @broken_variable_dylibs] [issues, unexpected_broken_dylibs, unexpected_present_dylibs].flatten.any?(&:present?) end @@ -148,6 +151,7 @@ class LinkageChecker end checked_dylibs = Set.new + dlopened_if_needed_files = Set.new keg_files_dylibs.each do |file, dylibs| dylibs.each do |dylib| @@ -159,6 +163,15 @@ class LinkageChecker if dylib.start_with? "@" @variable_dylibs << dylib + + if dlopened_if_needed_files.add?(file) + file = Pathname.new(file) + next if file.binary_executable? + next if dylib_found_via_dlopen(file.dylib_id) + + @broken_variable_dylibs << file.dylib_id + end + next end