@@ -2063,8 +2063,8 @@ fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor
20632063/// linker, and since they never participate in the linking, using `KEEP` in the linker scripts
20642064/// can't keep them either. This causes #47384.
20652065///
2066- /// To keep them around, we could use `--whole-archive` and equivalents to force rlib to
2067- /// participate in linking like object files, but this proves to be expensive (#93791). Therefore
2066+ /// To keep them around, we could use `--whole-archive`, `-force_load` and equivalents to force rlib
2067+ /// to participate in linking like object files, but this proves to be expensive (#93791). Therefore
20682068/// we instead just introduce an undefined reference to them. This could be done by `-u` command
20692069/// line option to the linker or `EXTERN(...)` in linker scripts, however they does not only
20702070/// introduce an undefined reference, but also make them the GC roots, preventing `--gc-sections`
@@ -2106,8 +2106,20 @@ fn add_linked_symbol_object(
21062106 file. set_mangling ( object:: write:: Mangling :: None ) ;
21072107 }
21082108
2109+ // ld64 requires a relocation to load undefined symbols, see below.
2110+ // Not strictly needed if linking with lld, but might as well do it there too.
2111+ let ld64_section_helper = if file. format ( ) == object:: BinaryFormat :: MachO {
2112+ Some ( file. add_section (
2113+ file. segment_name ( object:: write:: StandardSegment :: Data ) . to_vec ( ) ,
2114+ "__data" . into ( ) ,
2115+ object:: SectionKind :: Data ,
2116+ ) )
2117+ } else {
2118+ None
2119+ } ;
2120+
21092121 for ( sym, kind) in symbols. iter ( ) {
2110- file. add_symbol ( object:: write:: Symbol {
2122+ let symbol = file. add_symbol ( object:: write:: Symbol {
21112123 name : sym. clone ( ) . into ( ) ,
21122124 value : 0 ,
21132125 size : 0 ,
@@ -2121,6 +2133,47 @@ fn add_linked_symbol_object(
21212133 section : object:: write:: SymbolSection :: Undefined ,
21222134 flags : object:: SymbolFlags :: None ,
21232135 } ) ;
2136+
2137+ // The linker shipped with Apple's Xcode, ld64, works a bit differently from other linkers.
2138+ //
2139+ // Code-wise, the relevant parts of ld64 are roughly:
2140+ // 1. Find the `ArchiveLoadMode` based on commandline options, default to `parseObjects`.
2141+ // https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/Options.cpp#L924-L932
2142+ // https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/Options.h#L55
2143+ //
2144+ // 2. Read the archive table of contents (__.SYMDEF file).
2145+ // https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/archive_file.cpp#L294-L325
2146+ //
2147+ // 3. Begin linking by loading "atoms" from input files.
2148+ // https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/doc/design/linker.html
2149+ // https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/InputFiles.cpp#L1349
2150+ //
2151+ // a. Directly specified object files (`.o`) are parsed immediately.
2152+ // https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/macho_relocatable_file.cpp#L4611-L4627
2153+ //
2154+ // - Undefined symbols are not atoms (`n_value > 0` denotes a common symbol).
2155+ // https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/macho_relocatable_file.cpp#L2455-L2468
2156+ // https://maskray.me/blog/2022-02-06-all-about-common-symbols
2157+ //
2158+ // - Relocations/fixups are atoms.
2159+ // https://github.com/apple-oss-distributions/ld64/blob/ce6341ae966b3451aa54eeb049f2be865afbd578/src/ld/parsers/macho_relocatable_file.cpp#L2088-L2114
2160+ //
2161+ // b. Archives are not parsed yet.
2162+ // https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/archive_file.cpp#L467-L577
2163+ //
2164+ // 4. When a symbol is needed by an atom, parse the object file that contains the symbol.
2165+ // https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/InputFiles.cpp#L1417-L1491
2166+ // https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/archive_file.cpp#L579-L597
2167+ //
2168+ // All of the steps above are fairly similar to other linkers, except that **it completely
2169+ // ignores undefined symbols**.
2170+ //
2171+ // So to make this trick work on ld64, we need to do something else to load the relevant
2172+ // object files. We do this by inserting a relocation (fixup) for each symbol.
2173+ if let Some ( section) = ld64_section_helper {
2174+ apple:: add_data_and_relocation ( & mut file, section, symbol, & sess. target , * kind)
2175+ . expect ( "failed adding relocation" ) ;
2176+ }
21242177 }
21252178
21262179 let path = tmpdir. join ( "symbols.o" ) ;
0 commit comments