authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-05-15 13:15:23+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-06-17 15:33:50+01:00
log8aab222ffbde5bfc141b23b2553b2887cf1a3ae3
tree9b8afec613ed127e6f44d665c54e27c1edf24e14
parent561fdd0ed3d93a373f126f4df01caf813ad32fec

Compilation: add missing link file options to cache manifest

Also add a standalone test which covers the `-fentry` case. It does this by performing two reproducible compilations which are identical other than having different entry points, and checking whether the emitted binaries are identical (they should *not* be). Resolves: #23869

5 files changed, 88 insertions(+), 0 deletions(-)

src/Compilation.zig+15
...@@ -3233,6 +3233,13 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -3233,6 +3233,13 @@ fn addNonIncrementalStuffToCacheManifest(
3233 man.hash.addOptional(opts.allow_shlib_undefined);3233 man.hash.addOptional(opts.allow_shlib_undefined);
3234 man.hash.add(opts.bind_global_refs_locally);3234 man.hash.add(opts.bind_global_refs_locally);
32353235
3236 const EntryTag = @typeInfo(link.File.OpenOptions.Entry).@"union".tag_type.?;
3237 man.hash.add(@as(EntryTag, opts.entry));
3238 switch (opts.entry) {
3239 .default, .disabled, .enabled => {},
3240 .named => |name| man.hash.addBytes(name),
3241 }
3242
3236 // ELF specific stuff3243 // ELF specific stuff
3237 man.hash.add(opts.z_nodelete);3244 man.hash.add(opts.z_nodelete);
3238 man.hash.add(opts.z_notext);3245 man.hash.add(opts.z_notext);
...@@ -3254,6 +3261,9 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -3254,6 +3261,9 @@ fn addNonIncrementalStuffToCacheManifest(
3254 man.hash.addOptional(opts.max_memory);3261 man.hash.addOptional(opts.max_memory);
3255 man.hash.addOptional(opts.global_base);3262 man.hash.addOptional(opts.global_base);
3256 man.hash.addListOfBytes(opts.export_symbol_names);3263 man.hash.addListOfBytes(opts.export_symbol_names);
3264 man.hash.add(opts.import_symbols);
3265 man.hash.add(opts.import_table);
3266 man.hash.add(opts.export_table);
32573267
3258 // Mach-O specific stuff3268 // Mach-O specific stuff
3259 try link.File.MachO.hashAddFrameworks(man, opts.frameworks);3269 try link.File.MachO.hashAddFrameworks(man, opts.frameworks);
...@@ -3264,6 +3274,9 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -3264,6 +3274,9 @@ fn addNonIncrementalStuffToCacheManifest(
3264 man.hash.add(opts.dead_strip_dylibs);3274 man.hash.add(opts.dead_strip_dylibs);
3265 man.hash.add(opts.force_load_objc);3275 man.hash.add(opts.force_load_objc);
3266 man.hash.add(opts.discard_local_symbols);3276 man.hash.add(opts.discard_local_symbols);
3277 man.hash.addOptional(opts.compatibility_version);
3278 man.hash.addOptionalBytes(opts.install_name);
3279 man.hash.addOptional(opts.darwin_sdk_layout);
32673280
3268 // COFF specific stuff3281 // COFF specific stuff
3269 man.hash.addOptional(opts.subsystem);3282 man.hash.addOptional(opts.subsystem);
...@@ -3272,6 +3285,8 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -3272,6 +3285,8 @@ fn addNonIncrementalStuffToCacheManifest(
3272 man.hash.add(opts.dynamicbase);3285 man.hash.add(opts.dynamicbase);
3273 man.hash.addOptional(opts.major_subsystem_version);3286 man.hash.addOptional(opts.major_subsystem_version);
3274 man.hash.addOptional(opts.minor_subsystem_version);3287 man.hash.addOptional(opts.minor_subsystem_version);
3288 man.hash.addOptionalBytes(opts.pdb_source_path);
3289 man.hash.addOptionalBytes(opts.module_definition_file);
3275}3290}
32763291
3277fn emitFromCObject(3292fn emitFromCObject(
test/standalone/build.zig.zon+3
...@@ -201,6 +201,9 @@...@@ -201,6 +201,9 @@
201 .config_header = .{201 .config_header = .{
202 .path = "config_header",202 .path = "config_header",
203 },203 },
204 .entry_point = .{
205 .path = "entry_point",
206 },
204 },207 },
205 .paths = .{208 .paths = .{
206 "build.zig",209 "build.zig",
test/standalone/entry_point/build.zig created+46
...@@ -0,0 +1,46 @@
1pub fn build(b: *std.Build) !void {
2 const mod = b.createModule(.{
3 // Setting the entry point doesn't work properly on all targets right now. Since we're
4 // really just trying to make sure that the compiler *frontend* respects `-fentry` and
5 // includes it in the cache manifest, just test for a target where it works.
6 .target = b.resolveTargetQuery(try .parse(.{
7 .arch_os_abi = "x86_64-linux",
8 })),
9 .optimize = .ReleaseFast, // non-Debug build for reproducible output
10 .root_source_file = b.path("main.zig"),
11 });
12
13 const exe_foo = b.addExecutable(.{
14 .name = "the_exe", // same name for reproducible output
15 .root_module = mod,
16 });
17 exe_foo.entry = .{ .symbol_name = "foo" };
18 const exe_bar = b.addExecutable(.{
19 .name = "the_exe", // same name for reproducible output
20 .root_module = mod,
21 });
22 exe_bar.entry = .{ .symbol_name = "bar" };
23
24 // Despite the output binary being reproducible, the `entry` differed, so the emitted binaries
25 // should be different. But the two compilations are otherwise identical, so if `entry` isn't
26 // being respected properly, we will see identical binaries.
27
28 const check_differ_exe = b.addExecutable(.{
29 .name = "check_differ",
30 .root_module = b.createModule(.{
31 .target = b.graph.host,
32 .optimize = .Debug,
33 .root_source_file = b.path("check_differ.zig"),
34 }),
35 });
36
37 const diff_cmd = b.addRunArtifact(check_differ_exe);
38 diff_cmd.addFileArg(exe_foo.getEmittedBin());
39 diff_cmd.addFileArg(exe_bar.getEmittedBin());
40 diff_cmd.expectExitCode(0);
41
42 const test_step = b.step("test", "Test it");
43 b.default_step = test_step;
44 test_step.dependOn(&diff_cmd.step);
45}
46const std = @import("std");
test/standalone/entry_point/check_differ.zig created+17
...@@ -0,0 +1,17 @@
1pub fn main() !void {
2 var arena_state: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
3 defer arena_state.deinit();
4 const arena = arena_state.allocator();
5
6 const args = try std.process.argsAlloc(arena);
7 if (args.len != 3) return error.BadUsage; // usage: 'check_differ <path a> <path b>'
8
9 const contents_1 = try std.fs.cwd().readFileAlloc(arena, args[1], 1024 * 1024 * 64); // 64 MiB ought to be plenty
10 const contents_2 = try std.fs.cwd().readFileAlloc(arena, args[2], 1024 * 1024 * 64); // 64 MiB ought to be plenty
11
12 if (std.mem.eql(u8, contents_1, contents_2)) {
13 return error.FilesMatch;
14 }
15 // success, files differ
16}
17const std = @import("std");
test/standalone/entry_point/main.zig created+7
...@@ -0,0 +1,7 @@
1pub const _start = {};
2export fn foo() u32 {
3 return 123;
4}
5export fn bar() u32 {
6 return 456;
7}