Skip to content

Commit b8f46fa

Browse files
committed
fix: make zig cc pass -l/-L like Clang/GCC for ELF
This commit makes the way `zig cc` passes `-l`/`-L` flags for ELF linking consistent with Clang and GCC. The fix itself is in `src/link.zig` and `src/link/Elf.zig`. The new test is in `test/link/elf.zig`. It follows the example from #19699. The rest of the changes are new functionality needed for the test: * `omit_soname` option for `Build.Module`, `OverlayOptions`. This passes `-fno-soname` to the linker when enabled. * `addLibraryPathSpecial` for `Build.Module`, `Build.Step.Compile`. Like `addLibraryPath` but for `[]const u8`. `addRPathSpecial` is also added for completeness. * `setCwd` for `Build.Step.Compile`. Allows setting current directory for compile steps. * `opt_cwd` for `evalZigProcess` in `Build.Step`. Allows running `evalZigProcess` in a specific directory. Needed for setting current directory for compile steps. * `--zig-lib-dir` is now appended differently in `Build.Step.Compile`. This is needed for compatibility with `setCwd`.
1 parent 1cc388d commit b8f46fa

File tree

10 files changed

+148
-32
lines changed

10 files changed

+148
-32
lines changed

lib/compiler/build_runner.zig

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1496,7 +1496,10 @@ fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {
14961496

14971497
.config_header_step => |other| step.dependOn(&other.step),
14981498
};
1499-
for (mod.lib_paths.items) |lp| lp.addStepDependencies(step);
1499+
for (mod.lib_paths.items) |lib_path| switch (lib_path) {
1500+
.lazy_path => |lp| lp.addStepDependencies(step),
1501+
.special => {},
1502+
};
15001503
for (mod.rpaths.items) |rpath| switch (rpath) {
15011504
.lazy_path => |lp| lp.addStepDependencies(step),
15021505
.special => {},

lib/std/Build/Module.zig

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ dwarf_format: ?std.dwarf.Format,
1212

1313
c_macros: std.ArrayListUnmanaged([]const u8),
1414
include_dirs: std.ArrayListUnmanaged(IncludeDir),
15-
lib_paths: std.ArrayListUnmanaged(LazyPath),
15+
lib_paths: std.ArrayListUnmanaged(LibraryPath),
1616
rpaths: std.ArrayListUnmanaged(RPath),
1717
frameworks: std.StringArrayHashMapUnmanaged(LinkFrameworkOptions),
1818
link_objects: std.ArrayListUnmanaged(LinkObject),
1919

2020
strip: ?bool,
21+
omit_soname: ?bool,
2122
unwind_tables: ?std.builtin.UnwindTables,
2223
single_threaded: ?bool,
2324
stack_protector: ?bool,
@@ -41,6 +42,11 @@ export_symbol_names: []const []const u8 = &.{},
4142
/// Use `getGraph` instead of accessing this field directly.
4243
cached_graph: Graph = .{ .modules = &.{}, .names = &.{} },
4344

45+
pub const LibraryPath = union(enum) {
46+
lazy_path: LazyPath,
47+
special: []const u8,
48+
};
49+
4450
pub const RPath = union(enum) {
4551
lazy_path: LazyPath,
4652
special: []const u8,
@@ -247,6 +253,7 @@ pub const CreateOptions = struct {
247253
link_libcpp: ?bool = null,
248254
single_threaded: ?bool = null,
249255
strip: ?bool = null,
256+
omit_soname: ?bool = null,
250257
unwind_tables: ?std.builtin.UnwindTables = null,
251258
dwarf_format: ?std.dwarf.Format = null,
252259
code_model: std.builtin.CodeModel = .default,
@@ -296,6 +303,7 @@ pub fn init(
296303
.frameworks = .{},
297304
.link_objects = .{},
298305
.strip = options.strip,
306+
.omit_soname = options.omit_soname,
299307
.unwind_tables = options.unwind_tables,
300308
.single_threaded = options.single_threaded,
301309
.stack_protector = options.stack_protector,
@@ -513,7 +521,12 @@ pub fn addFrameworkPath(m: *Module, directory_path: LazyPath) void {
513521

514522
pub fn addLibraryPath(m: *Module, directory_path: LazyPath) void {
515523
const b = m.owner;
516-
m.lib_paths.append(b.allocator, directory_path.dupe(b)) catch @panic("OOM");
524+
m.lib_paths.append(b.allocator, .{ .lazy_path = directory_path.dupe(b) }) catch @panic("OOM");
525+
}
526+
527+
pub fn addLibraryPathSpecial(m: *Module, bytes: []const u8) void {
528+
const b = m.owner;
529+
m.lib_paths.append(b.allocator, .{ .special = b.dupe(bytes) }) catch @panic("OOM");
517530
}
518531

519532
pub fn addRPath(m: *Module, directory_path: LazyPath) void {
@@ -545,6 +558,7 @@ pub fn appendZigProcessFlags(
545558
const b = m.owner;
546559

547560
try addFlag(zig_args, m.strip, "-fstrip", "-fno-strip");
561+
try addFlag(zig_args, m.omit_soname, "-fno-soname", "-fsoname");
548562
try addFlag(zig_args, m.single_threaded, "-fsingle-threaded", "-fno-single-threaded");
549563
try addFlag(zig_args, m.stack_check, "-fstack-check", "-fno-stack-check");
550564
try addFlag(zig_args, m.stack_protector, "-fstack-protector", "-fno-stack-protector");
@@ -611,10 +625,16 @@ pub fn appendZigProcessFlags(
611625
try zig_args.appendSlice(m.c_macros.items);
612626

613627
try zig_args.ensureUnusedCapacity(2 * m.lib_paths.items.len);
614-
for (m.lib_paths.items) |lib_path| {
615-
zig_args.appendAssumeCapacity("-L");
616-
zig_args.appendAssumeCapacity(lib_path.getPath2(b, asking_step));
617-
}
628+
for (m.lib_paths.items) |lib_path| switch (lib_path) {
629+
.lazy_path => |lp| {
630+
zig_args.appendAssumeCapacity("-L");
631+
zig_args.appendAssumeCapacity(lp.getPath2(b, asking_step));
632+
},
633+
.special => |bytes| {
634+
zig_args.appendAssumeCapacity("-L");
635+
zig_args.appendAssumeCapacity(bytes);
636+
},
637+
};
618638

619639
try zig_args.ensureUnusedCapacity(2 * m.rpaths.items.len);
620640
for (m.rpaths.items) |rpath| switch (rpath) {

lib/std/Build/Step.zig

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ pub const ZigProcess = struct {
371371
/// is the zig compiler - the same version that compiled the build runner.
372372
pub fn evalZigProcess(
373373
s: *Step,
374+
opt_cwd: ?[]const u8,
374375
argv: []const []const u8,
375376
prog_node: std.Progress.Node,
376377
watch: bool,
@@ -401,7 +402,7 @@ pub fn evalZigProcess(
401402
};
402403
s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;
403404
s.clearZigProcess();
404-
try handleChildProcessTerm(s, term, null, argv);
405+
try handleChildProcessTerm(s, term, opt_cwd, argv);
405406
return error.MakeFailed;
406407
}
407408

@@ -412,8 +413,8 @@ pub fn evalZigProcess(
412413
const arena = b.allocator;
413414
const gpa = arena;
414415

415-
try handleChildProcUnsupported(s, null, argv);
416-
try handleVerbose(s.owner, null, argv);
416+
try handleChildProcUnsupported(s, opt_cwd, argv);
417+
try handleVerbose(s.owner, opt_cwd, argv);
417418

418419
var child = std.process.Child.init(argv, arena);
419420
child.env_map = &b.graph.env_map;
@@ -422,6 +423,7 @@ pub fn evalZigProcess(
422423
child.stderr_behavior = .Pipe;
423424
child.request_resource_usage_statistics = true;
424425
child.progress_node = prog_node;
426+
child.cwd = opt_cwd;
425427

426428
child.spawn() catch |err| return s.fail("failed to spawn zig compiler {s}: {s}", .{
427429
argv[0], @errorName(err),
@@ -463,15 +465,15 @@ pub fn evalZigProcess(
463465
else => {},
464466
};
465467

466-
try handleChildProcessTerm(s, term, null, argv);
468+
try handleChildProcessTerm(s, term, opt_cwd, argv);
467469
}
468470

469471
// This is intentionally printed for failure on the first build but not for
470472
// subsequent rebuilds.
471473
if (s.result_error_bundle.errorMessageCount() > 0) {
472474
return s.fail("the following command failed with {d} compilation errors:\n{s}", .{
473475
s.result_error_bundle.errorMessageCount(),
474-
try allocPrintCmd(arena, null, argv),
476+
try allocPrintCmd(arena, opt_cwd, argv),
475477
});
476478
}
477479

lib/std/Build/Step/Compile.zig

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const fs = std.fs;
55
const assert = std.debug.assert;
66
const panic = std.debug.panic;
77
const ArrayList = std.ArrayList;
8+
const Build = std.Build;
89
const StringHashMap = std.StringHashMap;
910
const Sha256 = std.crypto.hash.sha2.Sha256;
1011
const Allocator = mem.Allocator;
@@ -243,6 +244,9 @@ zig_process: ?*Step.ZigProcess,
243244
/// builtin fuzzer, see the `fuzz` flag in `Module`.
244245
sanitize_coverage_trace_pc_guard: ?bool = null,
245246

247+
/// Use `setCwd` to set the initial current working directory
248+
cwd: ?Build.LazyPath = null,
249+
246250
pub const ExpectedCompileErrors = union(enum) {
247251
contains: []const u8,
248252
exact: []const []const u8,
@@ -942,10 +946,18 @@ pub fn addLibraryPath(compile: *Compile, directory_path: LazyPath) void {
942946
compile.root_module.addLibraryPath(directory_path);
943947
}
944948

949+
pub fn addLibraryPathSpecial(compile: *Compile, bytes: []const u8) void {
950+
compile.root_module.addLibraryPathSpecial(bytes);
951+
}
952+
945953
pub fn addRPath(compile: *Compile, directory_path: LazyPath) void {
946954
compile.root_module.addRPath(directory_path);
947955
}
948956

957+
pub fn addRPathSpecial(compile: *Compile, bytes: []const u8) void {
958+
compile.root_module.addRPathSpecial(bytes);
959+
}
960+
949961
pub fn addSystemFrameworkPath(compile: *Compile, directory_path: LazyPath) void {
950962
compile.root_module.addSystemFrameworkPath(directory_path);
951963
}
@@ -1697,7 +1709,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
16971709

16981710
if (opt_zig_lib_dir) |zig_lib_dir| {
16991711
try zig_args.append("--zig-lib-dir");
1700-
try zig_args.append(zig_lib_dir);
1712+
try zig_args.append(try fs.cwd().realpathAlloc(arena, zig_lib_dir));
17011713
}
17021714

17031715
try addFlag(&zig_args, "PIE", compile.pie);
@@ -1795,9 +1807,11 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
17951807
const b = step.owner;
17961808
const compile: *Compile = @fieldParentPtr("step", step);
17971809

1810+
const cwd: ?[]const u8 = if (compile.cwd) |lazy_cwd| lazy_cwd.getPath(b) else null;
17981811
const zig_args = try getZigArgs(compile, false);
17991812

18001813
const maybe_output_dir = step.evalZigProcess(
1814+
cwd,
18011815
zig_args,
18021816
options.progress_node,
18031817
(b.graph.incremental == true) and options.watch,
@@ -1880,8 +1894,9 @@ pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) !Path {
18801894
c.step.result_error_bundle.deinit(gpa);
18811895
c.step.result_error_bundle = std.zig.ErrorBundle.empty;
18821896

1897+
const cwd: ?[]const u8 = if (c.cwd) |lazy_cwd| lazy_cwd.getPath(c.step.owner) else null;
18831898
const zig_args = try getZigArgs(c, true);
1884-
const maybe_output_bin_path = try c.step.evalZigProcess(zig_args, progress_node, false);
1899+
const maybe_output_bin_path = try c.step.evalZigProcess(cwd, zig_args, progress_node, false);
18851900
return maybe_output_bin_path.?;
18861901
}
18871902

@@ -2122,3 +2137,8 @@ pub fn getCompileDependencies(start: *Compile, chase_dynamic: bool) []const *Com
21222137

21232138
return compiles.keys();
21242139
}
2140+
2141+
pub fn setCwd(self: *Compile, cwd: Build.LazyPath) void {
2142+
cwd.addStepDependencies(&self.step);
2143+
self.cwd = cwd;
2144+
}

lib/std/Build/Step/ObjCopy.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
236236
try argv.appendSlice(&.{ full_src_path, full_dest_path });
237237

238238
try argv.append("--listen=-");
239-
_ = try step.evalZigProcess(argv.items, prog_node, false);
239+
_ = try step.evalZigProcess(null, argv.items, prog_node, false);
240240

241241
objcopy.output_file.path = full_dest_path;
242242
if (objcopy.output_file_debug) |*file| file.path = full_dest_path_debug;

lib/std/Build/Step/TranslateC.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
200200
const c_source_path = translate_c.source.getPath2(b, step);
201201
try argv_list.append(c_source_path);
202202

203-
const output_dir = try step.evalZigProcess(argv_list.items, prog_node, false);
203+
const output_dir = try step.evalZigProcess(null, argv_list.items, prog_node, false);
204204

205205
const basename = std.fs.path.stem(std.fs.path.basename(c_source_path));
206206
translate_c.out_basename = b.fmt("{s}.zig", .{basename});

src/link.zig

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1794,6 +1794,8 @@ pub const Input = union(enum) {
17941794
needed: bool,
17951795
weak: bool,
17961796
reexport: bool,
1797+
name: ?[]const u8,
1798+
lib_directory: Directory,
17971799
};
17981800

17991801
pub const DsoExact = struct {
@@ -2121,7 +2123,7 @@ fn resolveLibInput(
21212123
else => |e| fatal("unable to search for tbd library '{}': {s}", .{ test_path, @errorName(e) }),
21222124
};
21232125
errdefer file.close();
2124-
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query);
2126+
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query, name_query.name, lib_directory);
21252127
}
21262128

21272129
{
@@ -2135,10 +2137,10 @@ fn resolveLibInput(
21352137
}),
21362138
};
21372139
try checked_paths.writer(gpa).print("\n {}", .{test_path});
2138-
switch (try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, .{
2140+
switch (try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, lib_directory, target, .{
21392141
.path = test_path,
21402142
.query = name_query.query,
2141-
}, link_mode, color)) {
2143+
}, link_mode, color, name_query.name)) {
21422144
.no_match => {},
21432145
.ok => return .ok,
21442146
}
@@ -2159,7 +2161,7 @@ fn resolveLibInput(
21592161
}),
21602162
};
21612163
errdefer file.close();
2162-
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query);
2164+
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query, name_query.name, lib_directory);
21632165
}
21642166

21652167
// In the case of MinGW, the main check will be .lib but we also need to
@@ -2175,7 +2177,7 @@ fn resolveLibInput(
21752177
else => |e| fatal("unable to search for static library '{}': {s}", .{ test_path, @errorName(e) }),
21762178
};
21772179
errdefer file.close();
2178-
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query);
2180+
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query, name_query.name, lib_directory);
21792181
}
21802182

21812183
return .no_match;
@@ -2187,6 +2189,8 @@ fn finishResolveLibInput(
21872189
file: std.fs.File,
21882190
link_mode: std.builtin.LinkMode,
21892191
query: UnresolvedInput.Query,
2192+
name: ?[]const u8,
2193+
lib_directory: Directory,
21902194
) ResolveLibInputResult {
21912195
switch (link_mode) {
21922196
.static => resolved_inputs.appendAssumeCapacity(.{ .archive = .{
@@ -2201,6 +2205,8 @@ fn finishResolveLibInput(
22012205
.needed = query.needed,
22022206
.weak = query.weak,
22032207
.reexport = query.reexport,
2208+
.name = name,
2209+
.lib_directory = lib_directory,
22042210
} }),
22052211
}
22062212
return .ok;
@@ -2220,8 +2226,8 @@ fn resolvePathInput(
22202226
color: std.zig.Color,
22212227
) Allocator.Error!?ResolveLibInputResult {
22222228
switch (Compilation.classifyFileExt(pq.path.sub_path)) {
2223-
.static_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color),
2224-
.shared_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color),
2229+
.static_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, Directory.cwd(), target, pq, .static, color, null),
2230+
.shared_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, Directory.cwd(), target, pq, .dynamic, color, null),
22252231
.object => {
22262232
var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|
22272233
fatal("failed to open object {}: {s}", .{ pq.path, @errorName(err) });
@@ -2257,10 +2263,12 @@ fn resolvePathInputLib(
22572263
resolved_inputs: *std.ArrayListUnmanaged(Input),
22582264
/// Allocated via `gpa`.
22592265
ld_script_bytes: *std.ArrayListUnmanaged(u8),
2266+
lib_directory: Directory,
22602267
target: std.Target,
22612268
pq: UnresolvedInput.PathQuery,
22622269
link_mode: std.builtin.LinkMode,
22632270
color: std.zig.Color,
2271+
name: ?[]const u8,
22642272
) Allocator.Error!ResolveLibInputResult {
22652273
try resolved_inputs.ensureUnusedCapacity(gpa, 1);
22662274

@@ -2285,7 +2293,7 @@ fn resolvePathInputLib(
22852293
if (n != ld_script_bytes.items.len) break :elf_file;
22862294
if (!mem.eql(u8, ld_script_bytes.items[0..4], "\x7fELF")) break :elf_file;
22872295
// Appears to be an ELF file.
2288-
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query);
2296+
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query, name, lib_directory);
22892297
}
22902298
const stat = file.stat() catch |err|
22912299
fatal("failed to stat {}: {s}", .{ test_path, @errorName(err) });
@@ -2351,7 +2359,7 @@ fn resolvePathInputLib(
23512359
}),
23522360
};
23532361
errdefer file.close();
2354-
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query);
2362+
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query, name, lib_directory);
23552363
}
23562364

23572365
pub fn openObject(path: Path, must_link: bool, hidden: bool) !Input.Object {
@@ -2374,6 +2382,8 @@ pub fn openDso(path: Path, needed: bool, weak: bool, reexport: bool) !Input.Dso
23742382
.needed = needed,
23752383
.weak = weak,
23762384
.reexport = reexport,
2385+
.name = null,
2386+
.lib_directory = Directory.cwd(),
23772387
};
23782388
}
23792389

0 commit comments

Comments
 (0)