| ... | ... | @@ -21,10 +21,12 @@ pub fn cmdObjCopy( |
| 21 | 21 | var opt_input: ?[]const u8 = null; |
| 22 | 22 | var opt_output: ?[]const u8 = null; |
| 23 | 23 | var opt_extract: ?[]const u8 = null; |
| 24 | var opt_add_debuglink: ?[]const u8 = null; |
| 24 | 25 | var only_section: ?[]const u8 = null; |
| 25 | 26 | var pad_to: ?u64 = null; |
| 26 | 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 | 30 | var listen = false; |
| 29 | 31 | while (i < args.len) : (i += 1) { |
| 30 | 32 | const arg = args[i]; |
| ... | ... | @@ -71,10 +73,19 @@ pub fn cmdObjCopy( |
| 71 | 73 | fatal("unable to parse: '{s}': {s}", .{ args[i], @errorName(err) }); |
| 72 | 74 | }; |
| 73 | 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 | 77 | } else if (mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--strip-all")) { |
| 76 | | strip_only_debug = true; |
| 77 | 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 | 89 | } else if (mem.eql(u8, arg, "--extract-to")) { |
| 79 | 90 | i += 1; |
| 80 | 91 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); |
| ... | ... | @@ -114,10 +125,14 @@ pub fn cmdObjCopy( |
| 114 | 125 | |
| 115 | 126 | switch (out_fmt) { |
| 116 | 127 | .hex, .raw => { |
| 117 | | if (strip_only_debug or strip_all) |
| 128 | if (strip_debug or strip_all or only_keep_debug) |
| 118 | 129 | fatal("zig objcopy: ELF to RAW or HEX copying does not support --strip", .{}); |
| 119 | 130 | if (opt_extract != null) |
| 120 | 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 | 137 | try emitElf(arena, in_file, out_file, elf_hdr, .{ |
| 123 | 138 | .ofmt = out_fmt, |
| ... | ... | @@ -136,12 +151,12 @@ pub fn cmdObjCopy( |
| 136 | 151 | fatal("zig objcopy: ELF to ELF copying does not support --only-section", .{}); |
| 137 | 152 | if (pad_to) |_| |
| 138 | 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 | 155 | try stripElf(arena, in_file, out_file, elf_hdr, .{ |
| 143 | | .strip_only_debug = strip_only_debug, |
| 156 | .strip_debug = strip_debug, |
| 144 | 157 | .strip_all = strip_all, |
| 158 | .only_keep_debug = only_keep_debug, |
| 159 | .add_debuglink = opt_add_debuglink, |
| 145 | 160 | .extract_to = opt_extract, |
| 146 | 161 | }); |
| 147 | 162 | return std.process.cleanExit(); |
| ... | ... | @@ -190,15 +205,18 @@ const usage = |
| 190 | 205 | \\Usage: zig objcopy [options] input output |
| 191 | 206 | \\ |
| 192 | 207 | \\Options: |
| 193 | | \\ -h, --help Print this help and exit |
| 194 | | \\ --output-target=<value> Format of the output file |
| 195 | | \\ -O <value> Alias for --output-target |
| 196 | | \\ --only-section=<section> Remove all but <section> |
| 197 | | \\ -j <value> Alias for --only-section |
| 198 | | \\ --pad-to <addr> Pad the last section up to address <addr> |
| 199 | | \\ --strip-debug, -g Remove all debug sections from the output.¶ |
| 200 | | \\ --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 |
| 208 | \\ -h, --help Print this help and exit |
| 209 | \\ --output-target=<value> Format of the output file |
| 210 | \\ -O <value> Alias for --output-target |
| 211 | \\ --only-section=<section> Remove all but <section> |
| 212 | \\ -j <value> Alias for --only-section |
| 213 | \\ --pad-to <addr> Pad the last section up to address <addr> |
| 214 | \\ --strip-debug, -g Remove all debug sections from the output. |
| 215 | \\ --strip-all, -S Remove all debug sections and symbol table from the output. |
| 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 | 222 | pub const EmitRawElfOptions = struct { |
| ... | ... | @@ -644,8 +662,10 @@ test "containsValidAddressRange" { |
| 644 | 662 | |
| 645 | 663 | pub const StripElfOptions = struct { |
| 646 | 664 | extract_to: ?[]const u8 = null, |
| 665 | add_debuglink: ?[]const u8 = null, |
| 647 | 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 | 671 | fn stripElf( |
| ... | ... | @@ -655,7 +675,12 @@ fn stripElf( |
| 655 | 675 | elf_hdr: elf.Header, |
| 656 | 676 | options: StripElfOptions, |
| 657 | 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 | 685 | var elf_contents = try ElfContents.parse(allocator, in_file, elf_hdr); |
| 661 | 686 | defer elf_contents.deinit(); |
| ... | ... | @@ -665,23 +690,40 @@ fn stripElf( |
| 665 | 690 | fatal("zig objcopy: unable to create '{s}': {s}", .{ filename, @errorName(err) }); |
| 666 | 691 | }; |
| 667 | 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 | 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 | 712 | const dbg_file = std.fs.cwd().openFile(filename, .{}) catch |err| { |
| 674 | 713 | fatal("zig objcopy: could not read `{s}`: {s}\n", .{ filename, @errorName(err) }); |
| 675 | 714 | }; |
| 676 | 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 | 721 | } else { |
| 680 | 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 | 729 | // note: this is "a minimal effort implementation" |
| ... | ... | @@ -872,7 +914,7 @@ const ElfContents = struct { |
| 872 | 914 | } |
| 873 | 915 | |
| 874 | 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 | 918 | fn emit(self: *const Self, gpa: Allocator, output: File, source: File, filter: Filter, debuglink: ?DebugLink) !void { |
| 877 | 919 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 878 | 920 | defer arena.deinit(); |
| ... | ... | @@ -900,6 +942,10 @@ const ElfContents = struct { |
| 900 | 942 | update.action = action: { |
| 901 | 943 | if (section.usage == .none) break :action .strip; |
| 902 | 944 | break :action switch (filter) { |
| 945 | .all => switch (section.usage) { |
| 946 | .none => .strip, |
| 947 | else => .keep, |
| 948 | }, |
| 903 | 949 | .program => switch (section.usage) { |
| 904 | 950 | .common, .exe => .keep, |
| 905 | 951 | else => .strip, |
| ... | ... | @@ -910,10 +956,12 @@ const ElfContents = struct { |
| 910 | 956 | }, |
| 911 | 957 | .debug => switch (section.usage) { |
| 912 | 958 | .exe, .symbols => .empty, |
| 959 | .none => .strip, |
| 913 | 960 | else => .keep, |
| 914 | 961 | }, |
| 915 | 962 | .debug_and_symbols => switch (section.usage) { |
| 916 | 963 | .exe => .empty, |
| 964 | .none => .strip, |
| 917 | 965 | else => .keep, |
| 918 | 966 | }, |
| 919 | 967 | }; |
| ... | ... | @@ -1117,7 +1165,7 @@ const ElfContents = struct { |
| 1117 | 1165 | } |
| 1118 | 1166 | |
| 1119 | 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 | 1169 | // TODO: fill the paddings with zero or copy from source file |
| 1122 | 1170 | for (cmdbuf.items) |cmd| { |
| 1123 | 1171 | switch (cmd) { |