From 1f410b500c8dbe33191a54a4ae02a6fb0febec46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Tue, 14 Jun 2022 14:41:47 +0300 Subject: [PATCH 1/2] ELF: understand -Wl,--compress-debug-sections This argument is both a compiler and a linker flag. The linker flag was not understood; now it is. Go likes to use it as a linker flag. Tested with sqlite3. The size difference is significant, and I confirmed gdb understands both binaries. zlib: 3.66MB ------------ CC="zig cc" CFLAGS="-Wl,--compress-debug-sections=zlib -O2" ./configure --disable-tcl make FILE SIZE VM SIZE -------------- -------------- 39.1% 1.43Mi 88.4% 1.43Mi .text 19.6% 734Ki 0.0% 0 .debug_info 16.4% 613Ki 0.0% 0 .debug_loc 13.1% 492Ki 0.0% 0 .debug_line 4.2% 157Ki 9.5% 157Ki .rodata 2.3% 87.6Ki 0.0% 0 .debug_ranges 1.5% 56.2Ki 0.0% 0 .symtab 1.1% 40.2Ki 0.0% 0 .strtab 1.0% 38.2Ki 0.0% 0 .debug_str 0.7% 26.2Ki 0.0% 0 .debug_frame 0.4% 15.3Ki 0.9% 15.3Ki .data 0.1% 4.71Ki 0.3% 4.71Ki .dynsym 0.1% 3.65Ki 0.2% 3.26Ki [16 Others] 0.1% 2.55Ki 0.2% 2.55Ki .rela.plt 0.1% 2.12Ki 0.0% 0 [ELF Section Headers] 0.0% 0 0.1% 2.02Ki .bss 0.0% 1.84Ki 0.1% 1.84Ki .dynstr 0.0% 1.72Ki 0.1% 1.72Ki .plt 0.0% 1.58Ki 0.1% 1.58Ki .hash 0.0% 1.17Ki 0.0% 0 .debug_abbrev 0.0% 1.01Ki 0.1% 1.01Ki .rela.dyn 100.0% 3.66Mi 100.0% 1.62Mi TOTAL none: 8.56MB ------------ CC="zig cc" CFLAGS="-O2" ./configure --disable-tcl make FILE SIZE VM SIZE -------------- -------------- 41.1% 3.52Mi 0.0% 0 .debug_loc 18.5% 1.59Mi 0.0% 0 .debug_info 16.7% 1.43Mi 88.4% 1.43Mi .text 11.8% 1.01Mi 0.0% 0 .debug_line 5.9% 515Ki 0.0% 0 .debug_ranges 1.8% 157Ki 9.5% 157Ki .rodata 1.3% 118Ki 0.0% 0 .debug_frame 1.3% 110Ki 0.0% 0 .debug_str 0.6% 56.2Ki 0.0% 0 .symtab 0.5% 40.2Ki 0.0% 0 .strtab 0.2% 15.3Ki 0.9% 15.3Ki .data 0.1% 4.71Ki 0.3% 4.71Ki .dynsym 0.0% 3.64Ki 0.2% 3.26Ki [16 Others] 0.0% 2.98Ki 0.0% 0 .debug_abbrev 0.0% 2.55Ki 0.2% 2.55Ki .rela.plt 0.0% 2.12Ki 0.0% 0 [ELF Section Headers] 0.0% 0 0.1% 2.02Ki .bss 0.0% 1.84Ki 0.1% 1.84Ki .dynstr 0.0% 1.72Ki 0.1% 1.72Ki .plt 0.0% 1.58Ki 0.1% 1.58Ki .hash 0.0% 1.01Ki 0.1% 1.01Ki .rela.dyn 100.0% 8.56Mi 100.0% 1.62Mi TOTAL --- src/Compilation.zig | 3 +++ src/link.zig | 3 +++ src/link/Elf.zig | 6 ++++++ src/main.zig | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/src/Compilation.zig b/src/Compilation.zig index b00f135813..8abe2770ec 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -860,6 +860,7 @@ pub const InitOptions = struct { linker_nxcompat: bool = false, linker_dynamicbase: bool = false, linker_optimization: ?u8 = null, + linker_compress_debug_sections: ?link.CompressDebugSections = null, major_subsystem_version: ?u32 = null, minor_subsystem_version: ?u32 = null, clang_passthrough_mode: bool = false, @@ -1687,6 +1688,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .no_builtin = options.no_builtin, .allow_shlib_undefined = options.linker_allow_shlib_undefined, .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, + .compress_debug_sections = options.linker_compress_debug_sections, .import_memory = options.linker_import_memory orelse false, .import_table = options.linker_import_table, .export_table = options.linker_export_table, @@ -2459,6 +2461,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.add(comp.bin_file.options.z_now); man.hash.add(comp.bin_file.options.z_relro); man.hash.add(comp.bin_file.options.hash_style); + man.hash.addOptional(comp.bin_file.options.compress_debug_sections); man.hash.add(comp.bin_file.options.include_compiler_rt); if (comp.bin_file.options.link_libc) { man.hash.add(comp.bin_file.options.libc_installation != null); diff --git a/src/link.zig b/src/link.zig index 871ceded6c..7975684263 100644 --- a/src/link.zig +++ b/src/link.zig @@ -123,6 +123,7 @@ pub const Options = struct { nxcompat: bool, dynamicbase: bool, linker_optimization: u8, + compress_debug_sections: ?CompressDebugSections, bind_global_refs_locally: bool, import_memory: bool, import_table: bool, @@ -219,6 +220,8 @@ pub const Options = struct { pub const HashStyle = enum { sysv, gnu, both }; +pub const CompressDebugSections = enum { none, zlib }; + pub const File = struct { tag: Tag, options: Options, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index f7d582bae7..bab7fb44cc 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1351,6 +1351,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v link.hashAddSystemLibs(&man.hash, self.base.options.system_libs); man.hash.add(allow_shlib_undefined); man.hash.add(self.base.options.bind_global_refs_locally); + man.hash.addOptional(self.base.options.compress_debug_sections); man.hash.add(self.base.options.tsan); man.hash.addOptionalBytes(self.base.options.sysroot); man.hash.add(self.base.options.linker_optimization); @@ -1754,6 +1755,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append("--allow-shlib-undefined"); } + if (self.base.options.compress_debug_sections) |how| { + const arg = try std.fmt.allocPrint(arena, "--compress-debug-sections={s}", .{@tagName(how)}); + try argv.append(arg); + } + if (self.base.options.bind_global_refs_locally) { try argv.append("-Bsymbolic"); } diff --git a/src/main.zig b/src/main.zig index 4fe048bb93..c23aa3111a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -443,6 +443,8 @@ const usage_build_generic = \\ -dynamic Force output to be dynamically linked \\ -static Force output to be statically linked \\ -Bsymbolic Bind global references locally + \\ --compress-debug-sections= Compress DWARF debug sections + \\ none|zlib \\ --subsystem [subsystem] (Windows) /SUBSYSTEM: to the linker \\ --stack [size] Override default stack size \\ --image-base [addr] Set base address for executable image @@ -657,6 +659,7 @@ fn buildOutputType( var version_script: ?[]const u8 = null; var disable_c_depfile = false; var linker_gc_sections: ?bool = null; + var linker_compress_debug_sections: ?link.CompressDebugSections = null; var linker_allow_shlib_undefined: ?bool = null; var linker_bind_global_refs_locally: ?bool = null; var linker_import_memory: ?bool = null; @@ -938,6 +941,11 @@ fn buildOutputType( install_name = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); }; + } else if (mem.startsWith(u8, arg, "--compress-debug-sections=")) { + const param = arg["--compress-debug-sections=".len..]; + linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, param) orelse { + fatal("expected --compress-debug-sections=[none|zlib], found '{s}'", .{param}); + }; } else if (mem.eql(u8, arg, "-pagezero_size")) { const next_arg = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); @@ -1776,6 +1784,15 @@ fn buildOutputType( linker_global_base = parseIntSuffix(arg, "--global-base=".len); } else if (mem.startsWith(u8, arg, "--export=")) { try linker_export_symbol_names.append(arg["--export=".len..]); + } else if (mem.eql(u8, arg, "--compress-debug-sections")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{s}'", .{arg}); + } + const arg1 = linker_args.items[i]; + linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, arg1) orelse { + fatal("expected [none|zlib] after --compress-debug-sections, found '{s}'", .{arg1}); + }; } else if (mem.eql(u8, arg, "-z")) { i += 1; if (i >= linker_args.items.len) { @@ -2849,6 +2866,7 @@ fn buildOutputType( .linker_nxcompat = linker_nxcompat, .linker_dynamicbase = linker_dynamicbase, .linker_optimization = linker_optimization, + .linker_compress_debug_sections = linker_compress_debug_sections, .major_subsystem_version = major_subsystem_version, .minor_subsystem_version = minor_subsystem_version, .link_eh_frame_hdr = link_eh_frame_hdr, From 2b99182e25e045109b090a794bb06295b09a0eeb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Jul 2022 14:18:56 -0700 Subject: [PATCH 2/2] stage2: cleanups to --compress-debug-sections * make the setting in the linker backend be non-optional; by this time all defaults are supposed to be resolved. * integrate with `zig cc` * change the CLI parsing to match C compiler parsing, allowing `--compress-debug-sections` alone to choose a default encoding of zlib. --- src/Compilation.zig | 4 ++-- src/clang_options_data.zig | 36 +++++++++++++++++----------------- src/link.zig | 2 +- src/link/Elf.zig | 8 ++++---- src/main.zig | 17 ++++++++++++++-- tools/update_clang_options.zig | 8 ++++++++ 6 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 8abe2770ec..ce8fc7dcaf 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1688,7 +1688,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .no_builtin = options.no_builtin, .allow_shlib_undefined = options.linker_allow_shlib_undefined, .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, - .compress_debug_sections = options.linker_compress_debug_sections, + .compress_debug_sections = options.linker_compress_debug_sections orelse .none, .import_memory = options.linker_import_memory orelse false, .import_table = options.linker_import_table, .export_table = options.linker_export_table, @@ -2461,7 +2461,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.add(comp.bin_file.options.z_now); man.hash.add(comp.bin_file.options.z_relro); man.hash.add(comp.bin_file.options.hash_style); - man.hash.addOptional(comp.bin_file.options.compress_debug_sections); + man.hash.add(comp.bin_file.options.compress_debug_sections); man.hash.add(comp.bin_file.options.include_compiler_rt); if (comp.bin_file.options.link_libc) { man.hash.add(comp.bin_file.options.libc_installation != null); diff --git a/src/clang_options_data.zig b/src/clang_options_data.zig index cd9d603157..d4320b1619 100644 --- a/src/clang_options_data.zig +++ b/src/clang_options_data.zig @@ -2308,7 +2308,7 @@ flagpd1("compiler-options-dump"), .{ .name = "compress-debug-sections", .syntax = .flag, - .zig_equivalent = .other, + .zig_equivalent = .compress_debug_sections, .pd1 = true, .pd2 = true, .psl = false, @@ -3834,7 +3834,7 @@ m("mavx512cd"), m("mavx512dq"), m("mavx512er"), m("mavx512f"), -flagpd1("mavx512fp16"), +m("mavx512fp16"), m("mavx512ifma"), m("mavx512pf"), m("mavx512vbmi"), @@ -3867,7 +3867,7 @@ flagpd1("mconstructor-aliases"), flagpd1("mcpu=?"), m("mcrbits"), m("mcrc"), -flagpd1("mcrc32"), +m("mcrc32"), m("mcumode"), m("mcx16"), sepd1("mdebug-pass"), @@ -3892,8 +3892,8 @@ flagpd1("mfancy-math-387"), flagpd1("mfentry"), flagpd1("mfix4300"), flagpd1("mfix-and-continue"), -flagpd1("mfix-cmse-cve-2021-35465"), -flagpd1("mfix-cortex-a53-835769"), +m("mfix-cmse-cve-2021-35465"), +m("mfix-cortex-a53-835769"), m("mfloat128"), sepd1("mfloat-abi"), m("mfma"), @@ -3913,8 +3913,8 @@ flagpd1("mglobal-merge"), flagpd1("mgpopt"), m("mhard-float"), m("mhvx"), -flagpd1("mhvx-ieee-fp"), -flagpd1("mhvx-qfloat"), +m("mhvx-ieee-fp"), +m("mhvx-qfloat"), m("mhreset"), m("mhtm"), flagpd1("miamcu"), @@ -4005,7 +4005,7 @@ m("mno-avx512cd"), m("mno-avx512dq"), m("mno-avx512er"), m("mno-avx512f"), -flagpd1("mno-avx512fp16"), +m("mno-avx512fp16"), m("mno-avx512ifma"), m("mno-avx512pf"), m("mno-avx512vbmi"), @@ -4019,7 +4019,7 @@ flagpd1("mno-backchain"), m("mno-bmi"), m("mno-bmi2"), flagpd1("mno-branch-likely"), -flagpd1("mno-bti-at-return-twice"), +m("mno-bti-at-return-twice"), m("mno-bulk-memory"), flagpd1("mno-check-zero-division"), m("mno-cldemote"), @@ -4032,7 +4032,7 @@ flagpd1("mno-constant-cfstrings"), flagpd1("mno-constructor-aliases"), m("mno-crbits"), m("mno-crc"), -flagpd1("mno-crc32"), +m("mno-crc32"), m("mno-cumode"), m("mno-cx16"), m("mno-dsp"), @@ -4044,8 +4044,8 @@ flagpd1("mnoexecstack"), m("mno-execute-only"), flagpd1("mno-extern-sdata"), m("mno-f16c"), -flagpd1("mno-fix-cmse-cve-2021-35465"), -flagpd1("mno-fix-cortex-a53-835769"), +m("mno-fix-cmse-cve-2021-35465"), +m("mno-fix-cortex-a53-835769"), m("mno-float128"), m("mno-fma"), m("mno-fma4"), @@ -4058,8 +4058,8 @@ m("mno-ginv"), flagpd1("mno-global-merge"), flagpd1("mno-gpopt"), m("mno-hvx"), -flagpd1("mno-hvx-ieee-fp"), -flagpd1("mno-hvx-qfloat"), +m("mno-hvx-ieee-fp"), +m("mno-hvx-qfloat"), m("mno-hreset"), m("mno-htm"), flagpd1("mno-iamcu"), @@ -4137,7 +4137,7 @@ m("mno-reference-types"), m("mno-relax"), flagpd1("mno-relax-all"), flagpd1("mno-relax-pic-calls"), -flagpd1("mno-relaxed-simd"), +m("mno-relaxed-simd"), flagpd1("mno-restrict-it"), m("mno-retpoline"), m("mno-retpoline-external-thunk"), @@ -4259,7 +4259,7 @@ flagpd1("mrelax-pic-calls"), .pd2 = true, .psl = false, }, -flagpd1("mrelaxed-simd"), +m("mrelaxed-simd"), sepd1("mrelocation-model"), flagpd1("mrestrict-it"), m("mretpoline"), @@ -4323,7 +4323,7 @@ m("mv66"), m("mv67"), flagpd1("mv67t"), m("mv68"), -flagpd1("mv69"), +m("mv69"), m("mvaes"), m("mvirt"), m("mvpclmulqdq"), @@ -5087,7 +5087,7 @@ joinpd1("msign-return-address-key="), .{ .name = "compress-debug-sections=", .syntax = .joined, - .zig_equivalent = .other, + .zig_equivalent = .compress_debug_sections, .pd1 = true, .pd2 = true, .psl = false, diff --git a/src/link.zig b/src/link.zig index 7975684263..9b12fc2b48 100644 --- a/src/link.zig +++ b/src/link.zig @@ -123,7 +123,7 @@ pub const Options = struct { nxcompat: bool, dynamicbase: bool, linker_optimization: u8, - compress_debug_sections: ?CompressDebugSections, + compress_debug_sections: CompressDebugSections, bind_global_refs_locally: bool, import_memory: bool, import_table: bool, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index bab7fb44cc..3e9b1ab3ed 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1351,7 +1351,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v link.hashAddSystemLibs(&man.hash, self.base.options.system_libs); man.hash.add(allow_shlib_undefined); man.hash.add(self.base.options.bind_global_refs_locally); - man.hash.addOptional(self.base.options.compress_debug_sections); + man.hash.add(self.base.options.compress_debug_sections); man.hash.add(self.base.options.tsan); man.hash.addOptionalBytes(self.base.options.sysroot); man.hash.add(self.base.options.linker_optimization); @@ -1755,9 +1755,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append("--allow-shlib-undefined"); } - if (self.base.options.compress_debug_sections) |how| { - const arg = try std.fmt.allocPrint(arena, "--compress-debug-sections={s}", .{@tagName(how)}); - try argv.append(arg); + switch (self.base.options.compress_debug_sections) { + .none => {}, + .zlib => try argv.append("--compress-debug-sections=zlib"), } if (self.base.options.bind_global_refs_locally) { diff --git a/src/main.zig b/src/main.zig index c23aa3111a..643cc3498b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -443,8 +443,9 @@ const usage_build_generic = \\ -dynamic Force output to be dynamically linked \\ -static Force output to be statically linked \\ -Bsymbolic Bind global references locally - \\ --compress-debug-sections= Compress DWARF debug sections - \\ none|zlib + \\ --compress-debug-sections=[e] Debug section compression settings + \\ none No compression + \\ zlib Compression with deflate/inflate \\ --subsystem [subsystem] (Windows) /SUBSYSTEM: to the linker \\ --stack [size] Override default stack size \\ --image-base [addr] Set base address for executable image @@ -946,6 +947,8 @@ fn buildOutputType( linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, param) orelse { fatal("expected --compress-debug-sections=[none|zlib], found '{s}'", .{param}); }; + } else if (mem.eql(u8, arg, "--compress-debug-sections")) { + linker_compress_debug_sections = link.CompressDebugSections.zlib; } else if (mem.eql(u8, arg, "-pagezero_size")) { const next_arg = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); @@ -1648,6 +1651,15 @@ fn buildOutputType( .weak_library => try system_libs.put(it.only_arg, .{ .weak = true }), .weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }), .headerpad_max_install_names => headerpad_max_install_names = true, + .compress_debug_sections => { + if (it.only_arg.len == 0) { + linker_compress_debug_sections = .zlib; + } else { + linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, it.only_arg) orelse { + fatal("expected [none|zlib] after --compress-debug-sections, found '{s}'", .{it.only_arg}); + }; + } + }, } } // Parse linker args. @@ -4617,6 +4629,7 @@ pub const ClangArgIterator = struct { weak_library, weak_framework, headerpad_max_install_names, + compress_debug_sections, }; const Args = struct { diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index fc460d8744..2c13f1082b 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -460,6 +460,14 @@ const known_options = [_]KnownOpt{ .name = "headerpad_max_install_names", .ident = "headerpad_max_install_names", }, + .{ + .name = "compress-debug-sections", + .ident = "compress_debug_sections", + }, + .{ + .name = "compress-debug-sections=", + .ident = "compress_debug_sections", + }, }; const blacklisted_options = [_][]const u8{};