| ... | @@ -21,10 +21,12 @@ pub fn cmdObjCopy( | ... | @@ -21,10 +21,12 @@ pub fn cmdObjCopy( |
| 21 | var opt_input: ?[]const u8 = null; | 21 | var opt_input: ?[]const u8 = null; |
| 22 | var opt_output: ?[]const u8 = null; | 22 | var opt_output: ?[]const u8 = null; |
| 23 | var opt_extract: ?[]const u8 = null; | 23 | var opt_extract: ?[]const u8 = null; |
| | 24 | var opt_add_debuglink: ?[]const u8 = null; |
| 24 | var only_section: ?[]const u8 = null; | 25 | var only_section: ?[]const u8 = null; |
| 25 | var pad_to: ?u64 = null; | 26 | var pad_to: ?u64 = null; |
| 26 | var strip_all: bool = false; | 27 | var strip_all: bool = false; |
| 27 | var strip_only_debug: bool = false; | 28 | var strip_debug: bool = false; |
| | 29 | var only_keep_debug: bool = false; |
| 28 | var listen = false; | 30 | var listen = false; |
| 29 | while (i < args.len) : (i += 1) { | 31 | while (i < args.len) : (i += 1) { |
| 30 | const arg = args[i]; | 32 | const arg = args[i]; |
| ... | @@ -71,10 +73,19 @@ pub fn cmdObjCopy( | ... | @@ -71,10 +73,19 @@ pub fn cmdObjCopy( |
| 71 | fatal("unable to parse: '{s}': {s}", .{ args[i], @errorName(err) }); | 73 | fatal("unable to parse: '{s}': {s}", .{ args[i], @errorName(err) }); |
| 72 | }; | 74 | }; |
| 73 | } else if (mem.eql(u8, arg, "-g") or mem.eql(u8, arg, "--strip-debug")) { | 75 | } else if (mem.eql(u8, arg, "-g") or mem.eql(u8, arg, "--strip-debug")) { |
| 74 | strip_only_debug = true; | 76 | strip_debug = true; |
| 75 | } else if (mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--strip-all")) { | 77 | } else if (mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--strip-all")) { |
| 76 | strip_only_debug = true; | | |
| 77 | strip_all = true; | 78 | strip_all = true; |
| | 79 | } else if (mem.eql(u8, arg, "--only-keep-debug")) { |
| | 80 | only_keep_debug = true; |
| | 81 | } else if (mem.startsWith(u8, arg, "--add-gnu-debuglink=")) { |
| | 82 | opt_add_debuglink = arg["--add-gnu-debuglink=".len..]; |
| | 83 | } else if (mem.eql(u8, arg, "--add-gnu-debuglink")) { |
| | 84 | i += 1; |
| | 85 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); |
| | 86 | opt_add_debuglink = args[i]; |
| | 87 | } else if (mem.startsWith(u8, arg, "--extract-to=")) { |
| | 88 | opt_extract = arg["--extract-to=".len..]; |
| 78 | } else if (mem.eql(u8, arg, "--extract-to")) { | 89 | } else if (mem.eql(u8, arg, "--extract-to")) { |
| 79 | i += 1; | 90 | i += 1; |
| 80 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); | 91 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); |
| ... | @@ -114,10 +125,14 @@ pub fn cmdObjCopy( | ... | @@ -114,10 +125,14 @@ pub fn cmdObjCopy( |
| 114 | | 125 | |
| 115 | switch (out_fmt) { | 126 | switch (out_fmt) { |
| 116 | .hex, .raw => { | 127 | .hex, .raw => { |
| 117 | if (strip_only_debug or strip_all) | 128 | if (strip_debug or strip_all or only_keep_debug) |
| 118 | fatal("zig objcopy: ELF to RAW or HEX copying does not support --strip", .{}); | 129 | fatal("zig objcopy: ELF to RAW or HEX copying does not support --strip", .{}); |
| 119 | if (opt_extract != null) | 130 | if (opt_extract != null) |
| 120 | fatal("zig objcopy: ELF to RAW or HEX copying does not support --extract-to", .{}); | 131 | fatal("zig objcopy: ELF to RAW or HEX copying does not support --extract-to", .{}); |
| | 132 | if (opt_extract != null) |
| | 133 | fatal("zig objcopy: ELF to RAW or HEX copying does not support --extract-to", .{}); |
| | 134 | if (opt_extract != null) |
| | 135 | fatal("zig objcopy: ELF to RAW or HEX copying does not support --extract-to", .{}); |
| 121 | | 136 | |
| 122 | try emitElf(arena, in_file, out_file, elf_hdr, .{ | 137 | try emitElf(arena, in_file, out_file, elf_hdr, .{ |
| 123 | .ofmt = out_fmt, | 138 | .ofmt = out_fmt, |
| ... | @@ -136,12 +151,12 @@ pub fn cmdObjCopy( | ... | @@ -136,12 +151,12 @@ pub fn cmdObjCopy( |
| 136 | fatal("zig objcopy: ELF to ELF copying does not support --only-section", .{}); | 151 | fatal("zig objcopy: ELF to ELF copying does not support --only-section", .{}); |
| 137 | if (pad_to) |_| | 152 | if (pad_to) |_| |
| 138 | fatal("zig objcopy: ELF to ELF copying does not support --pad-to", .{}); | 153 | fatal("zig objcopy: ELF to ELF copying does not support --pad-to", .{}); |
| 139 | if (!strip_only_debug and !strip_all) | | |
| 140 | fatal("zig objcopy: ELF to ELF copying only supports --strip", .{}); | | |
| 141 | | 154 | |
| 142 | try stripElf(arena, in_file, out_file, elf_hdr, .{ | 155 | try stripElf(arena, in_file, out_file, elf_hdr, .{ |
| 143 | .strip_only_debug = strip_only_debug, | 156 | .strip_debug = strip_debug, |
| 144 | .strip_all = strip_all, | 157 | .strip_all = strip_all, |
| | 158 | .only_keep_debug = only_keep_debug, |
| | 159 | .add_debuglink = opt_add_debuglink, |
| 145 | .extract_to = opt_extract, | 160 | .extract_to = opt_extract, |
| 146 | }); | 161 | }); |
| 147 | return std.process.cleanExit(); | 162 | return std.process.cleanExit(); |
| ... | @@ -190,15 +205,18 @@ const usage = | ... | @@ -190,15 +205,18 @@ const usage = |
| 190 | \\Usage: zig objcopy [options] input output | 205 | \\Usage: zig objcopy [options] input output |
| 191 | \\ | 206 | \\ |
| 192 | \\Options: | 207 | \\Options: |
| 193 | \\ -h, --help Print this help and exit | 208 | \\ -h, --help Print this help and exit |
| 194 | \\ --output-target=<value> Format of the output file | 209 | \\ --output-target=<value> Format of the output file |
| 195 | \\ -O <value> Alias for --output-target | 210 | \\ -O <value> Alias for --output-target |
| 196 | \\ --only-section=<section> Remove all but <section> | 211 | \\ --only-section=<section> Remove all but <section> |
| 197 | \\ -j <value> Alias for --only-section | 212 | \\ -j <value> Alias for --only-section |
| 198 | \\ --pad-to <addr> Pad the last section up to address <addr> | 213 | \\ --pad-to <addr> Pad the last section up to address <addr> |
| 199 | \\ --strip-debug, -g Remove all debug sections from the output.¶ | 214 | \\ --strip-debug, -g Remove all debug sections from the output. |
| 200 | \\ --strip-all, -S Remove all debug sections and symbol table from the output. | 215 | \\ --strip-all, -S Remove all debug sections and symbol table from the output. |
| 201 | \\ --extract-to <file> Extract the removed sections into <file>, and add a .gnu-debuglink section | 216 | \\ --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. |
| | 217 | \\ --add-gnu-debuglink=<file> Creates a .gnu_debuglink section which contains a reference to <file> and adds it to the output file. |
| | 218 | \\ --extract-to <file> Extract the removed sections into <file>, and add a .gnu-debuglink section. |
| | 219 | \\ |
| 202 | ; | 220 | ; |
| 203 | | 221 | |
| 204 | pub const EmitRawElfOptions = struct { | 222 | pub const EmitRawElfOptions = struct { |
| ... | @@ -644,8 +662,10 @@ test "containsValidAddressRange" { | ... | @@ -644,8 +662,10 @@ test "containsValidAddressRange" { |
| 644 | | 662 | |
| 645 | pub const StripElfOptions = struct { | 663 | pub const StripElfOptions = struct { |
| 646 | extract_to: ?[]const u8 = null, | 664 | extract_to: ?[]const u8 = null, |
| | 665 | add_debuglink: ?[]const u8 = null, |
| 647 | strip_all: bool = false, | 666 | strip_all: bool = false, |
| 648 | strip_only_debug: bool = false, | 667 | strip_debug: bool = false, |
| | 668 | only_keep_debug: bool = false, |
| 649 | }; | 669 | }; |
| 650 | | 670 | |
| 651 | fn stripElf( | 671 | fn stripElf( |
| ... | @@ -655,7 +675,12 @@ fn stripElf( | ... | @@ -655,7 +675,12 @@ fn stripElf( |
| 655 | elf_hdr: elf.Header, | 675 | elf_hdr: elf.Header, |
| 656 | options: StripElfOptions, | 676 | options: StripElfOptions, |
| 657 | ) !void { | 677 | ) !void { |
| 658 | std.debug.assert(options.strip_only_debug or options.strip_all); | 678 | const filter: ElfContents.Filter = filter: { |
| | 679 | if (options.only_keep_debug) break :filter .debug; |
| | 680 | if (options.strip_all) break :filter .program; |
| | 681 | if (options.strip_debug) break :filter .program_and_symbols; |
| | 682 | break :filter .all; |
| | 683 | }; |
| 659 | | 684 | |
| 660 | var elf_contents = try ElfContents.parse(allocator, in_file, elf_hdr); | 685 | var elf_contents = try ElfContents.parse(allocator, in_file, elf_hdr); |
| 661 | defer elf_contents.deinit(); | 686 | defer elf_contents.deinit(); |
| ... | @@ -665,23 +690,40 @@ fn stripElf( | ... | @@ -665,23 +690,40 @@ fn stripElf( |
| 665 | fatal("zig objcopy: unable to create '{s}': {s}", .{ filename, @errorName(err) }); | 690 | fatal("zig objcopy: unable to create '{s}': {s}", .{ filename, @errorName(err) }); |
| 666 | }; | 691 | }; |
| 667 | defer dbg_file.close(); | 692 | defer dbg_file.close(); |
| 668 | try elf_contents.emit(allocator, dbg_file, in_file, if (options.strip_only_debug) .debug else .debug_and_symbols, null); | 693 | |
| | 694 | const filter_complement: ElfContents.Filter = switch (filter) { |
| | 695 | .program => .debug_and_symbols, |
| | 696 | .debug => .program_and_symbols, |
| | 697 | .program_and_symbols => .debug, |
| | 698 | .debug_and_symbols => .program, |
| | 699 | .all => fatal("zig objcopy: nothing to extract", .{}), |
| | 700 | }; |
| | 701 | |
| | 702 | try elf_contents.emit(allocator, dbg_file, in_file, filter_complement, null); |
| 669 | } | 703 | } |
| 670 | | 704 | |
| 671 | const debuglink: ?ElfContents.DebugLink = blk: { | 705 | const debuglink: ?ElfContents.DebugLink = blk: { |
| 672 | if (options.extract_to) |filename| { | 706 | const debuglink_filename = name: { |
| | 707 | if (options.add_debuglink) |filename| break :name filename; |
| | 708 | if (options.extract_to) |filename| break :name filename; |
| | 709 | break :name null; |
| | 710 | }; |
| | 711 | if (debuglink_filename) |filename| { |
| 673 | const dbg_file = std.fs.cwd().openFile(filename, .{}) catch |err| { | 712 | const dbg_file = std.fs.cwd().openFile(filename, .{}) catch |err| { |
| 674 | fatal("zig objcopy: could not read `{s}`: {s}\n", .{ filename, @errorName(err) }); | 713 | fatal("zig objcopy: could not read `{s}`: {s}\n", .{ filename, @errorName(err) }); |
| 675 | }; | 714 | }; |
| 676 | defer dbg_file.close(); | 715 | defer dbg_file.close(); |
| 677 | | 716 | |
| 678 | break :blk .{ .name = std.fs.path.basename(filename), .crc32 = try computeFileCrc(dbg_file) }; | 717 | break :blk .{ |
| | 718 | .name = std.fs.path.basename(filename), |
| | 719 | .crc32 = try computeFileCrc(dbg_file), |
| | 720 | }; |
| 679 | } else { | 721 | } else { |
| 680 | break :blk null; | 722 | break :blk null; |
| 681 | } | 723 | } |
| 682 | }; | 724 | }; |
| 683 | | 725 | |
| 684 | try elf_contents.emit(allocator, out_file, in_file, if (options.strip_only_debug) .program_and_symbols else .program, debuglink); | 726 | try elf_contents.emit(allocator, out_file, in_file, filter, debuglink); |
| 685 | } | 727 | } |
| 686 | | 728 | |
| 687 | // note: this is "a minimal effort implementation" | 729 | // note: this is "a minimal effort implementation" |
| ... | @@ -872,7 +914,7 @@ const ElfContents = struct { | ... | @@ -872,7 +914,7 @@ const ElfContents = struct { |
| 872 | } | 914 | } |
| 873 | | 915 | |
| 874 | const DebugLink = struct { name: []const u8, crc32: u32 }; | 916 | const DebugLink = struct { name: []const u8, crc32: u32 }; |
| 875 | const Filter = enum { program, debug, program_and_symbols, debug_and_symbols }; | 917 | const Filter = enum { all, program, debug, program_and_symbols, debug_and_symbols }; |
| 876 | fn emit(self: *const Self, gpa: Allocator, output: File, source: File, filter: Filter, debuglink: ?DebugLink) !void { | 918 | fn emit(self: *const Self, gpa: Allocator, output: File, source: File, filter: Filter, debuglink: ?DebugLink) !void { |
| 877 | var arena = std.heap.ArenaAllocator.init(gpa); | 919 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 878 | defer arena.deinit(); | 920 | defer arena.deinit(); |
| ... | @@ -900,6 +942,10 @@ const ElfContents = struct { | ... | @@ -900,6 +942,10 @@ const ElfContents = struct { |
| 900 | update.action = action: { | 942 | update.action = action: { |
| 901 | if (section.usage == .none) break :action .strip; | 943 | if (section.usage == .none) break :action .strip; |
| 902 | break :action switch (filter) { | 944 | break :action switch (filter) { |
| | 945 | .all => switch (section.usage) { |
| | 946 | .none => .strip, |
| | 947 | else => .keep, |
| | 948 | }, |
| 903 | .program => switch (section.usage) { | 949 | .program => switch (section.usage) { |
| 904 | .common, .exe => .keep, | 950 | .common, .exe => .keep, |
| 905 | else => .strip, | 951 | else => .strip, |
| ... | @@ -910,10 +956,12 @@ const ElfContents = struct { | ... | @@ -910,10 +956,12 @@ const ElfContents = struct { |
| 910 | }, | 956 | }, |
| 911 | .debug => switch (section.usage) { | 957 | .debug => switch (section.usage) { |
| 912 | .exe, .symbols => .empty, | 958 | .exe, .symbols => .empty, |
| | 959 | .none => .strip, |
| 913 | else => .keep, | 960 | else => .keep, |
| 914 | }, | 961 | }, |
| 915 | .debug_and_symbols => switch (section.usage) { | 962 | .debug_and_symbols => switch (section.usage) { |
| 916 | .exe => .empty, | 963 | .exe => .empty, |
| | 964 | .none => .strip, |
| 917 | else => .keep, | 965 | else => .keep, |
| 918 | }, | 966 | }, |
| 919 | }; | 967 | }; |
| ... | @@ -1117,7 +1165,7 @@ const ElfContents = struct { | ... | @@ -1117,7 +1165,7 @@ const ElfContents = struct { |
| 1117 | } | 1165 | } |
| 1118 | | 1166 | |
| 1119 | // write the target files | 1167 | // write the target files |
| 1120 | // TODO: pack together contiguous copies (cmdbuf if ordered by construction) | 1168 | // TODO: pack together contiguous copies (cmdbuf is ordered, by construction) |
| 1121 | // TODO: fill the paddings with zero or copy from source file | 1169 | // TODO: fill the paddings with zero or copy from source file |
| 1122 | for (cmdbuf.items) |cmd| { | 1170 | for (cmdbuf.items) |cmd| { |
| 1123 | switch (cmd) { | 1171 | switch (cmd) { |