Pass -no_fixup_chains to linker when required

Invoking `ld` with `-undefined dynamic_lookup` emits a warning starting
Xcode 14:

    ld: warning: -undefined dynamic_lookup may not work with chained fixups

Chained fixups is a linker optimisation that results in faster binary
load times, and is enabled by default starting Xcode 13 when the target
is macOS 12 or newer.

However, this interacts poorly with `-undefined dynamic_lookup`, and
Xcode will disable chained fixups when it is invoked with this flag
starting Xcode 14.3. Until then, we may be shipping binaries that are
broken in subtle ways, so let's disable chained fixups when necessary
instead.

I patterned the changes here after the handling of `-no_weak_imports`.
The only difference is that we need to check the flags that were passed
to the linker first to see if we do need to disable chained fixups.

For additional context, see:

https://developer.apple.com/documentation/xcode-release-notes/xcode-13-release-notes
https://www.wwdcnotes.com/notes/wwdc22/110362/
https://www.emergetools.com/blog/posts/iOS15LaunchTime
https://github.com/python/cpython/issues/97524
https://github.com/pybind/pybind11/pull/4301
This commit is contained in:
Carlo Cabrera 2023-03-04 00:02:27 +08:00
parent 41d0265543
commit 1b12d74945
No known key found for this signature in database
GPG Key ID: C74D447FC549A1D0
5 changed files with 35 additions and 0 deletions

View File

@ -105,6 +105,8 @@ module Superenv
# have runtime detection of CPU features. # have runtime detection of CPU features.
# w - Pass -no_weak_imports to the linker # w - Pass -no_weak_imports to the linker
# D - Generate debugging information # D - Generate debugging information
# f - Pass `-no_fixup_chains` to `ld` whenever it
# is invoked with `-undefined dynamic_lookup`
# #
# These flags will also be present: # These flags will also be present:
# a - apply fix for apr-1-config path # a - apply fix for apr-1-config path

View File

@ -23,4 +23,12 @@ module SharedEnvExtension
true true
end end
sig { returns(T::Boolean) }
def no_fixup_chains_support?
return false if !MacOS::CLT.version.null? && MacOS::CLT.version < "13.0"
return false if !MacOS::Xcode.version.null? && MacOS::Xcode.version < "13.0"
true
end
end end

View File

@ -101,4 +101,8 @@ module Stdenv
def no_weak_imports def no_weak_imports
append "LDFLAGS", "-Wl,-no_weak_imports" if no_weak_imports_support? append "LDFLAGS", "-Wl,-no_weak_imports" if no_weak_imports_support?
end end
def no_fixup_chains
append "LDFLAGS", "-Wl,-no_fixup_chains" if no_fixup_chains_support?
end
end end

View File

@ -136,4 +136,11 @@ module Superenv
def no_weak_imports def no_weak_imports
append_to_cccfg "w" if no_weak_imports_support? append_to_cccfg "w" if no_weak_imports_support?
end end
def no_fixup_chains
# Pass `-no_fixup_chains` whenever the linker is invoked with `-undefined dynamic_lookup`.
# See: https://github.com/python/cpython/issues/97524
# https://github.com/pybind/pybind11/pull/4301
append_to_cccfg "f" if no_fixup_chains_support?
end
end end

View File

@ -318,9 +318,11 @@ class Cmd
when :ld when :ld
args << "-headerpad_max_install_names" args << "-headerpad_max_install_names"
args << "-no_weak_imports" if no_weak_imports? args << "-no_weak_imports" if no_weak_imports?
args << "-no_fixup_chains" if no_fixup_chains?
when :ccld, :cxxld when :ccld, :cxxld
args << "-Wl,-headerpad_max_install_names" args << "-Wl,-headerpad_max_install_names"
args << "-Wl,-no_weak_imports" if no_weak_imports? args << "-Wl,-no_weak_imports" if no_weak_imports?
args << "-Wl,-no_fixup_chains" if no_fixup_chains?
end end
args args
end end
@ -416,6 +418,18 @@ class Cmd
config.include?("D") config.include?("D")
end end
def no_fixup_chains?
return false unless config.include?("f")
return true if @args.include?("-Wl,-undefined,dynamic_lookup")
args_consecutive_pairs = @args.each_cons(2)
return true if args_consecutive_pairs.include?(["-undefined", "dynamic_lookup"])
return true if args_consecutive_pairs.include?(["-Wl,-undefined", "-Wl,dynamic_lookup"])
# The next flag would produce an error, but we fix it in `refurbish_arg`.
@args.include?("-undefineddynamic_lookup")
end
def canonical_path(path) def canonical_path(path)
path = Pathname.new(path) path = Pathname.new(path)
path = path.realpath if path.exist? path = path.realpath if path.exist?