| ... | @@ -1853,6 +1853,9 @@ pub fn resolveInputs( | ... | @@ -1853,6 +1853,9 @@ pub fn resolveInputs( |
| 1853 | var ld_script_bytes: std.ArrayList(u8) = .empty; | 1853 | var ld_script_bytes: std.ArrayList(u8) = .empty; |
| 1854 | defer ld_script_bytes.deinit(gpa); | 1854 | defer ld_script_bytes.deinit(gpa); |
| 1855 | | 1855 | |
| | 1856 | var archive_dedup: ArchiveDedupMap = .empty; |
| | 1857 | defer archive_dedup.deinit(gpa); |
| | 1858 | |
| 1856 | var failed_libs: std.ArrayList(struct { | 1859 | var failed_libs: std.ArrayList(struct { |
| 1857 | name: []const u8, | 1860 | name: []const u8, |
| 1858 | strategy: UnresolvedInput.SearchStrategy, | 1861 | strategy: UnresolvedInput.SearchStrategy, |
| ... | @@ -1892,6 +1895,7 @@ pub fn resolveInputs( | ... | @@ -1892,6 +1895,7 @@ pub fn resolveInputs( |
| 1892 | resolved_inputs, | 1895 | resolved_inputs, |
| 1893 | &checked_paths, | 1896 | &checked_paths, |
| 1894 | &ld_script_bytes, | 1897 | &ld_script_bytes, |
| | 1898 | &archive_dedup, |
| 1895 | lib_directory, | 1899 | lib_directory, |
| 1896 | name_query, | 1900 | name_query, |
| 1897 | target, | 1901 | target, |
| ... | @@ -1919,6 +1923,7 @@ pub fn resolveInputs( | ... | @@ -1919,6 +1923,7 @@ pub fn resolveInputs( |
| 1919 | resolved_inputs, | 1923 | resolved_inputs, |
| 1920 | &checked_paths, | 1924 | &checked_paths, |
| 1921 | &ld_script_bytes, | 1925 | &ld_script_bytes, |
| | 1926 | &archive_dedup, |
| 1922 | lib_directory, | 1927 | lib_directory, |
| 1923 | name_query, | 1928 | name_query, |
| 1924 | target, | 1929 | target, |
| ... | @@ -1947,6 +1952,7 @@ pub fn resolveInputs( | ... | @@ -1947,6 +1952,7 @@ pub fn resolveInputs( |
| 1947 | resolved_inputs, | 1952 | resolved_inputs, |
| 1948 | &checked_paths, | 1953 | &checked_paths, |
| 1949 | &ld_script_bytes, | 1954 | &ld_script_bytes, |
| | 1955 | &archive_dedup, |
| 1950 | lib_directory, | 1956 | lib_directory, |
| 1951 | name_query, | 1957 | name_query, |
| 1952 | target, | 1958 | target, |
| ... | @@ -1966,6 +1972,7 @@ pub fn resolveInputs( | ... | @@ -1966,6 +1972,7 @@ pub fn resolveInputs( |
| 1966 | resolved_inputs, | 1972 | resolved_inputs, |
| 1967 | &checked_paths, | 1973 | &checked_paths, |
| 1968 | &ld_script_bytes, | 1974 | &ld_script_bytes, |
| | 1975 | &archive_dedup, |
| 1969 | lib_directory, | 1976 | lib_directory, |
| 1970 | name_query, | 1977 | name_query, |
| 1971 | target, | 1978 | target, |
| ... | @@ -1997,6 +2004,7 @@ pub fn resolveInputs( | ... | @@ -1997,6 +2004,7 @@ pub fn resolveInputs( |
| 1997 | unresolved_inputs, | 2004 | unresolved_inputs, |
| 1998 | resolved_inputs, | 2005 | resolved_inputs, |
| 1999 | &ld_script_bytes, | 2006 | &ld_script_bytes, |
| | 2007 | &archive_dedup, |
| 2000 | target, | 2008 | target, |
| 2001 | .{ | 2009 | .{ |
| 2002 | .path = Path.initCwd(an.name), | 2010 | .path = Path.initCwd(an.name), |
| ... | @@ -2015,6 +2023,7 @@ pub fn resolveInputs( | ... | @@ -2015,6 +2023,7 @@ pub fn resolveInputs( |
| 2015 | unresolved_inputs, | 2023 | unresolved_inputs, |
| 2016 | resolved_inputs, | 2024 | resolved_inputs, |
| 2017 | &ld_script_bytes, | 2025 | &ld_script_bytes, |
| | 2026 | &archive_dedup, |
| 2018 | target, | 2027 | target, |
| 2019 | .{ | 2028 | .{ |
| 2020 | .path = .{ | 2029 | .path = .{ |
| ... | @@ -2043,6 +2052,7 @@ pub fn resolveInputs( | ... | @@ -2043,6 +2052,7 @@ pub fn resolveInputs( |
| 2043 | unresolved_inputs, | 2052 | unresolved_inputs, |
| 2044 | resolved_inputs, | 2053 | resolved_inputs, |
| 2045 | &ld_script_bytes, | 2054 | &ld_script_bytes, |
| | 2055 | &archive_dedup, |
| 2046 | target, | 2056 | target, |
| 2047 | pq, | 2057 | pq, |
| 2048 | color, | 2058 | color, |
| ... | @@ -2088,6 +2098,8 @@ fn resolveLibInput( | ... | @@ -2088,6 +2098,8 @@ fn resolveLibInput( |
| 2088 | checked_paths: *std.ArrayList(u8), | 2098 | checked_paths: *std.ArrayList(u8), |
| 2089 | /// Allocated via `gpa`. | 2099 | /// Allocated via `gpa`. |
| 2090 | ld_script_bytes: *std.ArrayList(u8), | 2100 | ld_script_bytes: *std.ArrayList(u8), |
| | 2101 | /// Allocated via `gpa`. |
| | 2102 | archive_dedup: *ArchiveDedupMap, |
| 2091 | lib_directory: Directory, | 2103 | lib_directory: Directory, |
| 2092 | name_query: UnresolvedInput.NameQuery, | 2104 | name_query: UnresolvedInput.NameQuery, |
| 2093 | target: *const std.Target, | 2105 | target: *const std.Target, |
| ... | @@ -2095,6 +2107,7 @@ fn resolveLibInput( | ... | @@ -2095,6 +2107,7 @@ fn resolveLibInput( |
| 2095 | color: std.zig.Color, | 2107 | color: std.zig.Color, |
| 2096 | ) Allocator.Error!ResolveLibInputResult { | 2108 | ) Allocator.Error!ResolveLibInputResult { |
| 2097 | try resolved_inputs.ensureUnusedCapacity(gpa, 1); | 2109 | try resolved_inputs.ensureUnusedCapacity(gpa, 1); |
| | 2110 | try archive_dedup.ensureUnusedCapacity(gpa, 1); |
| 2098 | | 2111 | |
| 2099 | const lib_name = name_query.name; | 2112 | const lib_name = name_query.name; |
| 2100 | | 2113 | |
| ... | @@ -2110,7 +2123,7 @@ fn resolveLibInput( | ... | @@ -2110,7 +2123,7 @@ fn resolveLibInput( |
| 2110 | else => |e| fatal("unable to search for tbd library '{f}': {s}", .{ test_path, @errorName(e) }), | 2123 | else => |e| fatal("unable to search for tbd library '{f}': {s}", .{ test_path, @errorName(e) }), |
| 2111 | }; | 2124 | }; |
| 2112 | errdefer file.close(io); | 2125 | errdefer file.close(io); |
| 2113 | return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query); | 2126 | return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, name_query.query); |
| 2114 | } | 2127 | } |
| 2115 | | 2128 | |
| 2116 | { | 2129 | { |
| ... | @@ -2125,7 +2138,7 @@ fn resolveLibInput( | ... | @@ -2125,7 +2138,7 @@ fn resolveLibInput( |
| 2125 | }), | 2138 | }), |
| 2126 | }; | 2139 | }; |
| 2127 | try checked_paths.print(gpa, "\n {f}", .{test_path}); | 2140 | try checked_paths.print(gpa, "\n {f}", .{test_path}); |
| 2128 | switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, target, .{ | 2141 | switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, .{ |
| 2129 | .path = test_path, | 2142 | .path = test_path, |
| 2130 | .query = name_query.query, | 2143 | .query = name_query.query, |
| 2131 | }, link_mode, color)) { | 2144 | }, link_mode, color)) { |
| ... | @@ -2149,7 +2162,7 @@ fn resolveLibInput( | ... | @@ -2149,7 +2162,7 @@ fn resolveLibInput( |
| 2149 | }), | 2162 | }), |
| 2150 | }; | 2163 | }; |
| 2151 | errdefer file.close(io); | 2164 | errdefer file.close(io); |
| 2152 | return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query); | 2165 | return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, name_query.query); |
| 2153 | } | 2166 | } |
| 2154 | | 2167 | |
| 2155 | // In the case of MinGW, the main check will be .lib but we also need to | 2168 | // In the case of MinGW, the main check will be .lib but we also need to |
| ... | @@ -2165,26 +2178,61 @@ fn resolveLibInput( | ... | @@ -2165,26 +2178,61 @@ fn resolveLibInput( |
| 2165 | else => |e| fatal("unable to search for static library '{f}': {s}", .{ test_path, @errorName(e) }), | 2178 | else => |e| fatal("unable to search for static library '{f}': {s}", .{ test_path, @errorName(e) }), |
| 2166 | }; | 2179 | }; |
| 2167 | errdefer file.close(io); | 2180 | errdefer file.close(io); |
| 2168 | return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query); | 2181 | return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, name_query.query); |
| 2169 | } | 2182 | } |
| 2170 | | 2183 | |
| 2171 | return .no_match; | 2184 | return .no_match; |
| 2172 | } | 2185 | } |
| 2173 | | 2186 | |
| | 2187 | /// Deduplicates static archive link inputs based on their path. This is done for efficiency, so |
| | 2188 | /// that linker implementations do not need to open and scan the archive just to determine that they |
| | 2189 | /// need not extract any objects. At the time of writing, it also helps avoid "multiple definitions |
| | 2190 | /// of symbol" errors in incomplete linker implementations. |
| | 2191 | /// |
| | 2192 | /// Key is index into `resolved_inputs` of an `Input.archive`. |
| | 2193 | /// |
| | 2194 | /// Accessed through `ArchiveDedupAdapter`. |
| | 2195 | /// |
| | 2196 | const ArchiveDedupMap = std.array_hash_map.Custom(u32, void, void, true); |
| | 2197 | /// Adapter for accessing `ArchiveDedupMap` with an effective key type of `Path`. |
| | 2198 | const ArchiveDedupAdapter = struct { |
| | 2199 | resolved_inputs: []const Input, |
| | 2200 | pub fn hash(ctx: ArchiveDedupAdapter, path: Path) u32 { |
| | 2201 | _ = ctx; |
| | 2202 | return Path.TableAdapter.hash(.{}, path); |
| | 2203 | } |
| | 2204 | pub fn eql(ctx: ArchiveDedupAdapter, a_path: Path, b_input_index: u32, _: usize) bool { |
| | 2205 | const b_path = ctx.resolved_inputs[b_input_index].archive.path; |
| | 2206 | return a_path.eql(b_path); |
| | 2207 | } |
| | 2208 | }; |
| | 2209 | |
| 2174 | fn finishResolveLibInput( | 2210 | fn finishResolveLibInput( |
| | 2211 | io: Io, |
| 2175 | resolved_inputs: *std.ArrayList(Input), | 2212 | resolved_inputs: *std.ArrayList(Input), |
| | 2213 | archive_dedup: *ArchiveDedupMap, |
| 2176 | path: Path, | 2214 | path: Path, |
| 2177 | file: Io.File, | 2215 | file: Io.File, |
| 2178 | link_mode: std.lang.LinkMode, | 2216 | link_mode: std.lang.LinkMode, |
| 2179 | query: UnresolvedInput.Query, | 2217 | query: UnresolvedInput.Query, |
| 2180 | ) ResolveLibInputResult { | 2218 | ) ResolveLibInputResult { |
| 2181 | switch (link_mode) { | 2219 | switch (link_mode) { |
| 2182 | .static => resolved_inputs.appendAssumeCapacity(.{ .archive = .{ | 2220 | .static => { |
| 2183 | .path = path, | 2221 | const ctx: ArchiveDedupAdapter = .{ .resolved_inputs = resolved_inputs.items }; |
| 2184 | .file = file, | 2222 | const gop = archive_dedup.getOrPutAssumeCapacityAdapted(path, ctx); |
| 2185 | .must_link = query.must_link, | 2223 | if (gop.found_existing) { |
| 2186 | .hidden = query.hidden, | 2224 | // Ignore duplicate archive input |
| 2187 | } }), | 2225 | file.close(io); |
| | 2226 | return .ok; |
| | 2227 | } |
| | 2228 | gop.key_ptr.* = @intCast(resolved_inputs.items.len); |
| | 2229 | resolved_inputs.appendAssumeCapacity(.{ .archive = .{ |
| | 2230 | .path = path, |
| | 2231 | .file = file, |
| | 2232 | .must_link = query.must_link, |
| | 2233 | .hidden = query.hidden, |
| | 2234 | } }); |
| | 2235 | }, |
| 2188 | .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{ | 2236 | .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{ |
| 2189 | .path = path, | 2237 | .path = path, |
| 2190 | .file = file, | 2238 | .file = file, |
| ... | @@ -2206,13 +2254,15 @@ fn resolvePathInput( | ... | @@ -2206,13 +2254,15 @@ fn resolvePathInput( |
| 2206 | resolved_inputs: *std.ArrayList(Input), | 2254 | resolved_inputs: *std.ArrayList(Input), |
| 2207 | /// Allocated via `gpa`. | 2255 | /// Allocated via `gpa`. |
| 2208 | ld_script_bytes: *std.ArrayList(u8), | 2256 | ld_script_bytes: *std.ArrayList(u8), |
| | 2257 | /// Allocated via `gpa`. |
| | 2258 | archive_dedup: *ArchiveDedupMap, |
| 2209 | target: *const std.Target, | 2259 | target: *const std.Target, |
| 2210 | pq: UnresolvedInput.PathQuery, | 2260 | pq: UnresolvedInput.PathQuery, |
| 2211 | color: std.zig.Color, | 2261 | color: std.zig.Color, |
| 2212 | ) Allocator.Error!?ResolveLibInputResult { | 2262 | ) Allocator.Error!?ResolveLibInputResult { |
| 2213 | switch (Compilation.classifyFileExt(pq.path.sub_path)) { | 2263 | switch (Compilation.classifyFileExt(pq.path.sub_path)) { |
| 2214 | .static_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color), | 2264 | .static_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .static, color), |
| 2215 | .shared_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color), | 2265 | .shared_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .dynamic, color), |
| 2216 | .object => { | 2266 | .object => { |
| 2217 | var file = pq.path.root_dir.handle.openFile(io, pq.path.sub_path, .{}) catch |err| | 2267 | var file = pq.path.root_dir.handle.openFile(io, pq.path.sub_path, .{}) catch |err| |
| 2218 | fatal("failed to open object {f}: {s}", .{ pq.path, @errorName(err) }); | 2268 | fatal("failed to open object {f}: {s}", .{ pq.path, @errorName(err) }); |
| ... | @@ -2249,12 +2299,15 @@ fn resolvePathInputLib( | ... | @@ -2249,12 +2299,15 @@ fn resolvePathInputLib( |
| 2249 | resolved_inputs: *std.ArrayList(Input), | 2299 | resolved_inputs: *std.ArrayList(Input), |
| 2250 | /// Allocated via `gpa`. | 2300 | /// Allocated via `gpa`. |
| 2251 | ld_script_bytes: *std.ArrayList(u8), | 2301 | ld_script_bytes: *std.ArrayList(u8), |
| | 2302 | /// Allocated via `gpa`. |
| | 2303 | archive_dedup: *ArchiveDedupMap, |
| 2252 | target: *const std.Target, | 2304 | target: *const std.Target, |
| 2253 | pq: UnresolvedInput.PathQuery, | 2305 | pq: UnresolvedInput.PathQuery, |
| 2254 | link_mode: std.lang.LinkMode, | 2306 | link_mode: std.lang.LinkMode, |
| 2255 | color: std.zig.Color, | 2307 | color: std.zig.Color, |
| 2256 | ) Allocator.Error!ResolveLibInputResult { | 2308 | ) Allocator.Error!ResolveLibInputResult { |
| 2257 | try resolved_inputs.ensureUnusedCapacity(gpa, 1); | 2309 | try resolved_inputs.ensureUnusedCapacity(gpa, 1); |
| | 2310 | try archive_dedup.ensureUnusedCapacity(gpa, 1); |
| 2258 | | 2311 | |
| 2259 | const test_path: Path = pq.path; | 2312 | const test_path: Path = pq.path; |
| 2260 | // In the case of shared libraries, they might actually be "linker scripts" | 2313 | // In the case of shared libraries, they might actually be "linker scripts" |
| ... | @@ -2279,7 +2332,7 @@ fn resolvePathInputLib( | ... | @@ -2279,7 +2332,7 @@ fn resolvePathInputLib( |
| 2279 | mem.startsWith(u8, buf, std.elf.ARMAG_THIN)) | 2332 | mem.startsWith(u8, buf, std.elf.ARMAG_THIN)) |
| 2280 | { | 2333 | { |
| 2281 | // Appears to be an ELF or archive file. | 2334 | // Appears to be an ELF or archive file. |
| 2282 | return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query); | 2335 | return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, pq.query); |
| 2283 | } | 2336 | } |
| 2284 | const stat = file.stat(io) catch |err| | 2337 | const stat = file.stat(io) catch |err| |
| 2285 | fatal("failed to stat {f}: {t}", .{ test_path, err }); | 2338 | fatal("failed to stat {f}: {t}", .{ test_path, err }); |
| ... | @@ -2349,7 +2402,7 @@ fn resolvePathInputLib( | ... | @@ -2349,7 +2402,7 @@ fn resolvePathInputLib( |
| 2349 | }), | 2402 | }), |
| 2350 | }; | 2403 | }; |
| 2351 | errdefer file.close(io); | 2404 | errdefer file.close(io); |
| 2352 | return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query); | 2405 | return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, pq.query); |
| 2353 | } | 2406 | } |
| 2354 | | 2407 | |
| 2355 | pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object { | 2408 | pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object { |