| ... | @@ -27,6 +27,7 @@ pub fn cmdObjCopy( | ... | @@ -27,6 +27,7 @@ pub fn cmdObjCopy( |
| 27 | var strip_all: bool = false; | 27 | var strip_all: bool = false; |
| 28 | var strip_debug: bool = false; | 28 | var strip_debug: bool = false; |
| 29 | var only_keep_debug: bool = false; | 29 | var only_keep_debug: bool = false; |
| | 30 | var compress_debug_sections: bool = false; |
| 30 | var listen = false; | 31 | var listen = false; |
| 31 | while (i < args.len) : (i += 1) { | 32 | while (i < args.len) : (i += 1) { |
| 32 | const arg = args[i]; | 33 | const arg = args[i]; |
| ... | @@ -78,6 +79,8 @@ pub fn cmdObjCopy( | ... | @@ -78,6 +79,8 @@ pub fn cmdObjCopy( |
| 78 | strip_all = true; | 79 | strip_all = true; |
| 79 | } else if (mem.eql(u8, arg, "--only-keep-debug")) { | 80 | } else if (mem.eql(u8, arg, "--only-keep-debug")) { |
| 80 | only_keep_debug = true; | 81 | only_keep_debug = true; |
| | 82 | } else if (mem.eql(u8, arg, "--compress-debug-sections")) { |
| | 83 | compress_debug_sections = true; |
| 81 | } else if (mem.startsWith(u8, arg, "--add-gnu-debuglink=")) { | 84 | } else if (mem.startsWith(u8, arg, "--add-gnu-debuglink=")) { |
| 82 | opt_add_debuglink = arg["--add-gnu-debuglink=".len..]; | 85 | opt_add_debuglink = arg["--add-gnu-debuglink=".len..]; |
| 83 | } else if (mem.eql(u8, arg, "--add-gnu-debuglink")) { | 86 | } else if (mem.eql(u8, arg, "--add-gnu-debuglink")) { |
| ... | @@ -152,6 +155,7 @@ pub fn cmdObjCopy( | ... | @@ -152,6 +155,7 @@ pub fn cmdObjCopy( |
| 152 | .only_keep_debug = only_keep_debug, | 155 | .only_keep_debug = only_keep_debug, |
| 153 | .add_debuglink = opt_add_debuglink, | 156 | .add_debuglink = opt_add_debuglink, |
| 154 | .extract_to = opt_extract, | 157 | .extract_to = opt_extract, |
| | 158 | .compress_debug = compress_debug_sections, |
| 155 | }); | 159 | }); |
| 156 | return std.process.cleanExit(); | 160 | return std.process.cleanExit(); |
| 157 | }, | 161 | }, |
| ... | @@ -210,6 +214,7 @@ const usage = | ... | @@ -210,6 +214,7 @@ const usage = |
| 210 | \\ --only-keep-debug Strip a file, removing contents of any sections that would not be stripped by --strip-debug and leaving the debugging sections intact. | 214 | \\ --only-keep-debug Strip a file, removing contents of any sections that would not be stripped by --strip-debug and leaving the debugging sections intact. |
| 211 | \\ --add-gnu-debuglink=<file> Creates a .gnu_debuglink section which contains a reference to <file> and adds it to the output file. | 215 | \\ --add-gnu-debuglink=<file> Creates a .gnu_debuglink section which contains a reference to <file> and adds it to the output file. |
| 212 | \\ --extract-to <file> Extract the removed sections into <file>, and add a .gnu-debuglink section. | 216 | \\ --extract-to <file> Extract the removed sections into <file>, and add a .gnu-debuglink section. |
| | 217 | \\ --compress-debug-sections Compress DWARF debug sections with zlib |
| 213 | \\ | 218 | \\ |
| 214 | ; | 219 | ; |
| 215 | | 220 | |
| ... | @@ -660,6 +665,7 @@ const StripElfOptions = struct { | ... | @@ -660,6 +665,7 @@ const StripElfOptions = struct { |
| 660 | strip_all: bool = false, | 665 | strip_all: bool = false, |
| 661 | strip_debug: bool = false, | 666 | strip_debug: bool = false, |
| 662 | only_keep_debug: bool = false, | 667 | only_keep_debug: bool = false, |
| | 668 | compress_debug: bool = false, |
| 663 | }; | 669 | }; |
| 664 | | 670 | |
| 665 | fn stripElf( | 671 | fn stripElf( |
| ... | @@ -711,11 +717,11 @@ fn stripElf( | ... | @@ -711,11 +717,11 @@ fn stripElf( |
| 711 | }; | 717 | }; |
| 712 | defer dbg_file.close(); | 718 | defer dbg_file.close(); |
| 713 | | 719 | |
| 714 | try elf_file.emit(allocator, dbg_file, in_file, .{ .section_filter = flt }); | 720 | try elf_file.emit(allocator, dbg_file, in_file, .{ .section_filter = flt, .compress_debug = options.compress_debug }); |
| 715 | } | 721 | } |
| 716 | | 722 | |
| 717 | const debuglink: ?DebugLink = if (debuglink_path) |path| ElfFileHelper.createDebugLink(path) else null; | 723 | const debuglink: ?DebugLink = if (debuglink_path) |path| ElfFileHelper.createDebugLink(path) else null; |
| 718 | try elf_file.emit(allocator, out_file, in_file, .{ .section_filter = filter, .debuglink = debuglink }); | 724 | try elf_file.emit(allocator, out_file, in_file, .{ .section_filter = filter, .debuglink = debuglink, .compress_debug = options.compress_debug }); |
| 719 | }, | 725 | }, |
| 720 | } | 726 | } |
| 721 | } | 727 | } |
| ... | @@ -730,6 +736,7 @@ fn ElfFile(comptime is_64: bool) type { | ... | @@ -730,6 +736,7 @@ fn ElfFile(comptime is_64: bool) type { |
| 730 | const Elf_Ehdr = if (is_64) elf.Elf64_Ehdr else elf.Elf32_Ehdr; | 736 | const Elf_Ehdr = if (is_64) elf.Elf64_Ehdr else elf.Elf32_Ehdr; |
| 731 | const Elf_Phdr = if (is_64) elf.Elf64_Phdr else elf.Elf32_Phdr; | 737 | const Elf_Phdr = if (is_64) elf.Elf64_Phdr else elf.Elf32_Phdr; |
| 732 | const Elf_Shdr = if (is_64) elf.Elf64_Shdr else elf.Elf32_Shdr; | 738 | const Elf_Shdr = if (is_64) elf.Elf64_Shdr else elf.Elf32_Shdr; |
| | 739 | const Elf_Chdr = if (is_64) elf.Elf64_Chdr else elf.Elf32_Chdr; |
| 733 | const Elf_Sym = if (is_64) elf.Elf64_Sym else elf.Elf32_Sym; | 740 | const Elf_Sym = if (is_64) elf.Elf64_Sym else elf.Elf32_Sym; |
| 734 | const Elf_Verdef = if (is_64) elf.Elf64_Verdef else elf.Elf32_Verdef; | 741 | const Elf_Verdef = if (is_64) elf.Elf64_Verdef else elf.Elf32_Verdef; |
| 735 | const Elf_OffSize = if (is_64) elf.Elf64_Off else elf.Elf32_Off; | 742 | const Elf_OffSize = if (is_64) elf.Elf64_Off else elf.Elf32_Off; |
| ... | @@ -876,6 +883,7 @@ fn ElfFile(comptime is_64: bool) type { | ... | @@ -876,6 +883,7 @@ fn ElfFile(comptime is_64: bool) type { |
| 876 | const EmitElfOptions = struct { | 883 | const EmitElfOptions = struct { |
| 877 | section_filter: Filter = .all, | 884 | section_filter: Filter = .all, |
| 878 | debuglink: ?DebugLink = null, | 885 | debuglink: ?DebugLink = null, |
| | 886 | compress_debug: bool = false, |
| 879 | }; | 887 | }; |
| 880 | fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void { | 888 | fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void { |
| 881 | var arena = std.heap.ArenaAllocator.init(gpa); | 889 | var arena = std.heap.ArenaAllocator.init(gpa); |
| ... | @@ -939,6 +947,30 @@ fn ElfFile(comptime is_64: bool) type { | ... | @@ -939,6 +947,30 @@ fn ElfFile(comptime is_64: bool) type { |
| 939 | break :blk new_offset; | 947 | break :blk new_offset; |
| 940 | }; | 948 | }; |
| 941 | | 949 | |
| | 950 | // maybe compress .debug sections |
| | 951 | if (options.compress_debug) { |
| | 952 | for (self.sections[1..], sections_update[1..]) |section, *update| { |
| | 953 | if (update.action != .keep) continue; |
| | 954 | if (!std.mem.startsWith(u8, section.name, ".debug_")) continue; |
| | 955 | if ((section.section.sh_flags & elf.SHF_COMPRESSED) != 0) continue; // already compressed |
| | 956 | |
| | 957 | const chdr = Elf_Chdr{ |
| | 958 | .ch_type = elf.COMPRESS.ZLIB, |
| | 959 | .ch_size = section.section.sh_size, |
| | 960 | .ch_addralign = section.section.sh_addralign, |
| | 961 | }; |
| | 962 | |
| | 963 | const compressed_payload = try ElfFileHelper.tryCompressSection(allocator, in_file, section.section.sh_offset, section.section.sh_size, std.mem.asBytes(&chdr)); |
| | 964 | if (compressed_payload) |payload| { |
| | 965 | update.payload = payload; |
| | 966 | update.section = section.section; |
| | 967 | update.section.?.sh_addralign = @alignOf(Elf_Chdr); |
| | 968 | update.section.?.sh_size = @intCast(Elf_OffSize, payload.len); |
| | 969 | update.section.?.sh_flags |= elf.SHF_COMPRESSED; |
| | 970 | } |
| | 971 | } |
| | 972 | } |
| | 973 | |
| 942 | var cmdbuf = std.ArrayList(ElfFileHelper.WriteCmd).init(allocator); | 974 | var cmdbuf = std.ArrayList(ElfFileHelper.WriteCmd).init(allocator); |
| 943 | defer cmdbuf.deinit(); | 975 | defer cmdbuf.deinit(); |
| 944 | try cmdbuf.ensureUnusedCapacity(3 + new_shnum); | 976 | try cmdbuf.ensureUnusedCapacity(3 + new_shnum); |
| ... | @@ -1247,6 +1279,49 @@ const ElfFileHelper = struct { | ... | @@ -1247,6 +1279,49 @@ const ElfFileHelper = struct { |
| 1247 | } | 1279 | } |
| 1248 | } | 1280 | } |
| 1249 | | 1281 | |
| | 1282 | fn tryCompressSection(allocator: Allocator, in_file: File, offset: u64, size: u64, prefix: []const u8) !?[]align(8) const u8 { |
| | 1283 | if (size < prefix.len) return null; |
| | 1284 | |
| | 1285 | try in_file.seekTo(offset); |
| | 1286 | var section_reader = std.io.limitedReader(in_file.reader(), size); |
| | 1287 | |
| | 1288 | // allocate as large as decompressed data. if the compression doesn't fit, keep the data uncompressed. |
| | 1289 | const compressed_data = try allocator.alignedAlloc(u8, 8, @intCast(usize, size)); |
| | 1290 | var compressed_stream = std.io.fixedBufferStream(compressed_data); |
| | 1291 | |
| | 1292 | try compressed_stream.writer().writeAll(prefix); |
| | 1293 | |
| | 1294 | { |
| | 1295 | var compressor = try std.compress.zlib.compressStream(allocator, compressed_stream.writer(), .{}); |
| | 1296 | defer compressor.deinit(); |
| | 1297 | |
| | 1298 | var buf: [8000]u8 = undefined; |
| | 1299 | while (true) { |
| | 1300 | const bytes_read = try section_reader.read(&buf); |
| | 1301 | if (bytes_read == 0) break; |
| | 1302 | const bytes_written = compressor.write(buf[0..bytes_read]) catch |err| switch (err) { |
| | 1303 | error.NoSpaceLeft => { |
| | 1304 | allocator.free(compressed_data); |
| | 1305 | return null; |
| | 1306 | }, |
| | 1307 | else => return err, |
| | 1308 | }; |
| | 1309 | std.debug.assert(bytes_written == bytes_read); |
| | 1310 | } |
| | 1311 | compressor.finish() catch |err| switch (err) { |
| | 1312 | error.NoSpaceLeft => { |
| | 1313 | allocator.free(compressed_data); |
| | 1314 | return null; |
| | 1315 | }, |
| | 1316 | else => return err, |
| | 1317 | }; |
| | 1318 | } |
| | 1319 | |
| | 1320 | const compressed_len = @intCast(usize, compressed_stream.getPos() catch unreachable); |
| | 1321 | const data = allocator.realloc(compressed_data, compressed_len) catch compressed_data; |
| | 1322 | return data[0..compressed_len]; |
| | 1323 | } |
| | 1324 | |
| 1250 | fn createDebugLink(path: []const u8) DebugLink { | 1325 | fn createDebugLink(path: []const u8) DebugLink { |
| 1251 | const file = std.fs.cwd().openFile(path, .{}) catch |err| { | 1326 | const file = std.fs.cwd().openFile(path, .{}) catch |err| { |
| 1252 | fatal("zig objcopy: could not open `{s}`: {s}\n", .{ path, @errorName(err) }); | 1327 | fatal("zig objcopy: could not open `{s}`: {s}\n", .{ path, @errorName(err) }); |