authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-12-03 16:15:27-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-01 17:51:18-07:00
log04480f72d8993196052375c0f0aca9d33f475fe7
treeb742c23c5c60b57676d3d56bc09db49497324a9d
parent0ee8fbb15dd32a2bb91696e4765926e9170d2a23

fix linker test regressions

Caused by problems with transitive dependencies

4 files changed, 81 insertions(+), 33 deletions(-)

lib/std/Build/Module.zig+7-2
...@@ -229,7 +229,7 @@ pub fn init(m: *Module, owner: *std.Build, options: CreateOptions, compile: ?*St...@@ -229,7 +229,7 @@ pub fn init(m: *Module, owner: *std.Build, options: CreateOptions, compile: ?*St
229 }229 }
230230
231 // This logic accesses `depending_steps` which was just modified above.231 // This logic accesses `depending_steps` which was just modified above.
232 var it = m.iterateDependencies(null);232 var it = m.iterateDependencies(null, false);
233 while (it.next()) |item| addShallowDependencies(m, item.module);233 while (it.next()) |item| addShallowDependencies(m, item.module);
234}234}
235235
...@@ -244,7 +244,7 @@ pub fn addImport(m: *Module, name: []const u8, module: *Module) void {...@@ -244,7 +244,7 @@ pub fn addImport(m: *Module, name: []const u8, module: *Module) void {
244 const b = m.owner;244 const b = m.owner;
245 m.import_table.put(b.allocator, b.dupe(name), module) catch @panic("OOM");245 m.import_table.put(b.allocator, b.dupe(name), module) catch @panic("OOM");
246246
247 var it = module.iterateDependencies(null);247 var it = module.iterateDependencies(null, false);
248 while (it.next()) |item| addShallowDependencies(m, item.module);248 while (it.next()) |item| addShallowDependencies(m, item.module);
249}249}
250250
...@@ -319,6 +319,7 @@ pub const DependencyIterator = struct {...@@ -319,6 +319,7 @@ pub const DependencyIterator = struct {
319 allocator: std.mem.Allocator,319 allocator: std.mem.Allocator,
320 index: usize,320 index: usize,
321 set: std.AutoArrayHashMapUnmanaged(Key, []const u8),321 set: std.AutoArrayHashMapUnmanaged(Key, []const u8),
322 chase_dyn_libs: bool,
322323
323 pub const Key = struct {324 pub const Key = struct {
324 /// The compilation that contains the `Module`. Note that a `Module` might be325 /// The compilation that contains the `Module`. Note that a `Module` might be
...@@ -361,6 +362,8 @@ pub const DependencyIterator = struct {...@@ -361,6 +362,8 @@ pub const DependencyIterator = struct {
361 if (key.compile != null) {362 if (key.compile != null) {
362 for (module.link_objects.items) |link_object| switch (link_object) {363 for (module.link_objects.items) |link_object| switch (link_object) {
363 .other_step => |compile| {364 .other_step => |compile| {
365 if (!it.chase_dyn_libs and compile.isDynamicLibrary()) continue;
366
364 it.set.put(it.allocator, .{367 it.set.put(it.allocator, .{
365 .module = &compile.root_module,368 .module = &compile.root_module,
366 .compile = compile,369 .compile = compile,
...@@ -381,11 +384,13 @@ pub const DependencyIterator = struct {...@@ -381,11 +384,13 @@ pub const DependencyIterator = struct {
381pub fn iterateDependencies(384pub fn iterateDependencies(
382 m: *Module,385 m: *Module,
383 chase_steps: ?*Step.Compile,386 chase_steps: ?*Step.Compile,
387 chase_dyn_libs: bool,
384) DependencyIterator {388) DependencyIterator {
385 var it: DependencyIterator = .{389 var it: DependencyIterator = .{
386 .allocator = m.owner.allocator,390 .allocator = m.owner.allocator,
387 .index = 0,391 .index = 0,
388 .set = .{},392 .set = .{},
393 .chase_dyn_libs = chase_dyn_libs,
389 };394 };
390 it.set.ensureUnusedCapacity(m.owner.allocator, m.import_table.count() + 1) catch @panic("OOM");395 it.set.ensureUnusedCapacity(m.owner.allocator, m.import_table.count() + 1) catch @panic("OOM");
391 it.set.putAssumeCapacity(.{396 it.set.putAssumeCapacity(.{
lib/std/Build/Step/Compile.zig+63-24
...@@ -488,11 +488,12 @@ pub fn forceUndefinedSymbol(self: *Compile, symbol_name: []const u8) void {...@@ -488,11 +488,12 @@ pub fn forceUndefinedSymbol(self: *Compile, symbol_name: []const u8) void {
488}488}
489489
490/// Returns whether the library, executable, or object depends on a particular system library.490/// Returns whether the library, executable, or object depends on a particular system library.
491/// Includes transitive dependencies.
491pub fn dependsOnSystemLibrary(self: *const Compile, name: []const u8) bool {492pub fn dependsOnSystemLibrary(self: *const Compile, name: []const u8) bool {
492 var is_linking_libc = false;493 var is_linking_libc = false;
493 var is_linking_libcpp = false;494 var is_linking_libcpp = false;
494495
495 var it = self.root_module.iterateDependencies(self);496 var it = self.root_module.iterateDependencies(self, true);
496 while (it.next()) |module| {497 while (it.next()) |module| {
497 for (module.link_objects.items) |link_object| {498 for (module.link_objects.items) |link_object| {
498 switch (link_object) {499 switch (link_object) {
...@@ -841,7 +842,7 @@ fn appendModuleArgs(cs: *Compile, zig_args: *ArrayList([]const u8)) !void {...@@ -841,7 +842,7 @@ fn appendModuleArgs(cs: *Compile, zig_args: *ArrayList([]const u8)) !void {
841 var names = std.StringHashMap(void).init(b.allocator);842 var names = std.StringHashMap(void).init(b.allocator);
842843
843 {844 {
844 var it = cs.root_module.iterateDependencies(null);845 var it = cs.root_module.iterateDependencies(null, false);
845 _ = it.next(); // Skip over the root module.846 _ = it.next(); // Skip over the root module.
846 while (it.next()) |item| {847 while (it.next()) |item| {
847 // While we're traversing the root dependencies, let's make sure that no module names848 // While we're traversing the root dependencies, let's make sure that no module names
...@@ -1000,33 +1001,51 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1000,33 +1001,51 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
10001001
1001 try self.root_module.appendZigProcessFlags(&zig_args, step);1002 try self.root_module.appendZigProcessFlags(&zig_args, step);
10021003
1003 var it = self.root_module.iterateDependencies(self);1004 {
1005 // Fully recursive iteration including dynamic libraries to detect
1006 // libc and libc++ linkage.
1007 var it = self.root_module.iterateDependencies(self, true);
1008 while (it.next()) |key| {
1009 if (key.module.link_libc == true) self.is_linking_libc = true;
1010 if (key.module.link_libcpp == true) self.is_linking_libcpp = true;
1011 }
1012 }
1013
1014 // For this loop, don't chase dynamic libraries because their link
1015 // objects are already linked.
1016 var it = self.root_module.iterateDependencies(self, false);
1017
1004 while (it.next()) |key| {1018 while (it.next()) |key| {
1005 const module = key.module;1019 const module = key.module;
1006 const compile = key.compile.?;1020 const compile = key.compile.?;
1007 const dyn = compile.isDynamicLibrary();
10081021
1009 // Inherit dependency on libc and libc++.1022 // While walking transitive dependencies, if a given link object is
1010 if (module.link_libc == true) self.is_linking_libc = true;1023 // already included in a library, it should not redundantly be
1011 if (module.link_libcpp == true) self.is_linking_libcpp = true;1024 // placed on the linker line of the dependee.
1025 const my_responsibility = compile == self;
1026 const already_linked = !my_responsibility and compile.isDynamicLibrary();
10121027
1013 // Inherit dependencies on darwin frameworks.1028 // Inherit dependencies on darwin frameworks.
1014 if (!dyn) {1029 if (!already_linked) {
1015 for (module.frameworks.keys(), module.frameworks.values()) |name, info| {1030 for (module.frameworks.keys(), module.frameworks.values()) |name, info| {
1016 try frameworks.put(b.allocator, name, info);1031 try frameworks.put(b.allocator, name, info);
1017 }1032 }
1018 }1033 }
10191034
1020 // Inherit dependencies on system libraries and static libraries.1035 // Inherit dependencies on system libraries and static libraries.
1021 total_linker_objects += module.link_objects.items.len;
1022 for (module.link_objects.items) |link_object| {1036 for (module.link_objects.items) |link_object| {
1023 switch (link_object) {1037 switch (link_object) {
1024 .static_path => |static_path| try zig_args.append(static_path.getPath(b)),1038 .static_path => |static_path| {
1039 if (my_responsibility) {
1040 try zig_args.append(static_path.getPath(b));
1041 total_linker_objects += 1;
1042 }
1043 },
1025 .system_lib => |system_lib| {1044 .system_lib => |system_lib| {
1026 if ((try seen_system_libs.fetchPut(b.allocator, system_lib.name, {})) != null)1045 if ((try seen_system_libs.fetchPut(b.allocator, system_lib.name, {})) != null)
1027 continue;1046 continue;
10281047
1029 if (dyn)1048 if (already_linked)
1030 continue;1049 continue;
10311050
1032 if ((system_lib.search_strategy != prev_search_strategy or1051 if ((system_lib.search_strategy != prev_search_strategy or
...@@ -1088,29 +1107,36 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1088,29 +1107,36 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1088 }1107 }
1089 },1108 },
1090 .other_step => |other| {1109 .other_step => |other| {
1091 const included_in_lib = (compile.kind == .lib and other.kind == .obj);
1092 if (dyn or included_in_lib)
1093 continue;
1094
1095 switch (other.kind) {1110 switch (other.kind) {
1096 .exe => return step.fail("cannot link with an executable build artifact", .{}),1111 .exe => return step.fail("cannot link with an executable build artifact", .{}),
1097 .@"test" => return step.fail("cannot link with a test", .{}),1112 .@"test" => return step.fail("cannot link with a test", .{}),
1098 .obj => {1113 .obj => {
1099 try zig_args.append(other.getEmittedBin().getPath(b));1114 const included_in_lib = !my_responsibility and
1115 compile.kind == .lib and other.kind == .obj;
1116 if (!already_linked and !included_in_lib) {
1117 try zig_args.append(other.getEmittedBin().getPath(b));
1118 total_linker_objects += 1;
1119 }
1100 },1120 },
1101 .lib => l: {1121 .lib => l: {
1102 if (self.isStaticLibrary() and other.isStaticLibrary()) {1122 const other_produces_implib = other.producesImplib();
1123 const other_is_static = other_produces_implib or other.isStaticLibrary();
1124
1125 if (self.isStaticLibrary() and other_is_static) {
1103 // Avoid putting a static library inside a static library.1126 // Avoid putting a static library inside a static library.
1104 break :l;1127 break :l;
1105 }1128 }
11061129
1107 // For DLLs, we gotta link against the implib. For1130 // For DLLs, we must link against the implib.
1108 // everything else, we directly link against the library file.1131 // For everything else, we directly link
1109 const full_path_lib = if (other.producesImplib())1132 // against the library file.
1133 const full_path_lib = if (other_produces_implib)
1110 other.getGeneratedFilePath("generated_implib", &self.step)1134 other.getGeneratedFilePath("generated_implib", &self.step)
1111 else1135 else
1112 other.getGeneratedFilePath("generated_bin", &self.step);1136 other.getGeneratedFilePath("generated_bin", &self.step);
1137
1113 try zig_args.append(full_path_lib);1138 try zig_args.append(full_path_lib);
1139 total_linker_objects += 1;
11141140
1115 if (other.linkage == Linkage.dynamic and1141 if (other.linkage == Linkage.dynamic and
1116 self.rootModuleTarget().os.tag != .windows)1142 self.rootModuleTarget().os.tag != .windows)
...@@ -1123,16 +1149,21 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1123,16 +1149,21 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1123 },1149 },
1124 }1150 }
1125 },1151 },
1126 .assembly_file => |asm_file| {1152 .assembly_file => |asm_file| l: {
1153 if (!my_responsibility) break :l;
1154
1127 if (prev_has_cflags) {1155 if (prev_has_cflags) {
1128 try zig_args.append("-cflags");1156 try zig_args.append("-cflags");
1129 try zig_args.append("--");1157 try zig_args.append("--");
1130 prev_has_cflags = false;1158 prev_has_cflags = false;
1131 }1159 }
1132 try zig_args.append(asm_file.getPath(b));1160 try zig_args.append(asm_file.getPath(b));
1161 total_linker_objects += 1;
1133 },1162 },
11341163
1135 .c_source_file => |c_source_file| {1164 .c_source_file => |c_source_file| l: {
1165 if (!my_responsibility) break :l;
1166
1136 if (c_source_file.flags.len == 0) {1167 if (c_source_file.flags.len == 0) {
1137 if (prev_has_cflags) {1168 if (prev_has_cflags) {
1138 try zig_args.append("-cflags");1169 try zig_args.append("-cflags");
...@@ -1148,9 +1179,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1148,9 +1179,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1148 prev_has_cflags = true;1179 prev_has_cflags = true;
1149 }1180 }
1150 try zig_args.append(c_source_file.file.getPath(b));1181 try zig_args.append(c_source_file.file.getPath(b));
1182 total_linker_objects += 1;
1151 },1183 },
11521184
1153 .c_source_files => |c_source_files| {1185 .c_source_files => |c_source_files| l: {
1186 if (!my_responsibility) break :l;
1187
1154 if (c_source_files.flags.len == 0) {1188 if (c_source_files.flags.len == 0) {
1155 if (prev_has_cflags) {1189 if (prev_has_cflags) {
1156 try zig_args.append("-cflags");1190 try zig_args.append("-cflags");
...@@ -1165,6 +1199,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1165,6 +1199,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1165 try zig_args.append("--");1199 try zig_args.append("--");
1166 prev_has_cflags = true;1200 prev_has_cflags = true;
1167 }1201 }
1202
1168 if (c_source_files.dependency) |dep| {1203 if (c_source_files.dependency) |dep| {
1169 for (c_source_files.files) |file| {1204 for (c_source_files.files) |file| {
1170 try zig_args.append(dep.builder.pathFromRoot(file));1205 try zig_args.append(dep.builder.pathFromRoot(file));
...@@ -1174,9 +1209,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1174,9 +1209,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1174 try zig_args.append(b.pathFromRoot(file));1209 try zig_args.append(b.pathFromRoot(file));
1175 }1210 }
1176 }1211 }
1212 total_linker_objects += c_source_files.files.len;
1177 },1213 },
11781214
1179 .win32_resource_file => |rc_source_file| {1215 .win32_resource_file => |rc_source_file| l: {
1216 if (!my_responsibility) break :l;
1217
1180 if (rc_source_file.flags.len == 0) {1218 if (rc_source_file.flags.len == 0) {
1181 if (prev_has_rcflags) {1219 if (prev_has_rcflags) {
1182 try zig_args.append("-rcflags");1220 try zig_args.append("-rcflags");
...@@ -1192,6 +1230,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1192,6 +1230,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1192 prev_has_rcflags = true;1230 prev_has_rcflags = true;
1193 }1231 }
1194 try zig_args.append(rc_source_file.file.getPath(b));1232 try zig_args.append(rc_source_file.file.getPath(b));
1233 total_linker_objects += 1;
1195 },1234 },
1196 }1235 }
1197 }1236 }
lib/std/Build/Step/Run.zig+1-1
...@@ -1291,7 +1291,7 @@ fn evalGeneric(self: *Run, child: *std.process.Child) !StdIoResult {...@@ -1291,7 +1291,7 @@ fn evalGeneric(self: *Run, child: *std.process.Child) !StdIoResult {
12911291
1292fn addPathForDynLibs(self: *Run, artifact: *Step.Compile) void {1292fn addPathForDynLibs(self: *Run, artifact: *Step.Compile) void {
1293 const b = self.step.owner;1293 const b = self.step.owner;
1294 var it = artifact.root_module.iterateDependencies(artifact);1294 var it = artifact.root_module.iterateDependencies(artifact, true);
1295 while (it.next()) |item| {1295 while (it.next()) |item| {
1296 const other = item.compile.?;1296 const other = item.compile.?;
1297 if (item.module == &other.root_module) {1297 if (item.module == &other.root_module) {
test/link/elf.zig+10-6
...@@ -1317,8 +1317,9 @@ fn testIFuncDlopen(b: *Build, opts: Options) *Step {...@@ -1317,8 +1317,9 @@ fn testIFuncDlopen(b: *Build, opts: Options) *Step {
1317fn testIFuncDso(b: *Build, opts: Options) *Step {1317fn testIFuncDso(b: *Build, opts: Options) *Step {
1318 const test_step = addTestStep(b, "ifunc-dso", opts);1318 const test_step = addTestStep(b, "ifunc-dso", opts);
13191319
1320 const dso = addSharedLibrary(b, opts, .{ .name = "a" });1320 const dso = addSharedLibrary(b, opts, .{
1321 addCSourceBytes(dso,1321 .name = "a",
1322 .c_source_bytes =
1322 \\#include<stdio.h>1323 \\#include<stdio.h>
1323 \\__attribute__((ifunc("resolve_foobar")))1324 \\__attribute__((ifunc("resolve_foobar")))
1324 \\void foobar(void);1325 \\void foobar(void);
...@@ -1329,16 +1330,19 @@ fn testIFuncDso(b: *Build, opts: Options) *Step {...@@ -1329,16 +1330,19 @@ fn testIFuncDso(b: *Build, opts: Options) *Step {
1329 \\static Func *resolve_foobar(void) {1330 \\static Func *resolve_foobar(void) {
1330 \\ return real_foobar;1331 \\ return real_foobar;
1331 \\}1332 \\}
1332 , &.{});1333 ,
1334 });
1333 dso.linkLibC();1335 dso.linkLibC();
13341336
1335 const exe = addExecutable(b, opts, .{ .name = "main" });1337 const exe = addExecutable(b, opts, .{
1336 addCSourceBytes(exe,1338 .name = "main",
1339 .c_source_bytes =
1337 \\void foobar(void);1340 \\void foobar(void);
1338 \\int main() {1341 \\int main() {
1339 \\ foobar();1342 \\ foobar();
1340 \\}1343 \\}
1341 , &.{});1344 ,
1345 });
1342 exe.linkLibrary(dso);1346 exe.linkLibrary(dso);
13431347
1344 const run = addRunArtifact(exe);1348 const run = addRunArtifact(exe);