From 59219e7e91cbfd785f89ec792d3950b9b9ad9b05 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 May 2022 13:36:33 -0700 Subject: [PATCH] stage2: add support for -fbuild-id,-fno-build-id closes #3047 --- build.zig | 1 + lib/std/build.zig | 14 ++++++++++++++ src/Compilation.zig | 3 +++ src/link.zig | 1 + src/link/Elf.zig | 9 ++++++++- src/main.zig | 24 +++++++++++++++++++++--- 6 files changed, 48 insertions(+), 4 deletions(-) diff --git a/build.zig b/build.zig index 24d874b924..163c0bb5a7 100644 --- a/build.zig +++ b/build.zig @@ -145,6 +145,7 @@ pub fn build(b: *Builder) !void { const exe = b.addExecutable("zig", main_file); exe.strip = strip; + exe.build_id = !strip; exe.install(); exe.setBuildMode(mode); exe.setTarget(target); diff --git a/lib/std/build.zig b/lib/std/build.zig index 668720aa79..a738a39a1b 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1549,6 +1549,12 @@ pub const LibExeObjStep = struct { valgrind_support: ?bool = null, each_lib_rpath: ?bool = null, + /// On ELF targets, this will emit a link section called ".note.gnu.build-id" + /// which can be used to coordinate a stripped binary with its debug symbols. + /// As an example, the bloaty project refuses to work unless its inputs have + /// build ids, in order to prevent accidental mismatches. + /// The default is to not include this section because it slows down linking. + build_id: ?bool = null, /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF /// file. @@ -2953,6 +2959,14 @@ pub const LibExeObjStep = struct { } } + if (self.build_id) |build_id| { + if (build_id) { + try zig_args.append("-fbuild-id"); + } else { + try zig_args.append("-fno-build-id"); + } + } + if (self.override_lib_dir) |dir| { try zig_args.append("--zig-lib-dir"); try zig_args.append(builder.pathFromRoot(dir)); diff --git a/src/Compilation.zig b/src/Compilation.zig index 31782e732b..eb8b2b4d69 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -756,6 +756,7 @@ pub const InitOptions = struct { linker_global_base: ?u64 = null, linker_export_symbol_names: []const []const u8 = &.{}, each_lib_rpath: ?bool = null, + build_id: ?bool = null, disable_c_depfile: bool = false, linker_z_nodelete: bool = false, linker_z_notext: bool = false, @@ -1639,6 +1640,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .skip_linker_dependencies = options.skip_linker_dependencies, .parent_compilation_link_libc = options.parent_compilation_link_libc, .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os, + .build_id = options.build_id orelse false, .cache_mode = cache_mode, .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole, .subsystem = options.subsystem, @@ -2339,6 +2341,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.addListOfBytes(comp.bin_file.options.lib_dirs); man.hash.addListOfBytes(comp.bin_file.options.rpath_list); man.hash.add(comp.bin_file.options.each_lib_rpath); + man.hash.add(comp.bin_file.options.build_id); man.hash.add(comp.bin_file.options.skip_linker_dependencies); man.hash.add(comp.bin_file.options.z_nodelete); man.hash.add(comp.bin_file.options.z_notext); diff --git a/src/link.zig b/src/link.zig index 8647259651..60baa6a92a 100644 --- a/src/link.zig +++ b/src/link.zig @@ -146,6 +146,7 @@ pub const Options = struct { skip_linker_dependencies: bool, parent_compilation_link_libc: bool, each_lib_rpath: bool, + build_id: bool, disable_lld_caching: bool, is_test: bool, use_stage1: bool, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 06b241eb4d..442ad73ea7 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1311,7 +1311,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // We can skip hashing libc and libc++ components that we are in charge of building from Zig // installation sources because they are always a product of the compiler version + target information. man.hash.addOptionalBytes(self.base.options.entry); - man.hash.add(stack_size); man.hash.addOptional(self.base.options.image_base_override); man.hash.add(gc_sections); man.hash.add(self.base.options.eh_frame_hdr); @@ -1320,6 +1319,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v man.hash.addListOfBytes(self.base.options.lib_dirs); man.hash.addListOfBytes(self.base.options.rpath_list); man.hash.add(self.base.options.each_lib_rpath); + if (self.base.options.output_mode == .Exe) { + man.hash.add(stack_size); + man.hash.add(self.base.options.build_id); + } man.hash.add(self.base.options.skip_linker_dependencies); man.hash.add(self.base.options.z_nodelete); man.hash.add(self.base.options.z_notext); @@ -1450,6 +1453,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v if (self.base.options.output_mode == .Exe) { try argv.append("-z"); try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size})); + + if (self.base.options.build_id) { + try argv.append("--build-id"); + } } if (self.base.options.image_base_override) |image_base| { diff --git a/src/main.zig b/src/main.zig index a9085d7c7f..37c584ba57 100644 --- a/src/main.zig +++ b/src/main.zig @@ -423,6 +423,8 @@ const usage_build_generic = \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries \\ -fno-allow-shlib-undefined Disallows undefined symbols in shared libraries + \\ -fbuild-id Helps coordinate stripped binaries with debug symbols + \\ -fno-build-id (default) Saves a bit of time linking \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker \\ --emit-relocs Enable output of relocation sections for post build tools \\ -z [arg] Set linker extension flags @@ -671,6 +673,7 @@ fn buildOutputType( var link_eh_frame_hdr = false; var link_emit_relocs = false; var each_lib_rpath: ?bool = null; + var build_id: ?bool = null; var sysroot: ?[]const u8 = null; var libc_paths_file: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIBC"); var machine_code_model: std.builtin.CodeModel = .default; @@ -1030,6 +1033,10 @@ fn buildOutputType( each_lib_rpath = true; } else if (mem.eql(u8, arg, "-fno-each-lib-rpath")) { each_lib_rpath = false; + } else if (mem.eql(u8, arg, "-fbuild-id")) { + build_id = true; + } else if (mem.eql(u8, arg, "-fno-build-id")) { + build_id = false; } else if (mem.eql(u8, arg, "--enable-cache")) { enable_cache = true; } else if (mem.eql(u8, arg, "--test-cmd-bin")) { @@ -1415,10 +1422,20 @@ fn buildOutputType( while (split_it.next()) |linker_arg| { // Handle nested-joined args like `-Wl,-rpath=foo`. // Must be prefixed with 1 or 2 dashes. - if (linker_arg.len >= 3 and linker_arg[0] == '-' and linker_arg[2] != '-') { + if (linker_arg.len >= 3 and + linker_arg[0] == '-' and + linker_arg[2] != '-') + { if (mem.indexOfScalar(u8, linker_arg, '=')) |equals_pos| { - try linker_args.append(linker_arg[0..equals_pos]); - try linker_args.append(linker_arg[equals_pos + 1 ..]); + const key = linker_arg[0..equals_pos]; + const value = linker_arg[equals_pos + 1 ..]; + if (mem.eql(u8, key, "build-id")) { + build_id = true; + warn("ignoring build-id style argument: '{s}'", .{value}); + continue; + } + try linker_args.append(key); + try linker_args.append(value); continue; } } @@ -2727,6 +2744,7 @@ fn buildOutputType( .stack_report = stack_report, .is_test = arg_mode == .zig_test, .each_lib_rpath = each_lib_rpath, + .build_id = build_id, .test_evented_io = test_evented_io, .test_filter = test_filter, .test_name_prefix = test_name_prefix,