Skip to content

Commit c1add1e

Browse files
authored
Merge pull request #15459 from motiejus/build-id-full
stage2: implement `--build-id` styles
2 parents 5b06daf + 728ce2d commit c1add1e

File tree

9 files changed

+210
-56
lines changed

9 files changed

+210
-56
lines changed

build.zig

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,14 @@ pub fn build(b: *std.Build) !void {
165165
exe.strip = strip;
166166
exe.pie = pie;
167167
exe.sanitize_thread = sanitize_thread;
168-
exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;
169168
exe.entitlements = entitlements;
169+
170+
exe.build_id = b.option(
171+
std.Build.Step.Compile.BuildId,
172+
"build-id",
173+
"Request creation of '.note.gnu.build-id' section",
174+
);
175+
170176
b.installArtifact(exe);
171177

172178
test_step.dependOn(&exe.step);

lib/std/Build.zig

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ const TypeId = enum {
181181
@"enum",
182182
string,
183183
list,
184+
build_id,
184185
};
185186

186187
const TopLevelStep = struct {
@@ -832,13 +833,13 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
832833
} else if (mem.eql(u8, s, "false")) {
833834
return false;
834835
} else {
835-
log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });
836+
log.err("Expected -D{s} to be a boolean, but received '{s}'", .{ name, s });
836837
self.markInvalidUserInput();
837838
return null;
838839
}
839840
},
840841
.list, .map => {
841-
log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{
842+
log.err("Expected -D{s} to be a boolean, but received a {s}.", .{
842843
name, @tagName(option_ptr.value),
843844
});
844845
self.markInvalidUserInput();
@@ -847,7 +848,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
847848
},
848849
.int => switch (option_ptr.value) {
849850
.flag, .list, .map => {
850-
log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{
851+
log.err("Expected -D{s} to be an integer, but received a {s}.", .{
851852
name, @tagName(option_ptr.value),
852853
});
853854
self.markInvalidUserInput();
@@ -856,12 +857,12 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
856857
.scalar => |s| {
857858
const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
858859
error.Overflow => {
859-
log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });
860+
log.err("-D{s} value {s} cannot fit into type {s}.", .{ name, s, @typeName(T) });
860861
self.markInvalidUserInput();
861862
return null;
862863
},
863864
else => {
864-
log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });
865+
log.err("Expected -D{s} to be an integer of type {s}.", .{ name, @typeName(T) });
865866
self.markInvalidUserInput();
866867
return null;
867868
},
@@ -871,15 +872,15 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
871872
},
872873
.float => switch (option_ptr.value) {
873874
.flag, .map, .list => {
874-
log.err("Expected -D{s} to be a float, but received a {s}.\n", .{
875+
log.err("Expected -D{s} to be a float, but received a {s}.", .{
875876
name, @tagName(option_ptr.value),
876877
});
877878
self.markInvalidUserInput();
878879
return null;
879880
},
880881
.scalar => |s| {
881882
const n = std.fmt.parseFloat(T, s) catch {
882-
log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });
883+
log.err("Expected -D{s} to be a float of type {s}.", .{ name, @typeName(T) });
883884
self.markInvalidUserInput();
884885
return null;
885886
};
@@ -888,7 +889,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
888889
},
889890
.@"enum" => switch (option_ptr.value) {
890891
.flag, .map, .list => {
891-
log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{
892+
log.err("Expected -D{s} to be an enum, but received a {s}.", .{
892893
name, @tagName(option_ptr.value),
893894
});
894895
self.markInvalidUserInput();
@@ -898,25 +899,43 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
898899
if (std.meta.stringToEnum(T, s)) |enum_lit| {
899900
return enum_lit;
900901
} else {
901-
log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });
902+
log.err("Expected -D{s} to be of type {s}.", .{ name, @typeName(T) });
902903
self.markInvalidUserInput();
903904
return null;
904905
}
905906
},
906907
},
907908
.string => switch (option_ptr.value) {
908909
.flag, .list, .map => {
909-
log.err("Expected -D{s} to be a string, but received a {s}.\n", .{
910+
log.err("Expected -D{s} to be a string, but received a {s}.", .{
910911
name, @tagName(option_ptr.value),
911912
});
912913
self.markInvalidUserInput();
913914
return null;
914915
},
915916
.scalar => |s| return s,
916917
},
918+
.build_id => switch (option_ptr.value) {
919+
.flag, .map, .list => {
920+
log.err("Expected -D{s} to be an enum, but received a {s}.", .{
921+
name, @tagName(option_ptr.value),
922+
});
923+
self.markInvalidUserInput();
924+
return null;
925+
},
926+
.scalar => |s| {
927+
if (Step.Compile.BuildId.parse(s)) |build_id| {
928+
return build_id;
929+
} else |err| {
930+
log.err("unable to parse option '-D{s}': {s}", .{ name, @errorName(err) });
931+
self.markInvalidUserInput();
932+
return null;
933+
}
934+
},
935+
},
917936
.list => switch (option_ptr.value) {
918937
.flag, .map => {
919-
log.err("Expected -D{s} to be a list, but received a {s}.\n", .{
938+
log.err("Expected -D{s} to be a list, but received a {s}.", .{
920939
name, @tagName(option_ptr.value),
921940
});
922941
self.markInvalidUserInput();
@@ -1183,15 +1202,18 @@ pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool {
11831202
}
11841203

11851204
fn typeToEnum(comptime T: type) TypeId {
1186-
return switch (@typeInfo(T)) {
1187-
.Int => .int,
1188-
.Float => .float,
1189-
.Bool => .bool,
1190-
.Enum => .@"enum",
1191-
else => switch (T) {
1192-
[]const u8 => .string,
1193-
[]const []const u8 => .list,
1194-
else => @compileError("Unsupported type: " ++ @typeName(T)),
1205+
return switch (T) {
1206+
Step.Compile.BuildId => .build_id,
1207+
else => return switch (@typeInfo(T)) {
1208+
.Int => .int,
1209+
.Float => .float,
1210+
.Bool => .bool,
1211+
.Enum => .@"enum",
1212+
else => switch (T) {
1213+
[]const u8 => .string,
1214+
[]const []const u8 => .list,
1215+
else => @compileError("Unsupported type: " ++ @typeName(T)),
1216+
},
11951217
},
11961218
};
11971219
}

lib/std/Build/Cache.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,10 @@ pub const HashHelper = struct {
235235
.none => {},
236236
}
237237
},
238+
std.Build.Step.Compile.BuildId => switch (x) {
239+
.none, .fast, .uuid, .sha1, .md5 => hh.add(std.meta.activeTag(x)),
240+
.hexstring => |hex_string| hh.addBytes(hex_string.toSlice()),
241+
},
238242
else => switch (@typeInfo(@TypeOf(x))) {
239243
.Bool, .Int, .Enum, .Array => hh.addBytes(mem.asBytes(&x)),
240244
else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))),

lib/std/Build/Step/Compile.zig

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ each_lib_rpath: ?bool = null,
116116
/// As an example, the bloaty project refuses to work unless its inputs have
117117
/// build ids, in order to prevent accidental mismatches.
118118
/// The default is to not include this section because it slows down linking.
119-
build_id: ?bool = null,
119+
build_id: ?BuildId = null,
120120

121121
/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
122122
/// file.
@@ -288,6 +288,82 @@ pub const Options = struct {
288288
use_lld: ?bool = null,
289289
};
290290

291+
pub const BuildId = union(enum) {
292+
none,
293+
fast,
294+
uuid,
295+
sha1,
296+
md5,
297+
hexstring: HexString,
298+
299+
pub fn eql(a: BuildId, b: BuildId) bool {
300+
const a_tag = std.meta.activeTag(a);
301+
const b_tag = std.meta.activeTag(b);
302+
if (a_tag != b_tag) return false;
303+
return switch (a) {
304+
.none, .fast, .uuid, .sha1, .md5 => true,
305+
.hexstring => |a_hexstring| mem.eql(u8, a_hexstring.toSlice(), b.hexstring.toSlice()),
306+
};
307+
}
308+
309+
pub const HexString = struct {
310+
bytes: [32]u8,
311+
len: u8,
312+
313+
/// Result is byte values, *not* hex-encoded.
314+
pub fn toSlice(hs: *const HexString) []const u8 {
315+
return hs.bytes[0..hs.len];
316+
}
317+
};
318+
319+
/// Input is byte values, *not* hex-encoded.
320+
/// Asserts `bytes` fits inside `HexString`
321+
pub fn initHexString(bytes: []const u8) BuildId {
322+
var result: BuildId = .{ .hexstring = .{
323+
.bytes = undefined,
324+
.len = @intCast(u8, bytes.len),
325+
} };
326+
@memcpy(result.hexstring.bytes[0..bytes.len], bytes);
327+
return result;
328+
}
329+
330+
/// Converts UTF-8 text to a `BuildId`.
331+
pub fn parse(text: []const u8) !BuildId {
332+
if (mem.eql(u8, text, "none")) {
333+
return .none;
334+
} else if (mem.eql(u8, text, "fast")) {
335+
return .fast;
336+
} else if (mem.eql(u8, text, "uuid")) {
337+
return .uuid;
338+
} else if (mem.eql(u8, text, "sha1") or mem.eql(u8, text, "tree")) {
339+
return .sha1;
340+
} else if (mem.eql(u8, text, "md5")) {
341+
return .md5;
342+
} else if (mem.startsWith(u8, text, "0x")) {
343+
var result: BuildId = .{ .hexstring = undefined };
344+
const slice = try std.fmt.hexToBytes(&result.hexstring.bytes, text[2..]);
345+
result.hexstring.len = @intCast(u8, slice.len);
346+
return result;
347+
}
348+
return error.InvalidBuildIdStyle;
349+
}
350+
351+
test parse {
352+
try std.testing.expectEqual(BuildId.md5, try parse("md5"));
353+
try std.testing.expectEqual(BuildId.none, try parse("none"));
354+
try std.testing.expectEqual(BuildId.fast, try parse("fast"));
355+
try std.testing.expectEqual(BuildId.uuid, try parse("uuid"));
356+
try std.testing.expectEqual(BuildId.sha1, try parse("sha1"));
357+
try std.testing.expectEqual(BuildId.sha1, try parse("tree"));
358+
359+
try std.testing.expect(BuildId.initHexString("").eql(try parse("0x")));
360+
try std.testing.expect(BuildId.initHexString("\x12\x34\x56").eql(try parse("0x123456")));
361+
try std.testing.expectError(error.InvalidLength, parse("0x12-34"));
362+
try std.testing.expectError(error.InvalidCharacter, parse("0xfoobbb"));
363+
try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx"));
364+
}
365+
};
366+
291367
pub const Kind = enum {
292368
exe,
293369
lib,
@@ -1810,7 +1886,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
18101886

18111887
try addFlag(&zig_args, "valgrind", self.valgrind_support);
18121888
try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
1813-
try addFlag(&zig_args, "build-id", self.build_id);
1889+
1890+
if (self.build_id) |build_id| {
1891+
try zig_args.append(switch (build_id) {
1892+
.hexstring => |hs| b.fmt("--build-id=0x{s}", .{
1893+
std.fmt.fmtSliceHexLower(hs.toSlice()),
1894+
}),
1895+
.none, .fast, .uuid, .sha1, .md5 => b.fmt("--build-id={s}", .{@tagName(build_id)}),
1896+
});
1897+
}
18141898

18151899
if (self.zig_lib_dir) |dir| {
18161900
try zig_args.append("--zig-lib-dir");

src/Compilation.zig

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const wasi_libc = @import("wasi_libc.zig");
2929
const fatal = @import("main.zig").fatal;
3030
const clangMain = @import("main.zig").clangMain;
3131
const Module = @import("Module.zig");
32+
const BuildId = std.Build.CompileStep.BuildId;
3233
const Cache = std.Build.Cache;
3334
const translate_c = @import("translate_c.zig");
3435
const clang = @import("clang.zig");
@@ -563,7 +564,7 @@ pub const InitOptions = struct {
563564
linker_print_map: bool = false,
564565
linker_opt_bisect_limit: i32 = -1,
565566
each_lib_rpath: ?bool = null,
566-
build_id: ?bool = null,
567+
build_id: ?BuildId = null,
567568
disable_c_depfile: bool = false,
568569
linker_z_nodelete: bool = false,
569570
linker_z_notext: bool = false,
@@ -797,7 +798,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
797798
const unwind_tables = options.want_unwind_tables orelse
798799
(link_libunwind or target_util.needUnwindTables(options.target));
799800
const link_eh_frame_hdr = options.link_eh_frame_hdr or unwind_tables;
800-
const build_id = options.build_id orelse false;
801+
const build_id = options.build_id orelse .none;
801802

802803
// Make a decision on whether to use LLD or our own linker.
803804
const use_lld = options.use_lld orelse blk: {
@@ -828,7 +829,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
828829
options.output_mode == .Lib or
829830
options.linker_script != null or options.version_script != null or
830831
options.emit_implib != null or
831-
build_id or
832+
build_id != .none or
832833
options.symbol_wrap_set.count() > 0)
833834
{
834835
break :blk true;

src/link.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const wasi_libc = @import("wasi_libc.zig");
1010

1111
const Air = @import("Air.zig");
1212
const Allocator = std.mem.Allocator;
13+
const BuildId = std.Build.CompileStep.BuildId;
1314
const Cache = std.Build.Cache;
1415
const Compilation = @import("Compilation.zig");
1516
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
@@ -157,7 +158,7 @@ pub const Options = struct {
157158
skip_linker_dependencies: bool,
158159
parent_compilation_link_libc: bool,
159160
each_lib_rpath: bool,
160-
build_id: bool,
161+
build_id: BuildId,
161162
disable_lld_caching: bool,
162163
is_test: bool,
163164
hash_style: HashStyle,

src/link/Elf.zig

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,8 +1542,18 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
15421542
try argv.append("-z");
15431543
try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}));
15441544

1545-
if (self.base.options.build_id) {
1546-
try argv.append("--build-id");
1545+
switch (self.base.options.build_id) {
1546+
.none => {},
1547+
.fast, .uuid, .sha1, .md5 => {
1548+
try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
1549+
@tagName(self.base.options.build_id),
1550+
}));
1551+
},
1552+
.hexstring => |hs| {
1553+
try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{
1554+
std.fmt.fmtSliceHexLower(hs.toSlice()),
1555+
}));
1556+
},
15471557
}
15481558
}
15491559

0 commit comments

Comments
 (0)