From 43b55ea9f4a5a05c9ff295ea00aab687141e24c3 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 1 Oct 2022 15:33:15 +0200 Subject: [PATCH 1/3] wasm-linker: generate 'producers' section The `producers` section contains meta data of the binary and/or object file. It *can* contain the source language, the tool it was processed by, and/or the SDK that was used to produce the file. For now, we always set the language and processed-by fields to Zig. In the future we will parse linked object files to detect their producers sections and append (if different) their language, SDK and processed-by fields. --- src/link/Wasm.zig | 66 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 0e6fe5dcc2..67b4218b3f 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2556,6 +2556,10 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try wasm.emitDataRelocations(&binary_bytes, data_index, symbol_table); } } else if (!wasm.base.options.strip) { + try wasm.emitNameSection(&binary_bytes, arena); + } + + if (!wasm.base.options.strip) { if (wasm.dwarf) |*dwarf| { const mod = wasm.base.options.module.?; try dwarf.writeDbgAbbrev(&wasm.base); @@ -2597,7 +2601,8 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod debug_bytes.clearRetainingCapacity(); } } - try wasm.emitNameSection(&binary_bytes, arena); + + try emitProducerSection(&binary_bytes); } // Only when writing all sections executed properly we write the magic @@ -2631,6 +2636,65 @@ fn emitDebugSection(binary_bytes: *std.ArrayList(u8), data: []const u8, name: [] ); } +fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void { + const header_offset = try reserveCustomSectionHeader(binary_bytes); + + const writer = binary_bytes.writer(); + const producers = "producers"; + try leb.writeULEB128(writer, @intCast(u32, producers.len)); + try writer.writeAll(producers); + + try leb.writeULEB128(writer, @as(u32, 2)); // 2 fields: Language + processed-by + + // used for the Zig version + var version_buf: [100]u8 = undefined; + const version = try std.fmt.bufPrint(&version_buf, "{}", .{build_options.semver}); + + // language field + { + const language = "language"; + try leb.writeULEB128(writer, @intCast(u32, language.len)); + try writer.writeAll(language); + + // field_value_count (TODO: Parse object files for producer sections to detect their language) + try leb.writeULEB128(writer, @as(u32, 1)); + + // versioned name + { + try leb.writeULEB128(writer, @as(u32, 3)); // len of "Zig" + try writer.writeAll("Zig"); + + try leb.writeULEB128(writer, @intCast(u32, version.len)); + try writer.writeAll(version); + } + } + + // processed-by field + { + const processed_by = "processed_by"; + try leb.writeULEB128(writer, @intCast(u32, processed_by.len)); + try writer.writeAll(processed_by); + + // field_value_count (TODO: Parse object files for producer sections to detect other used tools) + try leb.writeULEB128(writer, @as(u32, 1)); + + // versioned name + { + try leb.writeULEB128(writer, @as(u32, 3)); // len of "Zig" + try writer.writeAll("Zig"); + + try leb.writeULEB128(writer, @intCast(u32, version.len)); + try writer.writeAll(version); + } + } + + try writeCustomSectionHeader( + binary_bytes.items, + header_offset, + @intCast(u32, binary_bytes.items.len - header_offset - 6), + ); +} + fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void { const Name = struct { index: u32, From 3beef5a94f0bb15e39dff7c817c6c98f2b87010e Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 2 Oct 2022 18:38:52 +0200 Subject: [PATCH 2/3] CheckObjectStep: parsing and dumping producers --- lib/std/build/CheckObjectStep.zig | 36 +++++++++++++++++++++++++++++++ src/link/Wasm.zig | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/std/build/CheckObjectStep.zig b/lib/std/build/CheckObjectStep.zig index 0acc659d20..09a8d3c148 100644 --- a/lib/std/build/CheckObjectStep.zig +++ b/lib/std/build/CheckObjectStep.zig @@ -644,6 +644,8 @@ const WasmDumper = struct { if (mem.eql(u8, name, "name")) { try parseDumpNames(reader, writer, data); + } else if (mem.eql(u8, name, "producers")) { + try parseDumpProducers(reader, writer, data); } // TODO: Implement parsing and dumping other custom sections (such as relocations) }, @@ -863,4 +865,38 @@ const WasmDumper = struct { } } } + + fn parseDumpProducers(reader: anytype, writer: anytype, data: []const u8) !void { + const field_count = try std.leb.readULEB128(u32, reader); + try writer.print("fields {d}\n", .{field_count}); + var current_field: u32 = 0; + while (current_field < field_count) : (current_field += 1) { + const field_name_length = try std.leb.readULEB128(u32, reader); + const field_name = data[reader.context.pos..][0..field_name_length]; + reader.context.pos += field_name_length; + + const value_count = try std.leb.readULEB128(u32, reader); + try writer.print( + \\field_name {s} + \\values {d} + , .{ field_name, value_count }); + try writer.writeByte('\n'); + var current_value: u32 = 0; + while (current_value < value_count) : (current_value += 1) { + const value_length = try std.leb.readULEB128(u32, reader); + const value = data[reader.context.pos..][0..value_length]; + reader.context.pos += value_length; + + const version_length = try std.leb.readULEB128(u32, reader); + const version = data[reader.context.pos..][0..version_length]; + reader.context.pos += version_length; + + try writer.print( + \\value_name {s} + \\version {s} + , .{ value, version }); + try writer.writeByte('\n'); + } + } + } }; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 67b4218b3f..b808653107 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2671,7 +2671,7 @@ fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void { // processed-by field { - const processed_by = "processed_by"; + const processed_by = "processed-by"; try leb.writeULEB128(writer, @intCast(u32, processed_by.len)); try writer.writeAll(processed_by); From fda75f53fa19ed561a8fc0d7d5ed0e6a26ee3b1d Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 2 Oct 2022 18:39:47 +0200 Subject: [PATCH 3/3] test/link: Add linker test for producers section This also turns off non-debug modes for the bss linker tests for Wasm. This is done as it's not required to guarantee to zero out the bss section for non-debug modes. --- test/link.zig | 7 +++++- test/link/wasm/producers/build.zig | 36 ++++++++++++++++++++++++++++++ test/link/wasm/producers/lib.zig | 1 + 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 test/link/wasm/producers/build.zig create mode 100644 test/link/wasm/producers/lib.zig diff --git a/test/link.zig b/test/link.zig index a798b700cd..4bde09df38 100644 --- a/test/link.zig +++ b/test/link.zig @@ -34,7 +34,7 @@ fn addWasmCases(cases: *tests.StandaloneContext) void { }); cases.addBuildFile("test/link/wasm/bss/build.zig", .{ - .build_modes = true, + .build_modes = false, .requires_stage2 = true, }); @@ -44,6 +44,11 @@ fn addWasmCases(cases: *tests.StandaloneContext) void { .use_emulation = true, }); + cases.addBuildFile("test/link/wasm/producers/build.zig", .{ + .build_modes = true, + .requires_stage2 = true, + }); + cases.addBuildFile("test/link/wasm/segments/build.zig", .{ .build_modes = true, .requires_stage2 = true, diff --git a/test/link/wasm/producers/build.zig b/test/link/wasm/producers/build.zig new file mode 100644 index 0000000000..9e8d65c48f --- /dev/null +++ b/test/link/wasm/producers/build.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const Builder = std.build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test"); + test_step.dependOn(b.getInstallStep()); + + const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); + lib.setBuildMode(mode); + lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); + lib.use_llvm = false; + lib.use_stage1 = false; + lib.use_lld = false; + lib.install(); + + const zig_version = builtin.zig_version; + var version_buf: [100]u8 = undefined; + const version_fmt = std.fmt.bufPrint(&version_buf, "version {}", .{zig_version}) catch unreachable; + + const check_lib = lib.checkObject(.wasm); + check_lib.checkStart("name producers"); + check_lib.checkNext("fields 2"); + check_lib.checkNext("field_name language"); + check_lib.checkNext("values 1"); + check_lib.checkNext("value_name Zig"); + check_lib.checkNext(version_fmt); + check_lib.checkNext("field_name processed-by"); + check_lib.checkNext("values 1"); + check_lib.checkNext("value_name Zig"); + check_lib.checkNext(version_fmt); + + test_step.dependOn(&check_lib.step); +} diff --git a/test/link/wasm/producers/lib.zig b/test/link/wasm/producers/lib.zig new file mode 100644 index 0000000000..0e416dbf18 --- /dev/null +++ b/test/link/wasm/producers/lib.zig @@ -0,0 +1 @@ +export fn foo() void {}