|
3 | 3 |
|
4 | 4 | require "compilers" |
5 | 5 |
|
6 | | -class Keg |
7 | | - sig { params(relocation: Relocation, skip_protodesc_cold: T::Boolean).void } |
8 | | - def relocate_dynamic_linkage(relocation, skip_protodesc_cold: false) |
9 | | - # Patching the dynamic linker of glibc breaks it. |
10 | | - return if name.match? Version.formula_optionally_versioned_regex(:glibc) |
| 6 | +module OS |
| 7 | + module Linux |
| 8 | + module Keg |
| 9 | + extend T::Helpers |
11 | 10 |
|
12 | | - old_prefix, new_prefix = relocation.replacement_pair_for(:prefix) |
| 11 | + requires_ancestor { ::Keg } |
13 | 12 |
|
14 | | - elf_files.each do |file| |
15 | | - file.ensure_writable do |
16 | | - change_rpath!(file, old_prefix, new_prefix, skip_protodesc_cold:) |
17 | | - end |
18 | | - end |
19 | | - end |
| 13 | + sig { params(relocation: ::Keg::Relocation, skip_protodesc_cold: T::Boolean).void } |
| 14 | + def relocate_dynamic_linkage(relocation, skip_protodesc_cold: false) |
| 15 | + # Patching the dynamic linker of glibc breaks it. |
| 16 | + return if name.match? Version.formula_optionally_versioned_regex(:glibc) |
| 17 | + |
| 18 | + old_prefix, new_prefix = relocation.replacement_pair_for(:prefix) |
20 | 19 |
|
21 | | - sig { |
22 | | - params(file: Pathname, old_prefix: T.any(String, Regexp), new_prefix: String, |
23 | | - skip_protodesc_cold: T::Boolean).returns(T::Boolean) |
24 | | - } |
25 | | - def change_rpath!(file, old_prefix, new_prefix, skip_protodesc_cold: false) |
26 | | - return false if !file.elf? || !file.dynamic_elf? |
27 | | - |
28 | | - # Skip relocation of files with `protodesc_cold` sections because patchelf.rb seems to break them, |
29 | | - # but only when bottling (as we don't want to break existing bottles that require relocation). |
30 | | - # https://github.com/Homebrew/homebrew-core/pull/232490#issuecomment-3161362452 |
31 | | - return false if skip_protodesc_cold && file.section_names.include?("protodesc_cold") |
32 | | - |
33 | | - updated = {} |
34 | | - old_rpath = file.rpath |
35 | | - new_rpath = if old_rpath |
36 | | - rpath = old_rpath.split(":") |
37 | | - .map { |x| x.sub(old_prefix, new_prefix) } |
38 | | - .select { |x| x.start_with?(new_prefix, "$ORIGIN") } |
39 | | - |
40 | | - lib_path = "#{new_prefix}/lib" |
41 | | - rpath << lib_path unless rpath.include? lib_path |
42 | | - |
43 | | - # Add GCC's lib directory (as of GCC 12+) to RPATH when there is existing versioned linkage. |
44 | | - # This prevents broken linkage when pouring bottles built with an old GCC formula. |
45 | | - unless name.match?(Version.formula_optionally_versioned_regex(:gcc)) |
46 | | - rpath.map! { |rp| rp.sub(%r{lib/gcc/\d+$}, "lib/gcc/current") } |
| 20 | + elf_files.each do |file| |
| 21 | + file.ensure_writable do |
| 22 | + change_rpath!(file, old_prefix, new_prefix, skip_protodesc_cold:) |
| 23 | + end |
| 24 | + end |
47 | 25 | end |
48 | 26 |
|
49 | | - rpath.join(":") |
50 | | - end |
51 | | - updated[:rpath] = new_rpath if old_rpath != new_rpath |
52 | | - |
53 | | - old_interpreter = file.interpreter |
54 | | - new_interpreter = if old_interpreter.nil? |
55 | | - nil |
56 | | - elsif File.readable? "#{new_prefix}/lib/ld.so" |
57 | | - "#{new_prefix}/lib/ld.so" |
58 | | - else |
59 | | - old_interpreter.sub old_prefix, new_prefix |
60 | | - end |
61 | | - updated[:interpreter] = new_interpreter if old_interpreter != new_interpreter |
| 27 | + sig { |
| 28 | + params(file: ::OSPathname, old_prefix: T.any(String, Regexp), new_prefix: String, |
| 29 | + skip_protodesc_cold: T::Boolean).returns(T::Boolean) |
| 30 | + } |
| 31 | + def change_rpath!(file, old_prefix, new_prefix, skip_protodesc_cold: false) |
| 32 | + return false if !file.elf? || !file.dynamic_elf? |
| 33 | + |
| 34 | + # Skip relocation of files with `protodesc_cold` sections because patchelf.rb seems to break them, |
| 35 | + # but only when bottling (as we don't want to break existing bottles that require relocation). |
| 36 | + # https://github.com/Homebrew/homebrew-core/pull/232490#issuecomment-3161362452 |
| 37 | + return false if skip_protodesc_cold && file.section_names.include?("protodesc_cold") |
| 38 | + |
| 39 | + updated = {} |
| 40 | + old_rpath = file.rpath |
| 41 | + new_rpath = if old_rpath |
| 42 | + rpath = old_rpath.split(":") |
| 43 | + .map { |x| x.sub(old_prefix, new_prefix) } |
| 44 | + .select { |x| x.start_with?(new_prefix, "$ORIGIN") } |
| 45 | + |
| 46 | + lib_path = "#{new_prefix}/lib" |
| 47 | + rpath << lib_path unless rpath.include? lib_path |
| 48 | + |
| 49 | + # Add GCC's lib directory (as of GCC 12+) to RPATH when there is existing versioned linkage. |
| 50 | + # This prevents broken linkage when pouring bottles built with an old GCC formula. |
| 51 | + unless name.match?(Version.formula_optionally_versioned_regex(:gcc)) |
| 52 | + rpath.map! { |rp| rp.sub(%r{lib/gcc/\d+$}, "lib/gcc/current") } |
| 53 | + end |
| 54 | + |
| 55 | + rpath.join(":") |
| 56 | + end |
| 57 | + updated[:rpath] = new_rpath if old_rpath != new_rpath |
| 58 | + |
| 59 | + old_interpreter = file.interpreter |
| 60 | + new_interpreter = if old_interpreter.nil? |
| 61 | + nil |
| 62 | + elsif File.readable? "#{new_prefix}/lib/ld.so" |
| 63 | + "#{new_prefix}/lib/ld.so" |
| 64 | + else |
| 65 | + old_interpreter.sub old_prefix, new_prefix |
| 66 | + end |
| 67 | + updated[:interpreter] = new_interpreter if old_interpreter != new_interpreter |
| 68 | + |
| 69 | + file.patch!(interpreter: updated[:interpreter], rpath: updated[:rpath]) |
| 70 | + true |
| 71 | + end |
62 | 72 |
|
63 | | - file.patch!(interpreter: updated[:interpreter], rpath: updated[:rpath]) |
64 | | - true |
65 | | - end |
| 73 | + sig { params(options: T::Hash[Symbol, T::Boolean]).returns(T::Array[Symbol]) } |
| 74 | + def detect_cxx_stdlibs(options = {}) |
| 75 | + skip_executables = options.fetch(:skip_executables, false) |
| 76 | + results = Set.new |
| 77 | + elf_files.each do |file| |
| 78 | + next unless file.dynamic_elf? |
| 79 | + next if file.binary_executable? && skip_executables |
| 80 | + |
| 81 | + dylibs = file.dynamically_linked_libraries |
| 82 | + results << :libcxx if dylibs.any? { |s| s.include? "libc++.so" } |
| 83 | + results << :libstdcxx if dylibs.any? { |s| s.include? "libstdc++.so" } |
| 84 | + end |
| 85 | + results.to_a |
| 86 | + end |
66 | 87 |
|
67 | | - sig { params(options: T::Hash[Symbol, T::Boolean]).returns(T::Array[Symbol]) } |
68 | | - def detect_cxx_stdlibs(options = {}) |
69 | | - skip_executables = options.fetch(:skip_executables, false) |
70 | | - results = Set.new |
71 | | - elf_files.each do |file| |
72 | | - next unless file.dynamic_elf? |
73 | | - next if file.binary_executable? && skip_executables |
74 | | - |
75 | | - dylibs = file.dynamically_linked_libraries |
76 | | - results << :libcxx if dylibs.any? { |s| s.include? "libc++.so" } |
77 | | - results << :libstdcxx if dylibs.any? { |s| s.include? "libstdc++.so" } |
78 | | - end |
79 | | - results.to_a |
80 | | - end |
| 88 | + sig { returns(T::Array[::OSPathname]) } |
| 89 | + def elf_files |
| 90 | + hardlinks = Set.new |
| 91 | + elf_files = [] |
| 92 | + path.find do |pn| |
| 93 | + next if pn.symlink? || pn.directory? |
81 | 94 |
|
82 | | - sig { returns(T::Array[Pathname]) } |
83 | | - def elf_files |
84 | | - hardlinks = Set.new |
85 | | - elf_files = [] |
86 | | - path.find do |pn| |
87 | | - next if pn.symlink? || pn.directory? |
88 | | - next if !pn.dylib? && !pn.binary_executable? |
| 95 | + pn = ::OSPathname.wrap(pn) |
| 96 | + next if !pn.dylib? && !pn.binary_executable? |
89 | 97 |
|
90 | | - # If we've already processed a file, ignore its hardlinks (which have the |
91 | | - # same dev ID and inode). This prevents relocations from being performed |
92 | | - # on a binary more than once. |
93 | | - next unless hardlinks.add? [pn.stat.dev, pn.stat.ino] |
| 98 | + # If we've already processed a file, ignore its hardlinks (which have the |
| 99 | + # same dev ID and inode). This prevents relocations from being performed |
| 100 | + # on a binary more than once. |
| 101 | + next unless hardlinks.add? [pn.stat.dev, pn.stat.ino] |
94 | 102 |
|
95 | | - elf_files << pn |
| 103 | + elf_files << pn |
| 104 | + end |
| 105 | + elf_files |
| 106 | + end |
96 | 107 | end |
97 | | - elf_files |
98 | 108 | end |
99 | 109 | end |
| 110 | + |
| 111 | +Keg.prepend(OS::Linux::Keg) |
0 commit comments